From d5e994ab87849f23f5d145bc5344b65b8f7e60f5 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 9 Apr 2026 04:46:33 +0000 Subject: [PATCH] update scale setting --- index.php | 192 ++++++++++++++++++++++++++++++++++++++++++++----- post_debug.log | 3 + 2 files changed, 176 insertions(+), 19 deletions(-) diff --git a/index.php b/index.php index 6a5e7fa..3aa6895 100644 --- a/index.php +++ b/index.php @@ -501,20 +501,49 @@ if (isset($_GET['action']) || isset($_POST['action'])) { if ($action === 'pos_get_item_by_sku') { header('Content-Type: application/json'); - $sku = $_GET['sku'] ?? ''; - if (!$sku) { echo json_encode(null); exit; } - + $sku = trim((string)($_GET['sku'] ?? '')); + if ($sku === '') { echo json_encode(null); exit; } + + $weightBarcode = parseWeightBarcode($sku); + $lookupSku = $weightBarcode['item_code'] ?? $sku; + $oid = current_outlet_id(); $stmt = db()->prepare("SELECT * FROM stock_items WHERE sku = ? AND outlet_id = ? LIMIT 1"); - $stmt->execute([$sku, $oid]); + $stmt->execute([$lookupSku, $oid]); $p = $stmt->fetch(PDO::FETCH_ASSOC); - + if ($p) { $p['original_price'] = (float)$p['sale_price']; $p['sale_price'] = getPromotionalPrice($p); + $p['price'] = (float)$p['sale_price']; $p['nameEn'] = $p['name_en']; $p['nameAr'] = $p['name_ar']; $p['vatRate'] = $p['vat_rate']; + + if ($weightBarcode) { + $qty = 0.0; + if ($weightBarcode['mode'] === 'price') { + if ((float)$p['sale_price'] <= 0) { + echo json_encode(['error' => 'This item cannot use price-based scale barcodes because its sale price is zero.']); + exit; + } + $qty = round(((float)$weightBarcode['value']) / (float)$p['sale_price'], 3); + } else { + $qty = round((float)$weightBarcode['value'], 3); + } + + if ($qty <= 0) { + echo json_encode(['error' => 'The weighing scale barcode value is invalid.']); + exit; + } + + $p['qty'] = $qty; + $p['is_scale_barcode'] = true; + $p['scale_barcode_mode'] = $weightBarcode['mode']; + $p['scale_barcode_value'] = (float)$weightBarcode['value']; + $p['scanned_barcode'] = $sku; + } + echo json_encode($p); } else { echo json_encode(null); @@ -826,6 +855,71 @@ if (!isset($_SESSION['user_id'])) { $message = $_SESSION['message'] ?? ''; unset($_SESSION['message']); +function getSettingValue(string $key, ?string $default = null): ?string { + static $cache = []; + if (array_key_exists($key, $cache)) return $cache[$key]; + try { + $stmt = db()->prepare("SELECT value FROM settings WHERE `key` = ? LIMIT 1"); + $stmt->execute([$key]); + $value = $stmt->fetchColumn(); + } catch (Throwable $e) { + $value = false; + } + if ($value === false || $value === null || $value === '') $value = $default; + $cache[$key] = $value; + return $value; +} + +function getWeightBarcodeConfig(): array { + $prefixStart = (int)(getSettingValue('weight_barcode_prefix_start', '20') ?? '20'); + $prefixEnd = (int)(getSettingValue('weight_barcode_prefix_end', '29') ?? '29'); + if ($prefixStart < 20 || $prefixStart > 29) $prefixStart = 20; + if ($prefixEnd < 20 || $prefixEnd > 29) $prefixEnd = 29; + if ($prefixStart > $prefixEnd) { + [$prefixStart, $prefixEnd] = [$prefixEnd, $prefixStart]; + } + $mode = strtolower((string)(getSettingValue('weight_barcode_mode', 'weight') ?? 'weight')); + if (!in_array($mode, ['weight', 'price'], true)) $mode = 'weight'; + return [ + 'prefix_start' => $prefixStart, + 'prefix_end' => $prefixEnd, + 'mode' => $mode, + 'value_divisor' => 1000, + ]; +} + +function isWeightBarcode(string $barcode): bool { + $barcode = trim($barcode); + if (!preg_match('/^\d{13}$/', $barcode)) return false; + $config = getWeightBarcodeConfig(); + $prefix = (int)substr($barcode, 0, 2); + return $prefix >= $config['prefix_start'] && $prefix <= $config['prefix_end']; +} + +function parseWeightBarcode(string $barcode): ?array { + $barcode = trim($barcode); + if (!isWeightBarcode($barcode)) return null; + $config = getWeightBarcodeConfig(); + $rawValue = (int)substr($barcode, 7, 5); + return [ + 'full_barcode' => $barcode, + 'prefix' => substr($barcode, 0, 2), + 'item_code' => substr($barcode, 2, 5), + 'raw_value' => $rawValue, + 'value' => $rawValue / (float)$config['value_divisor'], + 'mode' => $config['mode'], + 'check_digit' => substr($barcode, 12, 1), + ]; +} + +function validateItemSkuBarcode(string $sku): ?string { + $sku = trim($sku); + if ($sku === '') return null; + if (!isWeightBarcode($sku)) return null; + $config = getWeightBarcodeConfig(); + return "This barcode is reserved for weighing scale scans. 13-digit barcodes starting with {$config['prefix_start']}-{$config['prefix_end']} cannot be saved as item barcodes; please save the 5-digit scale item code instead."; +} + function redirectWithMessage($msg, $url = null) { if (!$url) { $url = $_SERVER['REQUEST_URI']; @@ -892,7 +986,10 @@ function getPromotionalPrice($item) { $category_id = (int)$_POST['category_id'] ?: null; $unit_id = (int)$_POST['unit_id'] ?: null; $supplier_id = (int)$_POST['supplier_id'] ?: null; - $sku = $_POST['sku'] ?? ''; + $sku = trim((string)($_POST['sku'] ?? '')); + if ($sku_error = validateItemSkuBarcode($sku)) { + redirectWithMessage($sku_error, 'index.php?page=items'); + } $sale_price = (float)($_POST['sale_price'] ?? 0); $purchase_price = (float)($_POST['purchase_price'] ?? 0); $stock_quantity = (float)($_POST['stock_quantity'] ?? 0); @@ -925,7 +1022,10 @@ function getPromotionalPrice($item) { $category_id = (int)$_POST['category_id'] ?: null; $unit_id = (int)$_POST['unit_id'] ?: null; $supplier_id = (int)$_POST['supplier_id'] ?: null; - $sku = $_POST['sku'] ?? ''; + $sku = trim((string)($_POST['sku'] ?? '')); + if ($sku_error = validateItemSkuBarcode($sku)) { + redirectWithMessage($sku_error, 'index.php?page=items'); + } $sale_price = (float)($_POST['sale_price'] ?? 0); $purchase_price = (float)($_POST['purchase_price'] ?? 0); $stock_quantity = (float)($_POST['stock_quantity'] ?? 0); @@ -1449,6 +1549,8 @@ function getPromotionalPrice($item) { # --- Unified Import Logic (Excel & CSV) --- if (isset($_POST['import_items'])) { error_log("Import items triggered."); + $count = 0; + $skipped = 0; if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) { $tmpPath = $_FILES['excel_file']['tmp_name']; $rows = []; @@ -1468,6 +1570,10 @@ function getPromotionalPrice($item) { foreach ($rows as $row) { if (empty($row[0])) continue; $sku = trim((string)$row[0]); + if ($sku_error = validateItemSkuBarcode($sku)) { + $skipped++; + continue; + } $name_en = trim((string)($row[1] ?? '')); $name_ar = trim((string)($row[2] ?? '')); $sale_price = (float)($row[3] ?? 0); @@ -1491,7 +1597,12 @@ function getPromotionalPrice($item) { } $count++; } - redirectWithMessage("Import items completed! $count processed.", "index.php?page=items"); + $weightConfig = getWeightBarcodeConfig(); + $summary = "Import items completed! $count processed."; + if ($skipped > 0) { + $summary .= " $skipped skipped because 13-digit barcodes starting with {$weightConfig['prefix_start']}-{$weightConfig['prefix_end']} are reserved for weighing scale barcodes."; + } + redirectWithMessage($summary, "index.php?page=items"); } } @@ -2437,7 +2548,19 @@ if (isset($_POST['add_hr_department'])) { if (can('settings_view')) { $db = db(); if (isset($_POST['settings']) && is_array($_POST['settings'])) { - foreach ($_POST['settings'] as $key => $value) { + $settings = $_POST['settings']; + $settings['weight_barcode_mode'] = in_array(($settings['weight_barcode_mode'] ?? 'weight'), ['weight', 'price'], true) ? $settings['weight_barcode_mode'] : 'weight'; + $prefixStart = (int)($settings['weight_barcode_prefix_start'] ?? 20); + $prefixEnd = (int)($settings['weight_barcode_prefix_end'] ?? 29); + if ($prefixStart < 20 || $prefixStart > 29) $prefixStart = 20; + if ($prefixEnd < 20 || $prefixEnd > 29) $prefixEnd = 29; + if ($prefixStart > $prefixEnd) { + [$prefixStart, $prefixEnd] = [$prefixEnd, $prefixStart]; + } + $settings['weight_barcode_prefix_start'] = (string)$prefixStart; + $settings['weight_barcode_prefix_end'] = (string)$prefixEnd; + + foreach ($settings as $key => $value) { $stmt = $db->prepare("INSERT INTO settings (`key`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = ?"); $stmt->execute([$key, $value, $value]); } @@ -5132,7 +5255,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
-
+
13-digit scale barcodes with the reserved prefixes are not allowed here. Save the 5-digit scale item code instead.
@@ -5609,22 +5732,26 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; if (!this.items) this.items = []; const allowZeroStock = (typeof companySettings !== 'undefined' && String(companySettings.allow_zero_stock_sell) === '1'); const currentStock = parseFloat(product.stock_quantity) || 0; - + const addQty = Math.max(parseFloat(product.qty) || 1, 0.001); + const unitPrice = (product.price !== undefined && product.price !== null) ? (parseFloat(product.price) || 0) : (parseFloat(product.sale_price) || 0); + const normalizedProduct = {...product, price: unitPrice}; + const existing = this.items.find(item => item.id === product.id); if (existing) { - if (!allowZeroStock && (existing.qty + 1) > currentStock) { + if (!allowZeroStock && (existing.qty + addQty) > currentStock) { Swal.fire('Error', 'Insufficient stock!', 'error'); return; } - existing.qty++; + existing.qty = Number((existing.qty + addQty).toFixed(3)); + existing.price = unitPrice; } else { - if (!allowZeroStock && currentStock <= 0) { + if (!allowZeroStock && currentStock < addQty) { Swal.fire('Error', 'Insufficient stock!', 'error'); return; } - this.items.push({...product, qty: 1}); + this.items.push({...normalizedProduct, qty: Number(addQty.toFixed(3))}); } - + this.render(); // Add visual feedback @@ -6375,6 +6502,15 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; fetch('index.php?action=pos_get_item_by_sku&sku=' + encodeURIComponent(barcode)) .then(response => response.json()) .then(product => { + if (product && product.error) { + Swal.fire({ + toast: true, position: 'top-end', icon: 'error', + title: product.error, showConfirmButton: false, timer: 1800 + }); + e.target.select(); + return; + } + if (product) { cart.add(product); e.target.value = ''; @@ -8785,6 +8921,24 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
+
+ + +
+
+ + +
+
+ + +
+
+
13-digit scale barcode format: 2-digit prefix + 5-digit item code + 5-digit value + 1 check digit. Full 13-digit scale barcodes are reserved and cannot be saved on items or imported.
+
@@ -9975,7 +10129,7 @@ function loadSessionReport(id) {
-
+
Reserved 13-digit scale barcodes cannot be saved here. Use the 5-digit scale item code for weighing products.
@@ -10010,8 +10164,8 @@ function loadSessionReport(id) {