query("SELECT sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id FROM items ORDER BY id DESC");
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=stock_export_' . date('Ymd_His') . '.csv');
echo "";
$output = fopen('php://output', 'w');
fputcsv($output, ['SKU', 'Name', 'Price', 'Cost Price', 'Stock', 'VAT', 'Category ID', 'Supplier ID', 'Unit ID']);
foreach ($items as $row) { fputcsv($output, $row); }
fclose($output);
exit;
}
// Handle Import CSV
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'import_csv') {
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] === UPLOAD_ERR_OK) {
$pdo = db();
$file = fopen($_FILES['csv_file']['tmp_name'], 'r');
$bom = fread($file, 3);
if ($bom !== "") rewind($file);
$header = fgetcsv($file);
$imported = 0; $updated = 0;
$pdo->beginTransaction();
try {
$stmtInsert = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmtUpdate = $pdo->prepare("UPDATE items SET name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? WHERE sku=?");
$stmtCheck = $pdo->prepare("SELECT id FROM items WHERE sku=?");
while (($row = fgetcsv($file)) !== false) {
if (count($row) < 5) continue;
$sku = trim($row[0]); $name = trim($row[1]);
if ($sku === '' || $name === '') continue;
$price = (float)($row[2] ?? 0);
$cost_price = (float)($row[3] ?? 0);
$base_stock = (int)($row[4] ?? 0);
$vat = (float)($row[5] ?? 5);
$category_id = !empty($row[6]) ? (int)$row[6] : null;
$supplier_id = !empty($row[7]) ? (int)$row[7] : null;
$unit_id = !empty($row[8]) ? (int)$row[8] : null;
$stmtCheck->execute([$sku]);
if ($stmtCheck->fetchColumn()) {
$stmtUpdate->execute([$name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $sku]);
$updated++;
} else {
$stmtInsert->execute([$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id]);
$imported++;
}
}
$pdo->commit();
header('Location: stock.php?import_success=1&imported='.$imported.'&updated='.$updated);
exit;
} catch (Exception $e) {
$pdo->rollBack();
header('Location: stock.php?import_error='.urlencode($e->getMessage()));
exit;
}
}
header('Location: stock.php?import_error=No+file');
exit;
}
// Handle AJAX actions
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
header('Content-Type: application/json');
$pdo = db();
if ($_POST['action'] === 'save') {
try {
$sku = $_POST['sku'] ?? '';
$name = $_POST['name'] ?? '';
$price = (float)($_POST['price'] ?? 0);
$cost_price = (float)($_POST['cost_price'] ?? 0);
$base_stock = (int)($_POST['base_stock'] ?? 0);
$vat = (float)($_POST['vat'] ?? get_setting('vat_percentage', 5));
$category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null;
$supplier_id = !empty($_POST['supplier_id']) ? (int)$_POST['supplier_id'] : null;
$unit_id = !empty($_POST['unit_id']) ? (int)$_POST['unit_id'] : null;
if (!$sku || !$name) {
echo json_encode(['success' => false, 'error' => 'Missing SKU or Name']);
exit;
}
$image_url = $_POST['existing_image_url'] ?? null;
if (isset($_FILES['picture']) && $_FILES['picture']['error'] === UPLOAD_ERR_OK) {
$uploadDir = __DIR__ . '/assets/images/items/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0775, true);
}
$ext = pathinfo($_FILES['picture']['name'], PATHINFO_EXTENSION);
$filename = time() . '_' . rand(1000, 9999) . '.' . $ext;
if (move_uploaded_file($_FILES['picture']['tmp_name'], $uploadDir . $filename)) {
$image_url = 'assets/images/items/' . $filename;
}
}
$stmt = $pdo->prepare('SELECT id FROM items WHERE sku = ?');
$stmt->execute([$sku]);
$existing = $stmt->fetch();
if (isset($_POST['original_sku']) && $_POST['original_sku'] !== '') {
$orig_sku = $_POST['original_sku'];
if ($existing && $existing['id'] != ($pdo->query("SELECT id FROM items WHERE sku = " . $pdo->quote($orig_sku))->fetchColumn() ?: -1)) {
echo json_encode(['success' => false, 'error' => 'SKU already exists']);
exit;
}
$sql = "UPDATE items SET sku=?, name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? " . ($image_url ? ", image_url=?" : "") . " WHERE sku=?";
$params = [$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id];
if ($image_url) {
$params[] = $image_url;
}
$params[] = $orig_sku;
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
} else {
if ($existing) {
echo json_encode(['success' => false, 'error' => 'SKU already exists']);
exit;
}
$stmt = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $image_url]);
}
echo json_encode(['success' => true]);
exit;
} catch (Throwable $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
exit;
}
}
if ($_POST['action'] === 'delete') {
try {
$sku = $_POST['sku'] ?? '';
if (!$sku) {
echo json_encode(['success' => false, 'error' => 'Missing SKU']);
exit;
}
$stmt = $pdo->prepare('DELETE FROM items WHERE sku = ?');
$stmt->execute([$sku]);
echo json_encode(['success' => true]);
exit;
} catch (Throwable $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
exit;
}
}
}
$allStock = [];
try {
$allStock = stock_snapshot();
} catch (Throwable $e) {
$dbError = $e->getMessage();
}
$categories = [];
$suppliers = [];
try {
$pdo = db();
$categories = $pdo->query('SELECT id, name_ar, name_en FROM categories ORDER BY name_ar ASC')->fetchAll();
$suppliers = $pdo->query('SELECT id, name FROM suppliers ORDER BY name ASC')->fetchAll();
$units = $pdo->query('SELECT id, name_ar, name_en FROM units ORDER BY name_ar ASC')->fetchAll();
} catch (Throwable $e) {
// Ignore if not present
}
// Search and filter logic
$search = $_GET['q'] ?? '';
$catFilter = $_GET['category'] ?? '';
$supFilter = $_GET['supplier'] ?? '';
$filteredStock = [];
if (empty($dbError)) {
$lowerSearch = strtolower($search);
foreach ($allStock as $key => $row) {
$matchSearch = !$search || str_contains(strtolower((string)$row['sku']), $lowerSearch) || str_contains(strtolower((string)$row['name']), $lowerSearch);
$matchCat = !$catFilter || (isset($row['category_id']) && $row['category_id'] == $catFilter);
$matchSup = !$supFilter || (isset($row['supplier_id']) && $row['supplier_id'] == $supFilter);
if ($matchSearch && $matchCat && $matchSup) {
$filteredStock[$key] = $row;
}
}
}
// Pagination logic
$page = max(1, (int)($_GET['p'] ?? 1));
$limit = 10;
$total = count($filteredStock);
$totalPages = max(1, ceil($total / $limit));
$offset = ($page - 1) * $limit;
$stockRows = array_slice($filteredStock, $offset, $limit, true);
require __DIR__ . '/includes/header.php';
?>
= h(tr('إدارة الأصناف وجرد المخزون.', 'Manage items and inventory.')) ?>= h(tr('قائمة الأصناف والمخزون', 'Items & Stock List')) ?>