From c4d7baf9d6a41a21eea3e3d715c590b5cfed9b43 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 19 Apr 2026 13:30:50 +0000 Subject: [PATCH] Autosave: 20260419-133057 --- print_labels.php | 123 ++++++++++++-- print_single_label.php | 374 +++++++++++++++++++++++++++++++++++++++++ stock.php | 158 +++++++++++++++-- 3 files changed, 620 insertions(+), 35 deletions(-) create mode 100644 print_single_label.php diff --git a/print_labels.php b/print_labels.php index 667f162..10e56c0 100644 --- a/print_labels.php +++ b/print_labels.php @@ -8,6 +8,7 @@ $items = []; $skus = []; if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['skus'])) { $skus = is_array($_POST['skus']) ? $_POST['skus'] : explode(',', $_POST['skus']); + $skus = array_values(array_filter(array_map('trim', $skus))); } elseif (!empty($_GET['sku'])) { $skus = [$_GET['sku']]; } @@ -23,8 +24,63 @@ if (!empty($skus)) { } } +$customWidth = $_REQUEST['custom_width'] ?? '40'; +$customHeight = $_REQUEST['custom_height'] ?? '25'; + // Templates configuration $templates = [ + 'custom_roll' => [ + 'name' => 'Custom Size / مقاس مخصص (أدخل المقاس أدناه)', + 'cols' => 1, + 'rows' => 1, + 'width' => $customWidth . 'mm', + 'height' => $customHeight . 'mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => $customWidth . 'mm', + 'page_height' => $customHeight . 'mm', + ], + 'avery_65' => [ + 'name' => 'Roll / Zebra (40 x 25 mm)', + 'cols' => 1, + 'rows' => 1, + 'width' => '40mm', + 'height' => '25mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => '40mm', + 'page_height' => '25mm', + ], + 'roll_25x40' => [ + 'name' => 'Roll / Zebra (25 x 40 mm)', + 'cols' => 1, + 'rows' => 1, + 'width' => '25mm', + 'height' => '40mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => '25mm', + 'page_height' => '40mm', + ], + 'roll_38x25' => [ + 'name' => 'Roll / Zebra (38 x 25 mm)', + 'cols' => 1, + 'rows' => 1, + 'width' => '38mm', + 'height' => '25mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => '38mm', + 'page_height' => '25mm', + ], 'avery_65' => [ 'name' => 'Avery L7651 - 65 Labels (38.1 x 21.2 mm)', 'cols' => 5, @@ -35,6 +91,8 @@ $templates = [ 'margin_left' => '4.7mm', 'gap_x' => '2.5mm', 'gap_y' => '0mm', + 'page_width' => '210mm', + 'page_height' => '297mm', ], 'avery_54' => [ 'name' => 'Avery 54 Labels (32 x 25.4 mm)', @@ -46,6 +104,8 @@ $templates = [ 'margin_left' => '5mm', 'gap_x' => '2mm', 'gap_y' => '0mm', + 'page_width' => '210mm', + 'page_height' => '297mm', ], 'avery_45' => [ 'name' => 'Avery 45 Labels (38.1 x 29.6 mm)', @@ -57,12 +117,23 @@ $templates = [ 'margin_left' => '5mm', 'gap_x' => '2mm', 'gap_y' => '0mm', + 'page_width' => '210mm', + 'page_height' => '297mm', ] ]; $templateId = $_REQUEST['template'] ?? 'avery_65'; $tpl = $templates[$templateId] ?? $templates['avery_65']; +$pageWidth = $tpl['page_width'] ?? '210mm'; +$pageHeight = $tpl['page_height'] ?? '297mm'; +$pageCssSize = (isset($tpl['page_width']) && $tpl['page_width'] !== '210mm') ? "{$tpl['page_width']} {$tpl['page_height']}" : "A4 portrait"; + +if ($templateId !== 'custom_roll') { + $customWidth = floatval(str_replace('mm', '', $tpl['width'])); + $customHeight = floatval(str_replace('mm', '', $tpl['height'])); +} + // Prepare labels to print $labelsToPrint = []; if (!empty($items)) { @@ -85,6 +156,8 @@ if (!empty($items)) { } } +$isSinglePrint = isset($_GET['sku']) && count($skus) === 1; + ?> @@ -99,8 +172,8 @@ if (!empty($items)) { .print-only { display: none; } .page { - width: 210mm; - min-height: 297mm; + width: ; + height: ; padding-top: ; padding-left: ; padding-right: ; @@ -116,6 +189,7 @@ if (!empty($items)) { row-gap: ; box-sizing: border-box; page-break-after: always; + overflow: hidden; } .label-item { @@ -146,16 +220,21 @@ if (!empty($items)) { margin: 0; border: initial; border-radius: initial; - width: 210mm; - min-height: 297mm; + width: ; + height: ; box-shadow: initial; background: initial; page-break-after: always; + overflow: hidden; + } + .page:last-of-type { + page-break-after: auto; } .label-item { border: none; } - @page { size: A4 portrait; margin: 0; } + @page { size: ; margin: 0; } } + @@ -171,13 +250,15 @@ if (!empty($items)) {
-
-
@@ -195,8 +276,8 @@ if (!empty($items)) { - $item): ?> - + $item): + ?> @@ -213,8 +294,8 @@ if (!empty($items)) { - - + @@ -224,8 +305,8 @@ if (!empty($items)) { - -
+
@@ -245,8 +326,8 @@ if (!empty($items)) { foreach ($pages as $pageIdx => $pageLabels): ?>
- -
+
+ +
+ +
- + \ No newline at end of file diff --git a/print_single_label.php b/print_single_label.php new file mode 100644 index 0000000..26d1457 --- /dev/null +++ b/print_single_label.php @@ -0,0 +1,374 @@ +prepare("SELECT * FROM items WHERE sku IN ($placeholders)"); + $stmt->execute($skus); + $results = $stmt->fetchAll(); + // Index by SKU + foreach ($results as $row) { + $items[$row['sku']] = $row; + } +} + +$customWidth = $_REQUEST['custom_width'] ?? '40'; +$customHeight = $_REQUEST['custom_height'] ?? '25'; + +// Templates configuration +$templates = [ + 'custom_roll' => [ + 'name' => 'Custom Size / مقاس مخصص (أدخل المقاس أدناه)', + 'cols' => 1, + 'rows' => 1, + 'width' => $customWidth . 'mm', + 'height' => $customHeight . 'mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => $customWidth . 'mm', + 'page_height' => $customHeight . 'mm', + ], + 'roll_40x25' => [ + 'name' => 'Roll / Zebra (40 x 25 mm)', + 'cols' => 1, + 'rows' => 1, + 'width' => '40mm', + 'height' => '25mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => '40mm', + 'page_height' => '25mm', + ], + 'roll_25x40' => [ + 'name' => 'Roll / Zebra (25 x 40 mm)', + 'cols' => 1, + 'rows' => 1, + 'width' => '25mm', + 'height' => '40mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => '25mm', + 'page_height' => '40mm', + ], + 'roll_38x25' => [ + 'name' => 'Roll / Zebra (38 x 25 mm)', + 'cols' => 1, + 'rows' => 1, + 'width' => '38mm', + 'height' => '25mm', + 'margin_top' => '0mm', + 'margin_left' => '0mm', + 'gap_x' => '0mm', + 'gap_y' => '0mm', + 'page_width' => '38mm', + 'page_height' => '25mm', + ], + 'avery_65' => [ + 'name' => 'Avery L7651 - 65 Labels (38.1 x 21.2 mm)', + 'cols' => 5, + 'rows' => 13, + 'width' => '38.1mm', + 'height' => '21.2mm', + 'margin_top' => '10.5mm', + 'margin_left' => '4.7mm', + 'gap_x' => '2.5mm', + 'gap_y' => '0mm', + 'page_width' => '210mm', + 'page_height' => '297mm', + ], + 'avery_54' => [ + 'name' => 'Avery 54 Labels (32 x 25.4 mm)', + 'cols' => 6, + 'rows' => 9, + 'width' => '32mm', + 'height' => '25.4mm', + 'margin_top' => '10mm', + 'margin_left' => '5mm', + 'gap_x' => '2mm', + 'gap_y' => '0mm', + 'page_width' => '210mm', + 'page_height' => '297mm', + ], + 'avery_45' => [ + 'name' => 'Avery 45 Labels (38.1 x 29.6 mm)', + 'cols' => 5, + 'rows' => 9, + 'width' => '38.1mm', + 'height' => '29.6mm', + 'margin_top' => '15mm', + 'margin_left' => '5mm', + 'gap_x' => '2mm', + 'gap_y' => '0mm', + 'page_width' => '210mm', + 'page_height' => '297mm', + ] +]; + +$templateId = $_REQUEST['template'] ?? 'custom_roll'; +$tpl = $templates[$templateId] ?? $templates['custom_roll']; + +$pageWidth = $tpl['page_width'] ?? '210mm'; +$pageHeight = $tpl['page_height'] ?? '297mm'; +$pageCssSize = (isset($tpl['page_width']) && $tpl['page_width'] !== '210mm') ? "{$tpl['page_width']} {$tpl['page_height']}" : "A4 portrait"; + +if ($templateId !== 'custom_roll') { + $customWidth = floatval(str_replace('mm', '', $tpl['width'])); + $customHeight = floatval(str_replace('mm', '', $tpl['height'])); +} + +// Prepare labels to print +$labelsToPrint = []; +if (!empty($items)) { + // Check if quantities are posted + if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['qty'])) { + foreach ($_POST['qty'] as $sku => $qty) { + if (isset($items[$sku])) { + for ($i = 0; $i < (int)$qty; $i++) { + $labelsToPrint[] = $items[$sku]; + } + } + } + } else { + // Default 1 per item + foreach ($skus as $sku) { + if (isset($items[$sku])) { + $labelsToPrint[] = $items[$sku]; + } + } + } +} + +$isSinglePrint = isset($_GET['sku']) && count($skus) === 1; + +?> + + + + + <?= h(tr('طباعة ملصقات', 'Print Labels')) ?> + + + + + + + +
+
+
+
+ +
+
+
+
+ +
+ + +
+
+ + +
+ +
+ +
+
+ +
+ + + + + + + + + + + + $item): + ?> + + + + + + + + + + + + +
SKU
+ + + + + + +
+ + +
+
+
+ +
+ +
+
+
+
+ + + + +
+ +
+ + + + + \ No newline at end of file diff --git a/stock.php b/stock.php index 781e342..68bb7d0 100644 --- a/stock.php +++ b/stock.php @@ -5,6 +5,68 @@ $pageTitle = tr('المخزون', 'Stock'); $activeNav = 'stock'; $dbError = null; +// Handle Export CSV +if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'export_csv') { + $pdo = db(); + $stmt = $pdo->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'); @@ -144,20 +206,37 @@ require __DIR__ . '/includes/header.php';
-
+

-
- - +
+
+ " . $import_success_message . "
"; + endif; ?> + " . $import_error_message . "
"; + endif; ?> +
+
@@ -252,9 +331,9 @@ require __DIR__ . '/includes/header.php'; - + @@ -279,6 +358,38 @@ require __DIR__ . '/includes/header.php'; + + +