From d19d0d272ed9b8711c97fe29dc4a6b8ef9e377d9 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 16 Feb 2026 14:02:09 +0000 Subject: [PATCH] Autosave: 20260216-140209 --- index.php | 701 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 628 insertions(+), 73 deletions(-) diff --git a/index.php b/index.php index 1773430..660bbd5 100644 --- a/index.php +++ b/index.php @@ -73,6 +73,37 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action'])) { echo json_encode($payment); exit; } + + if ($_GET['action'] === 'get_held_carts') { + header('Content-Type: application/json'); + $stmt = db()->query("SELECT h.*, c.name as customer_name FROM pos_held_carts h LEFT JOIN customers c ON h.customer_id = c.id ORDER BY h.id DESC"); + echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)); + exit; + } + + if ($_GET['action'] === 'validate_discount') { + header('Content-Type: application/json'); + $code = $_GET['code'] ?? ''; + $stmt = db()->prepare("SELECT * FROM discount_codes WHERE code = ? AND status = 'active' AND (expiry_date IS NULL OR expiry_date >= CURDATE())"); + $stmt->execute([$code]); + $discount = $stmt->fetch(PDO::FETCH_ASSOC); + if ($discount) { + echo json_encode(['success' => true, 'discount' => $discount]); + } else { + echo json_encode(['success' => false, 'error' => 'Invalid or expired code']); + } + exit; + } + + if ($_GET['action'] === 'get_customer_loyalty') { + header('Content-Type: application/json'); + $id = (int)($_GET['customer_id'] ?? 0); + $stmt = db()->prepare("SELECT loyalty_points FROM customers WHERE id = ?"); + $stmt->execute([$id]); + $points = $stmt->fetchColumn(); + echo json_encode(['success' => true, 'points' => (float)$points]); + exit; + } } if ($_SERVER['REQUEST_METHOD'] === 'POST') { @@ -118,10 +149,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Loyalty Calculation: 1 point per 1 OMR spent on net amount $loyalty_earned = floor($net_amount); - // Check if credit is used for walk-in + // Check if credit is used for walk-in or exceeds limit + $credit_total = 0; foreach ($payments as $p) { - if ($p['method'] === 'credit' && !$customer_id) { - throw new Exception("Credit payment is only allowed for registered customers"); + if ($p['method'] === 'credit') { + if (!$customer_id) { + throw new Exception("Credit payment is only allowed for registered customers"); + } + $credit_total += (float)$p['amount']; + } + } + + if ($customer_id && $credit_total > 0) { + $stmt = $db->prepare("SELECT balance, credit_limit FROM customers WHERE id = ?"); + $stmt->execute([$customer_id]); + $cust = $stmt->fetch(PDO::FETCH_ASSOC); + if ($cust['credit_limit'] > 0 && (abs($cust['balance'] - $credit_total) > $cust['credit_limit'])) { + throw new Exception("Credit limit exceeded. Current Debt: " . number_format(abs($cust['balance']), 3) . ", Limit: " . number_format($cust['credit_limit'], 3)); } } @@ -197,13 +241,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { exit; } - if (isset($_GET['action']) && $_GET['action'] === 'get_held_carts') { - header('Content-Type: application/json'); - $stmt = db()->query("SELECT h.*, c.name as customer_name FROM pos_held_carts h LEFT JOIN customers c ON h.customer_id = c.id ORDER BY h.id DESC"); - echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)); - exit; - } - if (isset($_POST['action']) && $_POST['action'] === 'delete_held_cart') { header('Content-Type: application/json'); $id = (int)$_POST['id']; @@ -213,31 +250,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { exit; } - if (isset($_GET['action']) && $_GET['action'] === 'validate_discount') { - header('Content-Type: application/json'); - $code = $_GET['code'] ?? ''; - $stmt = db()->prepare("SELECT * FROM discount_codes WHERE code = ? AND status = 'active' AND (expiry_date IS NULL OR expiry_date >= CURDATE())"); - $stmt->execute([$code]); - $discount = $stmt->fetch(PDO::FETCH_ASSOC); - if ($discount) { - echo json_encode(['success' => true, 'discount' => $discount]); - } else { - echo json_encode(['success' => false, 'error' => 'Invalid or expired code']); - } - exit; - } - - if (isset($_GET['action']) && $_GET['action'] === 'get_customer_loyalty') { - header('Content-Type: application/json'); - $id = (int)($_GET['customer_id'] ?? 0); - $stmt = db()->prepare("SELECT loyalty_points FROM customers WHERE id = ?"); - $stmt->execute([$id]); - $points = $stmt->fetchColumn(); - echo json_encode(['success' => true, 'points' => (float)$points]); - exit; - } - if (isset($_POST['edit_customer'])) { + $id = (int)$_POST['id']; $name = $_POST['name'] ?? ''; $email = $_POST['email'] ?? ''; @@ -760,7 +774,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } $message = "Settings updated successfully!"; } - } if (isset($_POST['record_payment'])) { $invoice_id = (int)$_POST['invoice_id']; @@ -807,6 +820,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } } } +} // Routing & Data Fetching @@ -1644,10 +1658,14 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; -
+
+
@@ -1674,8 +1692,8 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
Cart
- - + +
@@ -1836,42 +1854,77 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; } }, async openHeldCartsModal() { - const resp = await fetch('index.php?action=get_held_carts'); - const carts = await resp.json(); - let html = '
'; - if (carts.length === 0) html += '

No held carts

'; - carts.forEach(c => { - html += ` -
-
- ${c.cart_name}
- ${c.customer_name || 'Walk-in'} - ${c.created_at} + try { + const resp = await fetch('index.php?action=get_held_carts'); + const text = await resp.text(); + let carts; + try { + carts = JSON.parse(text); + } catch (e) { + console.error('Failed to parse held carts:', text); + throw new Error('Invalid server response'); + } + const lang = document.documentElement.lang || 'en'; + let html = '
'; + if (carts.length === 0) { + html += ` +
+ +

${lang === 'ar' ? 'لا توجد طلبات معلقة' : 'No held carts found'}

+
`; + } + carts.forEach(c => { + html += ` +
+
+
${c.cart_name}
+
+ ${c.customer_name || (lang === 'ar' ? 'عميل عابر' : 'Walk-in')} + | + ${new Date(c.created_at).toLocaleString()} +
+
+
+ + +
-
- - -
-
- `; - }); - html += '
'; - Swal.fire({ - title: 'Held Carts', - html: html, - showConfirmButton: false, - width: '600px' - }); + `; + }); + html += '
'; + Swal.fire({ + title: lang === 'ar' ? 'الطلبات المعلقة' : 'Held Carts', + html: html, + showConfirmButton: false, + width: '700px', + customClass: { + container: 'held-carts-swal' + } + }); + } catch (err) { + console.error(err); + Swal.fire('Error', 'Failed to load held carts: ' + err.message, 'error'); + } }, async resume(id) { - const resp = await fetch('index.php?action=get_held_carts'); - const carts = await resp.json(); - const c = carts.find(x => x.id == id); - if (c) { - this.items = JSON.parse(c.items_json); - document.getElementById('posCustomer').value = c.customer_id || ''; - await this.onCustomerChange(); - await this.deleteHeld(id, true); - Swal.close(); + try { + const resp = await fetch('index.php?action=get_held_carts'); + const carts = await resp.json(); + const c = carts.find(x => x.id == id); + if (c) { + this.items = JSON.parse(c.items_json); + document.getElementById('posCustomer').value = c.customer_id || ''; + await this.onCustomerChange(); + await this.deleteHeld(id, true); + Swal.close(); + } + } catch (err) { + console.error(err); + Swal.fire('Error', 'Failed to resume cart', 'error'); } }, async deleteHeld(id, silent = false) { @@ -1942,6 +1995,10 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; async checkout() { if (this.items.length === 0) return; + const customerSelect = document.getElementById('posCustomer'); + const customerName = customerSelect.options[customerSelect.selectedIndex].text; + document.getElementById('paymentCustomerName').innerText = customerName; + const subtotal = this.items.reduce((sum, item) => sum + (item.price * item.qty), 0); let discountAmount = 0; if (this.discount) { @@ -1953,8 +2010,22 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; this.payments = []; this.renderPayments(); + if (document.getElementById('creditCustomerSearch')) { + document.getElementById('creditCustomerSearch').value = ''; + } document.getElementById('paymentAmountDue').innerText = 'OMR ' + total.toFixed(3); document.getElementById('partialAmount').value = total.toFixed(3); + + // Sync credit customer selection if credit is default or already selected + const creditSection = document.getElementById('creditCustomerSection'); + if (this.selectedPaymentMethod === 'credit') { + creditSection.style.display = 'block'; + this.filterCreditCustomers(); + document.getElementById('paymentCreditCustomer').value = customerSelect.value; + } else { + creditSection.style.display = 'none'; + } + this.updateRemaining(); const modal = new bootstrap.Modal(document.getElementById('posPaymentModal')); @@ -1964,6 +2035,20 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; this.selectedPaymentMethod = method; document.querySelectorAll('.payment-method-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); + + const creditSection = document.getElementById('creditCustomerSection'); + if (method === 'credit') { + creditSection.style.display = 'block'; + if (document.getElementById('creditCustomerSearch')) { + document.getElementById('creditCustomerSearch').value = ''; + } + this.filterCreditCustomers(); + // Sync with main customer select + document.getElementById('paymentCreditCustomer').value = document.getElementById('posCustomer').value; + } else { + creditSection.style.display = 'none'; + } + this.updateRemaining(); }, fillPartial(amount) { @@ -2222,6 +2307,44 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; document.getElementById('posReceiptModal').addEventListener('hidden.bs.modal', function () { location.reload(); }, { once: true }); + }, + filterCreditCustomers() { + const searchInput = document.getElementById('creditCustomerSearch'); + if (!searchInput) return; + const search = searchInput.value.toLowerCase(); + const select = document.getElementById('paymentCreditCustomer'); + if (!select) return; + + if (!this.allCreditCustomers || (this.allCreditCustomers.length <= 1 && select.options.length > 1)) { + this.allCreditCustomers = Array.from(select.options).map(opt => ({ + value: opt.value, + text: opt.text, + search: opt.getAttribute('data-search') || opt.text.toLowerCase() + })); + } + + if (!this.allCreditCustomers) return; + + const currentValue = select.value; + select.innerHTML = ''; + + this.allCreditCustomers.forEach(opt => { + if (opt.value === "" || opt.search.includes(search)) { + const o = document.createElement('option'); + o.value = opt.value; + o.text = opt.text; + o.setAttribute('data-search', opt.search); + if (opt.value === currentValue) o.selected = true; + select.appendChild(o); + } + }); + }, + async syncCustomer(val) { + document.getElementById('posCustomer').value = val; + const customerSelect = document.getElementById('posCustomer'); + const customerName = customerSelect.options[customerSelect.selectedIndex].text; + document.getElementById('paymentCustomerName').innerText = customerName; + await this.onCustomerChange(); } }; @@ -2372,14 +2495,29 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; - + + OMR OMR OMR
- + + + + + + +
+ + +
@@ -3379,6 +3517,206 @@ document.addEventListener('DOMContentLoaded', function() { + + + + + +