From 017bae675e3a636fe30fa4e8b14dff38fb898b94 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 20 Apr 2026 11:31:42 +0000 Subject: [PATCH] Autosave: 20260420-113152 --- debts.php | 156 +++++++++++++++++++++++++++++++++++++++++ edit_sale.php | 11 ++- includes/app.php | 16 ++++- includes/header.php | 7 +- includes/sale_form.php | 17 +++-- patch.php | 76 ++++++++++++++++++++ pos.php | 15 +++- print_receipt.php | 4 +- sale.php | 2 +- sales.php | 9 ++- 10 files changed, 296 insertions(+), 17 deletions(-) create mode 100644 debts.php create mode 100644 patch.php diff --git a/debts.php b/debts.php new file mode 100644 index 0000000..10e492b --- /dev/null +++ b/debts.php @@ -0,0 +1,156 @@ +prepare("UPDATE sales_orders SET payment_status = 'paid', status = 'completed' WHERE id = ?")->execute([$id]); + set_flash('success', tr('تم استلام المبلغ بنجاح.', 'Payment received successfully.')); + } catch (Throwable $e) { + set_flash('danger', tr('خطأ أثناء التحديث.', 'Error updating.')); + } + redirect_to('debts.php'); +} + +// Fetch all unpaid sales +$sqlUnpaid = "SELECT s.*, c.name as c_name, c.phone as c_phone + FROM sales_orders s + LEFT JOIN customers c ON s.customer_id = c.id + WHERE s.payment_status = 'unpaid' + ORDER BY s.sale_date DESC"; +$stmtUnpaid = $pdo->query($sqlUnpaid); +$unpaidSales = $stmtUnpaid->fetchAll(PDO::FETCH_ASSOC); + +// Aggregate by customer +$debtsByCustomer = []; +foreach ($unpaidSales as $sale) { + $cId = $sale['customer_id'] ?? 'unknown'; + if (!isset($debtsByCustomer[$cId])) { + $debtsByCustomer[$cId] = [ + 'name' => $sale['c_name'] ?: $sale['customer_name'] ?: tr('عميل غير معروف', 'Unknown Customer'), + 'phone' => $sale['c_phone'] ?: '', + 'total' => 0.0, + 'count' => 0 + ]; + } + $debtsByCustomer[$cId]['total'] += (float)$sale['total_amount']; + $debtsByCustomer[$cId]['count'] += 1; +} + +// Sort by highest debt +uasort($debtsByCustomer, fn($a, $b) => $b['total'] <=> $a['total']); +?> + +
+

+
+ +
+ +
+
+
+
+
+
+ +
+ +
    + +
  • +
    + + +
    + +
    +
    + +
  • + +
+ +
+
+
+ + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+
+
+
+
+ + + + diff --git a/edit_sale.php b/edit_sale.php index 31f34d6..405239a 100644 --- a/edit_sale.php +++ b/edit_sale.php @@ -30,8 +30,10 @@ try { if ($_SERVER['REQUEST_METHOD'] === 'POST') { $branchCode = trim((string) ($_POST['branch_code'] ?? '')); + $customerId = isset($_POST['customer_id']) && $_POST['customer_id'] !== '' ? (int)$_POST['customer_id'] : null; $customerName = trim((string) ($_POST['customer_name'] ?? '')); $paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash')); + $paymentStatus = ($paymentMethod === 'pay_later') ? 'unpaid' : 'paid'; $saleStatus = trim((string) ($_POST['sale_status'] ?? 'completed')); $notes = trim((string) ($_POST['notes'] ?? '')); $cartJson = (string) ($_POST['cart_json'] ?? '[]'); @@ -39,7 +41,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!in_array($branchCode, $allowedBranches, true)) { $error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.'); - } elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer'], true)) { + } elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer', 'pay_later'], true)) { $error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.'); } elseif (!is_array($items) || $items === []) { $error = tr('أضف صنفاً واحداً على الأقل إلى الفاتورة.', 'Add at least one item to the invoice.'); @@ -82,8 +84,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $cashierName = current_lang() === 'ar' ? $user['name_ar'] : $user['name_en']; $stmt = db()->prepare('UPDATE sales_orders SET branch_code = :branch_code, + customer_id = :customer_id, customer_name = :customer_name, payment_method = :payment_method, + payment_status = :payment_status, items_json = :items_json, item_count = :item_count, subtotal = :subtotal, @@ -94,8 +98,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { WHERE id = :id'); $stmt->execute([ ':branch_code' => $branchCode, + ':customer_id' => $customerId, ':customer_name' => $customerName !== '' ? $customerName : null, ':payment_method' => $paymentMethod, + ':payment_status' => $paymentStatus, ':items_json' => json_encode($normalized, JSON_UNESCAPED_UNICODE), ':item_count' => $itemCount, ':subtotal' => $subtotal, @@ -355,6 +361,7 @@ require __DIR__ . '/includes/header.php';
+
@@ -530,6 +538,7 @@ async function saveNewCustomer() { if (data.success) { customersData.push(data.customer); custInput.value = data.customer.name + (data.customer.phone ? ' - ' + data.customer.phone : ''); + document.getElementById('formCustomerId').value = data.customer.id; newCustomerModalObj.hide(); const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 }); Toast.fire({ icon: 'success', title: '' }); diff --git a/includes/app.php b/includes/app.php index 0cbdd7b..9c5c7bc 100644 --- a/includes/app.php +++ b/includes/app.php @@ -20,6 +20,14 @@ try { if ($stmt2->rowCount() === 0) { $pdo->exec("ALTER TABLE branches ADD COLUMN avatar varchar(255) DEFAULT NULL"); } + $stmt3 = $pdo->query("SHOW COLUMNS FROM sales_orders LIKE 'customer_id'"); + if ($stmt3->rowCount() === 0) { + $pdo->exec("ALTER TABLE sales_orders ADD COLUMN customer_id int(10) unsigned DEFAULT NULL"); + } + $stmt4 = $pdo->query("SHOW COLUMNS FROM sales_orders LIKE 'payment_status'"); + if ($stmt4->rowCount() === 0) { + $pdo->exec("ALTER TABLE sales_orders ADD COLUMN payment_status varchar(20) NOT NULL DEFAULT 'paid'"); + } @file_put_contents($flagFile, '1'); } } catch (\Throwable $e) {} @@ -323,8 +331,10 @@ function ensure_sales_table(): void cashier_username VARCHAR(60) NOT NULL, cashier_name VARCHAR(120) NOT NULL, role_name VARCHAR(40) NOT NULL, + customer_id INT(10) UNSIGNED DEFAULT NULL, customer_name VARCHAR(120) DEFAULT NULL, payment_method VARCHAR(30) NOT NULL, + payment_status VARCHAR(20) NOT NULL DEFAULT 'paid', items_json LONGTEXT NOT NULL, item_count INT UNSIGNED NOT NULL DEFAULT 0, subtotal DECIMAL(10,2) NOT NULL DEFAULT 0, @@ -346,9 +356,9 @@ function create_sale(array $data): int ensure_sales_table(); $stmt = db()->prepare('INSERT INTO sales_orders - (receipt_no, sale_mode, branch_code, cashier_username, cashier_name, role_name, customer_name, payment_method, items_json, item_count, subtotal, vat_amount, total_amount, status, notes, sale_date) + (receipt_no, sale_mode, branch_code, cashier_username, cashier_name, role_name, customer_id, customer_name, payment_method, payment_status, items_json, item_count, subtotal, vat_amount, total_amount, status, notes, sale_date) VALUES - (:receipt_no, :sale_mode, :branch_code, :cashier_username, :cashier_name, :role_name, :customer_name, :payment_method, :items_json, :item_count, :subtotal, :vat_amount, :total_amount, :status, :notes, NOW())'); + (:receipt_no, :sale_mode, :branch_code, :cashier_username, :cashier_name, :role_name, :customer_id, :customer_name, :payment_method, :payment_status, :items_json, :item_count, :subtotal, :vat_amount, :total_amount, :status, :notes, NOW())'); $stmt->bindValue(':receipt_no', $data['receipt_no']); $stmt->bindValue(':sale_mode', $data['sale_mode']); @@ -356,8 +366,10 @@ function create_sale(array $data): int $stmt->bindValue(':cashier_username', $data['cashier_username']); $stmt->bindValue(':cashier_name', $data['cashier_name']); $stmt->bindValue(':role_name', $data['role_name']); + $stmt->bindValue(':customer_id', $data['customer_id'] ?? null, PDO::PARAM_INT); $stmt->bindValue(':customer_name', $data['customer_name']); $stmt->bindValue(':payment_method', $data['payment_method']); + $stmt->bindValue(':payment_status', $data['payment_status'] ?? 'paid'); $stmt->bindValue(':items_json', json_encode($data['items'], JSON_UNESCAPED_UNICODE)); $stmt->bindValue(':item_count', $data['item_count'], PDO::PARAM_INT); $stmt->bindValue(':subtotal', $data['subtotal']); diff --git a/includes/header.php b/includes/header.php index 902a72b..d8d72bb 100644 --- a/includes/header.php +++ b/includes/header.php @@ -96,13 +96,13 @@ $isPublic = !empty($forcePublic) || !isset($user) || !$user; - +
-
+
@@ -168,6 +168,9 @@ $isPublic = !empty($forcePublic) || !isset($user) || !$user; + + + diff --git a/includes/sale_form.php b/includes/sale_form.php index a06394e..20b66b0 100644 --- a/includes/sale_form.php +++ b/includes/sale_form.php @@ -15,8 +15,10 @@ try { if ($_SERVER['REQUEST_METHOD'] === 'POST') { $branchCode = trim((string) ($_POST['branch_code'] ?? '')); + $customerId = isset($_POST['customer_id']) && $_POST['customer_id'] !== '' ? (int)$_POST['customer_id'] : null; $customerName = trim((string) ($_POST['customer_name'] ?? '')); $paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash')); + $paymentStatus = ($paymentMethod === 'pay_later') ? 'unpaid' : 'paid'; $saleStatus = trim((string) ($_POST['sale_status'] ?? 'completed')); $notes = trim((string) ($_POST['notes'] ?? '')); $cartJson = (string) ($_POST['cart_json'] ?? '[]'); @@ -24,7 +26,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!in_array($branchCode, $allowedBranches, true)) { $error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.'); - } elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer'], true)) { + } elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer', 'pay_later'], true)) { $error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.'); } elseif (!is_array($items) || $items === []) { $error = tr('أضف صنفاً واحداً على الأقل إلى الفاتورة.', 'Add at least one item to the invoice.'); @@ -73,8 +75,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'cashier_username' => $user['username'], 'cashier_name' => $cashierName, 'role_name' => $user['role'], + 'customer_id' => $customerId, 'customer_name' => $customerName !== '' ? $customerName : null, 'payment_method' => $paymentMethod, + 'payment_status' => $paymentStatus, 'items' => $normalized, 'item_count' => $itemCount, 'subtotal' => $subtotal, @@ -336,6 +340,7 @@ require __DIR__ . '/header.php';
+
@@ -436,7 +442,7 @@ custInput.addEventListener('input', function() { const q = this.value.toLowerCase().trim(); custDropdown.innerHTML = ''; if (q.length < 2) { - custDropdown.classList.remove('show'); + document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show'); return; } @@ -452,19 +458,19 @@ custInput.addEventListener('input', function() { div.innerHTML = `${c.name} ${c.phone ? ''+c.phone+'' : ''}`; div.onclick = function() { custInput.value = c.name + (c.phone ? ' - ' + c.phone : ''); - custDropdown.classList.remove('show'); + document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show'); }; custDropdown.appendChild(div); }); custDropdown.classList.add('show'); } else { - custDropdown.classList.remove('show'); + document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show'); } }); document.addEventListener('click', function(e) { if (!custInput.contains(e.target) && !custDropdown.contains(e.target)) { - custDropdown.classList.remove('show'); + document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show'); } }); @@ -499,6 +505,7 @@ async function saveNewCustomer() { if (data.success) { customersData.push(data.customer); custInput.value = data.customer.name + (data.customer.phone ? ' - ' + data.customer.phone : ''); + document.getElementById('formCustomerId').value = data.customer.id; newCustomerModalObj.hide(); const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 }); Toast.fire({ icon: 'success', title: '' }); diff --git a/patch.php b/patch.php new file mode 100644 index 0000000..55dd624 --- /dev/null +++ b/patch.php @@ -0,0 +1,76 @@ +query("SHOW COLUMNS FROM branches LIKE 'avatar'"); + if (\$stmt2->rowCount() === 0) { + \$pdo->exec("ALTER TABLE branches ADD COLUMN avatar varchar(255) DEFAULT NULL"); + } + @file_put_contents(\$flagFile, '1'); +S1; + +$r1 = <<query("SHOW COLUMNS FROM branches LIKE 'avatar'"); + if (\$stmt2->rowCount() === 0) { + \$pdo->exec("ALTER TABLE branches ADD COLUMN avatar varchar(255) DEFAULT NULL"); + } + \$stmt3 = \pdo->query("SHOW COLUMNS FROM sales_orders LIKE 'customer_id'"); + if (\$stmt3->rowCount() === 0) { + \$pdo->exec("ALTER TABLE sales_orders ADD COLUMN customer_id int(10) unsigned DEFAULT NULL"); + } + \$stmt4 = \pdo->query("SHOW COLUMNS FROM sales_orders LIKE 'payment_status'"); + if (\$stmt4->rowCount() === 0) { + \$pdo->exec("ALTER TABLE sales_orders ADD COLUMN payment_status varchar(20) NOT NULL DEFAULT 'paid'"); + } + @file_put_contents(\$flagFile, '1'); +R1; + +$c = str_replace($s1, $r1, $c); + +// add to ensure_sales_table() +$s2 = <<bindValue(':customer_name', $data['customer_name']); + $stmt->bindValue(':payment_method', $data['payment_method']); +S4; + +$r4 = <<bindValue(':customer_id', $data['customer_id'] ?? null, PDO::PARAM_INT); + $stmt->bindValue(':customer_name', $data['customer_name']); + $stmt->bindValue(':payment_method', $data['payment_method']); + $stmt->bindValue(':payment_status', $data['payment_status'] ?? 'paid'); +R4; +$c = str_replace($s4, $r4, $c); + +file_put_contents('includes/app.php', $c); +echo "Patched includes/app.php\n"; diff --git a/pos.php b/pos.php index f946e77..ad8ecf7 100644 --- a/pos.php +++ b/pos.php @@ -19,15 +19,17 @@ try { if ($_SERVER['REQUEST_METHOD'] === 'POST') { $branchCode = trim((string) ($_POST['branch_code'] ?? '')); + $customerId = isset($_POST['customer_id']) && $_POST['customer_id'] !== '' ? (int)$_POST['customer_id'] : null; $customerName = trim((string) ($_POST['customer_name'] ?? '')); $paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash')); + $paymentStatus = ($paymentMethod === 'pay_later') ? 'unpaid' : 'paid'; $notes = trim((string) ($_POST['notes'] ?? '')); $cartJson = (string) ($_POST['cart_json'] ?? '[]'); $items = json_decode($cartJson, true); if (!in_array($branchCode, $allowedBranches, true)) { $error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.'); - } elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer'], true)) { + } elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer', 'pay_later'], true)) { $error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.'); } elseif (!is_array($items) || $items === []) { $error = tr('أضف صنفاً واحداً على الأقل إلى السلة.', 'Add at least one item to the cart.'); @@ -71,8 +73,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'cashier_username' => $user['username'], 'cashier_name' => $cashierName, 'role_name' => $user['role'], + 'customer_id' => $customerId, 'customer_name' => $customerName !== '' ? $customerName : null, 'payment_method' => $paymentMethod, + 'payment_status' => $paymentStatus, 'items' => $normalized, 'item_count' => $itemCount, 'subtotal' => $subtotal, @@ -466,6 +470,7 @@ require __DIR__ . '/includes/header.php';
@@ -571,6 +579,7 @@ custInput.addEventListener('input', function() { a.innerHTML = `${c.name} ${c.phone ? ''+c.phone+'' : ''}`; a.onclick = function() { custInput.value = c.name + (c.phone ? ' - ' + c.phone : ''); + custInput.dataset.id = c.id; custDropdown.classList.add('d-none'); }; custDropdown.appendChild(a); @@ -618,6 +627,7 @@ async function saveNewCustomer() { if (data.success) { customersData.push(data.customer); custInput.value = data.customer.name + (data.customer.phone ? ' - ' + data.customer.phone : ''); + custInput.dataset.id = data.customer.id; newCustomerModalObj.hide(); const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 }); Toast.fire({ icon: 'success', title: '' }); @@ -777,6 +787,7 @@ function renderCart() { function clearCart() { cart = {}; document.getElementById('posCustomer').value = ''; + delete document.getElementById('posCustomer').dataset.id; renderCart(); } @@ -795,6 +806,7 @@ function openPaymentModal() { function submitSale(method) { const branch = document.getElementById('posBranch').value || ''; const customer = document.getElementById('posCustomer').value; + const customerId = document.getElementById('posCustomer').dataset.id || ''; const itemsArr = Object.values(cart).map(item => ({ sku: item.sku, @@ -802,6 +814,7 @@ function submitSale(method) { })); document.getElementById('inputBranch').value = branch; + document.getElementById('inputCustomerId').value = customerId; document.getElementById('inputCustomer').value = customer; document.getElementById('inputPayment').value = method; document.getElementById('inputCart').value = JSON.stringify(itemsArr); diff --git a/print_receipt.php b/print_receipt.php index 2afc7b6..4133667 100644 --- a/print_receipt.php +++ b/print_receipt.php @@ -206,12 +206,10 @@ $registerNo = 'REG-01'; : ()
-
: - +
-
diff --git a/sale.php b/sale.php index 18613ec..53e37da 100644 --- a/sale.php +++ b/sale.php @@ -366,7 +366,7 @@ require __DIR__ . '/includes/header.php';
-
+

diff --git a/sales.php b/sales.php index 24a1872..755e6ea 100644 --- a/sales.php +++ b/sales.php @@ -164,15 +164,20 @@ require __DIR__ . '/includes/header.php'; - + + + + + + - +