From 68b1e34fe918f17e81e735d0ce8d4174351cc99e Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 24 Feb 2026 14:09:00 +0000 Subject: [PATCH] Autosave: 20260224-140900 --- assets/images/products/prod_699dab0588305.jpg | Bin 0 -> 19425 bytes assets/js/main.js | 1003 +++-------------- pos.php | 492 +++----- 3 files changed, 323 insertions(+), 1172 deletions(-) create mode 100644 assets/images/products/prod_699dab0588305.jpg diff --git a/assets/images/products/prod_699dab0588305.jpg b/assets/images/products/prod_699dab0588305.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1da33d3b87c35982e53930c1e15f589e8696b573 GIT binary patch literal 19425 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ℑx-_7LIr)_rD({qqC+6J&Vigt! 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 { // SweetAlert2 Toast Mixin - const Toast = Swal.mixin({ + const Toast = (typeof Swal !== 'undefined') ? Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, @@ -10,7 +10,23 @@ document.addEventListener('DOMContentLoaded', () => { toast.addEventListener('mouseenter', Swal.stopTimer); toast.addEventListener('mouseleave', Swal.resumeTimer); } - }); + }) : null; + + function showToast(msg, type = 'primary') { + if (!Toast) { + console.log(`Toast: ${msg} (${type})`); + return; + } + let icon = 'info'; + if (type === 'success') icon = 'success'; + if (type === 'danger') icon = 'error'; + if (type === 'warning') icon = 'warning'; + + Toast.fire({ + icon: icon, + title: msg + }); + } // Global settings with fallbacks const settings = (typeof COMPANY_SETTINGS !== 'undefined') ? COMPANY_SETTINGS : { @@ -20,24 +36,21 @@ document.addEventListener('DOMContentLoaded', () => { }; let cart = []; - let currentOrderId = null; // Track order ID for updates + let currentOrderId = null; const cartItemsContainer = document.getElementById('cart-items'); const cartTotalPrice = document.getElementById('cart-total-price'); const cartSubtotal = document.getElementById('cart-subtotal'); const cartVatInput = document.getElementById('cart-vat-input'); - // Updated Button References const quickOrderBtn = document.getElementById('quick-order-btn'); const placeOrderBtn = document.getElementById('place-order-btn'); const recallBtn = document.getElementById('recall-bill-btn'); const reprintReceiptBtn = document.getElementById('reprint-receipt-btn'); - // Recall Modal const recallModalEl = document.getElementById('recallOrderModal'); const recallModal = recallModalEl ? new bootstrap.Modal(recallModalEl) : null; const recallList = document.getElementById('recall-orders-list'); - // Loyalty State let isLoyaltyRedemption = false; const loyaltySection = document.getElementById('loyalty-section'); const loyaltyPointsDisplay = document.getElementById('loyalty-points-display'); @@ -45,25 +58,21 @@ document.addEventListener('DOMContentLoaded', () => { const redeemLoyaltyBtn = document.getElementById('redeem-loyalty-btn'); const viewPointsHistoryBtn = document.getElementById('view-points-history-btn'); - // Points History Modal const pointsHistoryModalEl = document.getElementById('pointsHistoryModal'); const pointsHistoryModal = pointsHistoryModalEl ? new bootstrap.Modal(pointsHistoryModalEl) : null; const pointsHistoryBody = document.getElementById('points-history-body'); const pointsHistoryEmpty = document.getElementById('points-history-empty'); - // Table Management let currentTableId = null; let currentTableName = null; const tableDisplay = document.getElementById('current-table-display'); const tableModalEl = document.getElementById('tableSelectionModal'); const tableSelectionModal = tableModalEl ? new bootstrap.Modal(tableModalEl) : null; - // Variant Management const variantModalEl = document.getElementById('variantSelectionModal'); const variantSelectionModal = variantModalEl ? new bootstrap.Modal(variantModalEl) : null; let pendingProduct = null; - // Customer Search Elements const customerSearchInput = document.getElementById('customer-search'); const customerResults = document.getElementById('customer-results'); const selectedCustomerId = document.getElementById('selected-customer-id'); @@ -72,51 +81,50 @@ document.addEventListener('DOMContentLoaded', () => { const customerNameDisplay = document.getElementById('customer-name-display'); let currentCustomer = null; - // Payment Modal const paymentModalEl = document.getElementById('paymentSelectionModal'); const paymentSelectionModal = paymentModalEl ? new bootstrap.Modal(paymentModalEl) : null; const paymentMethodsContainer = document.getElementById('payment-methods-container'); - // Product Search & Filter - const productSearchInput = document.getElementById('product-search-input'); + const productSearchInput = document.getElementById('product-search'); let currentCategory = 'all'; let currentSearchQuery = ''; - // Helper for currency function formatCurrency(amount) { const symbol = settings.currency_symbol || '$'; const decimals = parseInt(settings.currency_decimals || 2); return symbol + parseFloat(amount).toFixed(decimals); } - // --- Product Filtering (Category + Search) --- function filterProducts() { const items = document.querySelectorAll('.product-item'); items.forEach(item => { - const matchesCategory = (currentCategory == 'all' || item.dataset.categoryId == currentCategory); - const name = item.querySelector('.card-title').textContent.toLowerCase(); - const matchesSearch = name.includes(currentSearchQuery); + const matchesCategory = (currentCategory == 'all' || item.dataset.category == currentCategory); + const name = (item.dataset.name || '').toLowerCase(); + const sku = (item.dataset.sku || '').toLowerCase(); + const matchesSearch = name.includes(currentSearchQuery) || sku.includes(currentSearchQuery); - if (matchesCategory && matchesSearch) { - item.style.display = 'block'; - } else { - item.style.display = 'none'; - } + item.style.display = (matchesCategory && matchesSearch) ? 'block' : 'none'; }); } window.filterCategory = function(categoryId, btnElement) { currentCategory = categoryId; - - // Update Active Button State + document.querySelectorAll('.category-btn').forEach(btn => btn.classList.remove('active')); if (btnElement) { - document.querySelectorAll('.category-btn').forEach(btn => btn.classList.remove('active')); btnElement.classList.add('active'); + } else { + const btn = document.querySelector(`.category-btn[data-category="${categoryId}"]`); + if (btn) btn.classList.add('active'); } - filterProducts(); }; + document.querySelectorAll('.category-btn').forEach(btn => { + btn.addEventListener('click', () => { + filterCategory(btn.dataset.category, btn); + }); + }); + if (productSearchInput) { productSearchInput.addEventListener('input', (e) => { currentSearchQuery = e.target.value.trim().toLowerCase(); @@ -124,19 +132,17 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // --- Recall Order Logic --- - if (recallBtn) { - recallBtn.addEventListener('click', () => { - fetchRecallOrders(); - }); - } + window.openRecallOrderModal = function() { + if (!recallModal) return; + fetchRecallOrders(); + recallModal.show(); + }; function fetchRecallOrders() { if (!recallList) return; - recallList.innerHTML = '

'; - if (recallModal) recallModal.show(); + recallList.innerHTML = '
'; - const outletId = new URLSearchParams(window.location.search).get('outlet_id') || 1; + const outletId = CURRENT_OUTLET ? CURRENT_OUTLET.id : 1; fetch(`api/recall_orders.php?action=list&outlet_id=${outletId}`) .then(res => res.json()) .then(data => { @@ -148,11 +154,7 @@ document.addEventListener('DOMContentLoaded', () => { item.innerHTML = `
Order #${order.id} ${order.order_type}
- - ${order.customer_name || 'Guest'} - ${order.table_number ? ' • Table ' + order.table_number : ''} - • ${order.time_formatted} - + ${order.customer_name || 'Guest'} ${order.table_number ? ' • Table ' + order.table_number : ''} • ${order.time_formatted}
${formatCurrency(order.total_amount)}
@@ -166,7 +168,7 @@ document.addEventListener('DOMContentLoaded', () => { recallList.innerHTML = '
No unpaid bills found.
'; } }) - .catch(err => { + .catch(() => { recallList.innerHTML = '
Error fetching orders.
'; }); } @@ -176,17 +178,10 @@ document.addEventListener('DOMContentLoaded', () => { .then(res => res.json()) .then(data => { if (data.success) { - // Set Order ID currentOrderId = data.order.id; + if (data.customer) selectCustomer(data.customer); + else if (clearCustomerBtn) clearCustomerBtn.click(); - // Set Customer - if (data.customer) { - selectCustomer(data.customer); - } else { - if (clearCustomerBtn) clearCustomerBtn.click(); - } - - // Set Table/Order Type const otInput = document.querySelector(`input[name="order_type"][value="${data.order.order_type}"]`); if (otInput) { otInput.checked = true; @@ -196,36 +191,30 @@ document.addEventListener('DOMContentLoaded', () => { checkOrderType(); } } - - // Populate Cart cart = data.items; - updateCart(); if (recallModal) recallModal.hide(); showToast(`Order #${orderId} loaded!`, 'success'); } else { showToast(data.error || 'Failed to load order', 'danger'); } - }) - .catch(err => showToast('Error loading order details', 'danger')); + }); } - // --- Customer Search --- - let searchTimeout; if (customerSearchInput) { + let searchTimeout; customerSearchInput.addEventListener('input', (e) => { const query = e.target.value.trim(); clearTimeout(searchTimeout); - if (query.length < 2) { - customerResults.style.display = 'none'; + if (customerResults) customerResults.style.display = 'none'; return; } - searchTimeout = setTimeout(() => { fetch(`api/search_customers.php?q=${encodeURIComponent(query)}`) .then(res => res.json()) .then(data => { + if (!customerResults) return; customerResults.innerHTML = ''; if (data.length > 0) { data.forEach(cust => { @@ -247,10 +236,8 @@ document.addEventListener('DOMContentLoaded', () => { }); }, 300); }); - - // Close search on click outside document.addEventListener('click', (e) => { - if (!customerSearchInput.contains(e.target) && !customerResults.contains(e.target)) { + if (customerResults && !customerSearchInput.contains(e.target) && !customerResults.contains(e.target)) { customerResults.style.display = 'none'; } }); @@ -258,137 +245,48 @@ document.addEventListener('DOMContentLoaded', () => { function selectCustomer(cust) { currentCustomer = cust; - selectedCustomerId.value = cust.id; - customerNameDisplay.textContent = cust.name; - customerSearchInput.value = cust.name; // Show name in input - customerSearchInput.disabled = true; // Lock input - customerResults.style.display = 'none'; - clearCustomerBtn.classList.remove('d-none'); + if (selectedCustomerId) selectedCustomerId.value = cust.id; + if (customerNameDisplay) customerNameDisplay.textContent = cust.name; + if (customerSearchInput) { + customerSearchInput.value = cust.name; + customerSearchInput.disabled = true; + } + if (customerResults) customerResults.style.display = 'none'; + if (clearCustomerBtn) clearCustomerBtn.classList.remove('d-none'); - // Loyalty Logic if (loyaltySection && typeof LOYALTY_SETTINGS !== 'undefined' && LOYALTY_SETTINGS.is_enabled) { loyaltySection.classList.remove('d-none'); - loyaltyPointsDisplay.textContent = cust.points + ' pts'; - - if (cust.eligible_for_free_meal) { - redeemLoyaltyBtn.disabled = false; - loyaltyMessage.innerHTML = 'Eligible for Free Meal!'; - } else { - redeemLoyaltyBtn.disabled = true; - const needed = cust.points_needed || 0; - loyaltyMessage.textContent = `${needed} pts away from a free meal.`; + if (loyaltyPointsDisplay) loyaltyPointsDisplay.textContent = cust.points + ' pts'; + if (redeemLoyaltyBtn) { + redeemLoyaltyBtn.disabled = !cust.eligible_for_free_meal; + if (loyaltyMessage) { + if (cust.eligible_for_free_meal) loyaltyMessage.innerHTML = 'Eligible for Free Meal!'; + else loyaltyMessage.textContent = `${cust.points_needed || 0} pts away from a free meal.`; + } } } - isLoyaltyRedemption = false; } if (clearCustomerBtn) { clearCustomerBtn.addEventListener('click', () => { currentCustomer = null; - selectedCustomerId.value = ''; - customerSearchInput.value = ''; - customerSearchInput.disabled = false; + if (selectedCustomerId) selectedCustomerId.value = ''; + if (customerSearchInput) { + customerSearchInput.value = ''; + customerSearchInput.disabled = false; + } clearCustomerBtn.classList.add('d-none'); if (customerInfo) customerInfo.classList.add('d-none'); - if (loyaltySection) loyaltySection.classList.add('d-none'); isLoyaltyRedemption = false; updateCart(); - - customerSearchInput.focus(); - }); - } - - // Loyalty Redeem Click - if (redeemLoyaltyBtn) { - redeemLoyaltyBtn.addEventListener('click', () => { - if (typeof LOYALTY_SETTINGS === 'undefined' || !LOYALTY_SETTINGS.is_enabled) return; - - if (cart.length === 0) { - showToast("Cart is empty!", "warning"); - return; - } - - if (cart.length > 1) { - showToast("Can only redeem a free meal with exactly one item in cart!", "warning"); - return; - } - - if (!currentCustomer || !currentCustomer.eligible_for_free_meal) return; - - Swal.fire({ - title: 'Redeem Loyalty?', - text: "Redeem points for a free meal? This will finalize the order immediately.", - icon: 'question', - showCancelButton: true, - confirmButtonColor: '#198754', - cancelButtonColor: '#6c757d', - confirmButtonText: 'Yes, redeem and finish!' - }).then((result) => { - if (result.isConfirmed) { - isLoyaltyRedemption = true; - updateCart(); - processOrder(null, 'Loyalty Redeem'); - } - }); }); } - // Points History View - if (viewPointsHistoryBtn) { - viewPointsHistoryBtn.addEventListener('click', () => { - if (!currentCustomer) return; - fetchPointsHistory(currentCustomer.id); - }); - } - - function fetchPointsHistory(customerId) { - if (!pointsHistoryBody || !pointsHistoryModal) return; - pointsHistoryBody.innerHTML = '
Loading...'; - pointsHistoryEmpty.classList.add('d-none'); - pointsHistoryModal.show(); - - fetch(`api/customer_loyalty_history.php?customer_id=${customerId}`) - .then(res => res.json()) - .then(data => { - pointsHistoryBody.innerHTML = ''; - if (data.success && data.history.length > 0) { - data.history.forEach(item => { - const tr = document.createElement('tr'); - const dateObj = new Date(item.created_at); - const date = dateObj.toLocaleDateString('en-GB', { day: '2-digit', month: 'short' }) + ' ' + - dateObj.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }); - - const pointsClass = item.points_change > 0 ? 'text-success' : 'text-danger'; - const pointsPrefix = item.points_change > 0 ? '+' : ''; - - tr.innerHTML = ` - ${date} - -
${item.reason}
- ${item.order_id ? `
Order #${item.order_id}
` : ''} - - ${pointsPrefix}${item.points_change} - `; - pointsHistoryBody.appendChild(tr); - }); - } else { - pointsHistoryEmpty.classList.remove('d-none'); - } - }) - .catch(err => { - pointsHistoryBody.innerHTML = 'Error loading history'; - }); - } - - // --- Table & Order Type Logic --- - const orderTypeInputs = document.querySelectorAll('input[name="order_type"]'); - - function checkOrderType() { + window.checkOrderType = function() { const checked = document.querySelector('input[name="order_type"]:checked'); if (!checked) return; - const selected = checked.value; if (selected === 'dine-in') { if (!currentTableId && tableSelectionModal) openTableSelectionModal(); @@ -396,240 +294,64 @@ document.addEventListener('DOMContentLoaded', () => { } else { if (tableDisplay) tableDisplay.style.display = 'none'; } - } + }; - orderTypeInputs.forEach(input => { + document.querySelectorAll('input[name="order_type"]').forEach(input => { input.addEventListener('change', checkOrderType); }); - function openTableSelectionModal() { - if (!tableSelectionModal) return; - const container = document.getElementById('table-list-container'); - if (!container) return; - container.innerHTML = '
'; - - // Update Modal Title - const modalTitle = document.querySelector('#tableSelectionModal .modal-title'); - if (modalTitle && typeof CURRENT_OUTLET !== 'undefined') { - modalTitle.textContent = `Select Table - ${CURRENT_OUTLET.name}`; - } - - tableSelectionModal.show(); - - const outletId = new URLSearchParams(window.location.search).get('outlet_id') || 1; - fetch(`api/tables.php?outlet_id=${outletId}`) - .then(res => res.json()) - .then(data => { - if (data.success) { - renderTables(data.tables); - } else { - container.innerHTML = `
${data.error}
`; - } - }) - .catch(() => { - container.innerHTML = `
Error loading tables.
`; + window.handleProductClick = function(product, variants) { + if (variants && variants.length > 0) { + openVariantModal(product, variants); + } else { + addToCart({ + id: product.id, name: product.name, name_ar: product.name_ar || "", + price: parseFloat(product.price), base_price: parseFloat(product.price), + hasVariants: false, quantity: 1, variant_id: null, variant_name: null }); - } - - function renderTables(tables) { - const container = document.getElementById('table-list-container'); - if (!container) return; - container.innerHTML = ''; - - if (tables.length === 0) { - const outletName = (typeof CURRENT_OUTLET !== 'undefined') ? CURRENT_OUTLET.name : 'this outlet'; - container.innerHTML = `
- - No tables found for ${outletName}. -
`; - return; } - - tables.forEach(table => { - const isOccupied = table.is_occupied; - const col = document.createElement('div'); - col.className = 'col-6 col-md-4 col-lg-3'; - col.innerHTML = ` -
-
-
${table.name}
- ${table.capacity} Pax -
${isOccupied ? 'Occupied' : 'Available'}
-
-
- `; - container.appendChild(col); - }); - } - - window.selectTable = function(id, name) { - currentTableId = id; - currentTableName = name; - if (tableDisplay) { - tableDisplay.innerHTML = `Table: ${name}`; - tableDisplay.style.display = 'block'; - } - if (tableSelectionModal) tableSelectionModal.hide(); - showToast(`Selected Table: ${name}`, 'success'); }; - // --- Cart Logic --- - document.querySelectorAll('.add-to-cart').forEach(card => { - card.addEventListener('click', (e) => { - const target = e.currentTarget; - const product = { - id: target.dataset.id, - name: target.dataset.name, - name_ar: target.dataset.nameAr || '', - price: parseFloat(target.dataset.price), - base_price: parseFloat(target.dataset.price), - hasVariants: target.dataset.hasVariants === 'true', - quantity: 1, - variant_id: null, - variant_name: null - }; - - if (product.hasVariants) { - openVariantModal(product); - } else { - addToCart(product); - } - }); - }); - - function openVariantModal(product) { + function openVariantModal(product, variants) { if (!variantSelectionModal) return; - pendingProduct = product; - const variants = (typeof PRODUCT_VARIANTS !== 'undefined') ? (PRODUCT_VARIANTS[product.id] || []) : []; const list = document.getElementById('variant-list'); const title = document.getElementById('variantModalTitle'); - if (title) title.textContent = `Select option for ${product.name}`; - + if (title) title.textContent = `Option: ${product.name}`; if (!list) return; list.innerHTML = ''; - variants.forEach(v => { const btn = document.createElement('button'); btn.className = 'list-group-item list-group-item-action d-flex justify-content-between align-items-center'; - const adj = parseFloat(v.price_adjustment); - const finalPrice = product.base_price + adj; - - btn.innerHTML = ` - ${v.name} - ${formatCurrency(finalPrice)} - `; + const finalPrice = parseFloat(product.price) + parseFloat(v.price_adjustment); + btn.innerHTML = `${v.name}${formatCurrency(finalPrice)}`; btn.onclick = () => { - pendingProduct.variant_id = v.id; - pendingProduct.variant_name = v.name; - pendingProduct.price = finalPrice; - addToCart(pendingProduct); + addToCart({ + id: product.id, name: product.name, name_ar: product.name_ar || "", + price: finalPrice, base_price: parseFloat(product.price), + hasVariants: true, quantity: 1, variant_id: v.id, variant_name: v.name + }); variantSelectionModal.hide(); }; list.appendChild(btn); }); - variantSelectionModal.show(); } - function addToCart(product) { + window.addToCart = function(product) { const existing = cart.find(item => item.id === product.id && item.variant_id === product.variant_id); - if (existing) { - existing.quantity++; - } else { - cart.push({...product}); - } + if (existing) existing.quantity++; + else cart.push({...product}); updateCart(); - } + }; window.changeQuantity = function(index, delta) { if (cart[index]) { cart[index].quantity += delta; - if (cart[index].quantity <= 0) { - removeFromCart(index); - } else { - updateCart(); - } + if (cart[index].quantity <= 0) cart.splice(index, 1); + updateCart(); } }; - function updateCart() { - if (reprintReceiptBtn) { - if (currentOrderId) reprintReceiptBtn.classList.remove('d-none'); - else reprintReceiptBtn.classList.add('d-none'); - } - - if (cart.length === 0) { - if (cartItemsContainer) { - cartItemsContainer.innerHTML = ` -
- -

Cart is empty

-
`; - } - if (cartSubtotal) cartSubtotal.innerText = formatCurrency(0); - if (cartVatInput) cartVatInput.value = 0; - if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(0); - if (quickOrderBtn) quickOrderBtn.disabled = true; - if (placeOrderBtn) placeOrderBtn.disabled = true; - return; - } - - if (cartItemsContainer) cartItemsContainer.innerHTML = ''; - let subtotal = 0; - - cart.forEach((item, index) => { - const itemTotal = item.price * item.quantity; - subtotal += itemTotal; - - if (cartItemsContainer) { - const row = document.createElement('div'); - row.className = 'd-flex justify-content-between align-items-center mb-3 border-bottom pb-2'; - - const variantLabel = item.variant_name ? `${item.variant_name}` : ''; - const arabicNameDisplay = item.name_ar ? `
${item.name_ar}
` : ''; - - row.innerHTML = ` -
-
${item.name}
- ${arabicNameDisplay} -
${formatCurrency(item.price)} ${variantLabel}
-
-
- - ${item.quantity} - -
-
-
${formatCurrency(itemTotal)}
- -
- `; - cartItemsContainer.appendChild(row); - } - }); - - if (cartSubtotal) cartSubtotal.innerText = formatCurrency(subtotal); - - let vat = 0; - if (isLoyaltyRedemption) { - vat = -subtotal; - } else { - const vatRate = parseFloat(settings.vat_rate) || 0; - vat = subtotal * (vatRate / 100); - } - - if (cartVatInput) cartVatInput.value = vat.toFixed(2); - - let total = subtotal + vat; - if (total < 0) total = 0; - - if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(total); - if (quickOrderBtn) quickOrderBtn.disabled = false; - if (placeOrderBtn) placeOrderBtn.disabled = false; - } - window.removeFromCart = function(index) { cart.splice(index, 1); updateCart(); @@ -637,494 +359,115 @@ document.addEventListener('DOMContentLoaded', () => { window.clearCart = function() { if (cart.length === 0) return; - Swal.fire({ - title: 'Clear Cart?', - text: "Are you sure you want to remove all items from the cart?", - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#dc3545', - cancelButtonColor: '#6c757d', - confirmButtonText: 'Yes, clear it!' - }).then((result) => { - if (result.isConfirmed) { - cart = []; - if (cartVatInput) cartVatInput.value = 0; - currentOrderId = null; - isLoyaltyRedemption = false; - updateCart(); - showToast("Cart cleared", "success"); - } - }); + cart = []; + currentOrderId = null; + isLoyaltyRedemption = false; + updateCart(); + showToast("Cart cleared", "success"); }; - // --- Payment Selection Logic --- - function renderPaymentMethods() { - if (!paymentMethodsContainer) return; - paymentMethodsContainer.innerHTML = ''; - - if (typeof PAYMENT_TYPES !== 'undefined' && PAYMENT_TYPES.length > 0) { - PAYMENT_TYPES.forEach(pt => { - const col = document.createElement('div'); - col.className = 'col-6'; - col.innerHTML = ` - - `; - paymentMethodsContainer.appendChild(col); - }); - } else { - paymentMethodsContainer.innerHTML = '
No payment methods configured.
'; + function updateCart() { + if (!cartItemsContainer) return; + if (cart.length === 0) { + cartItemsContainer.innerHTML = '

Cart is empty

'; + if (cartSubtotal) cartSubtotal.innerText = formatCurrency(0); + if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(0); + if (quickOrderBtn) quickOrderBtn.disabled = true; + if (placeOrderBtn) placeOrderBtn.disabled = true; + return; } - } - function getPaymentIcon(name) { - const n = name.toLowerCase(); - if (n.includes('cash')) return 'bi-cash-coin'; - if (n.includes('card') || n.includes('visa') || n.includes('master')) return 'bi-credit-card'; - if (n.includes('qr') || n.includes('scan')) return 'bi-qr-code'; - if (n.includes('bank') || n.includes('transfer')) return 'bi-building-columns'; - return 'bi-wallet2'; - } + cartItemsContainer.innerHTML = ''; + let subtotal = 0; + cart.forEach((item, index) => { + const itemTotal = item.price * item.quantity; + subtotal += itemTotal; + const row = document.createElement('div'); + row.className = 'd-flex justify-content-between align-items-center mb-3 border-bottom pb-2'; + row.innerHTML = ` +
+
${item.name}
+ ${item.name_ar ? `
${item.name_ar}
` : ''} +
${formatCurrency(item.price)}
+
+
+ + ${item.quantity} + +
+
+
${formatCurrency(itemTotal)}
+ +
+ `; + cartItemsContainer.appendChild(row); + }); - renderPaymentMethods(); - - // --- Checkout Flow (Quick Order) --- - function validateOrder() { - if (cart.length === 0) return false; - - const orderTypeInput = document.querySelector('input[name="order_type"]:checked'); - const orderType = orderTypeInput ? orderTypeInput.value : 'takeaway'; - - if (orderType === 'dine-in' && !currentTableId) { - showToast('Please select a table first', 'warning'); - openTableSelectionModal(); - return false; - } - return true; + if (cartSubtotal) cartSubtotal.innerText = formatCurrency(subtotal); + const vatRate = parseFloat(settings.vat_rate) || 0; + const vat = subtotal * (vatRate / 100); + if (cartVatInput) cartVatInput.value = vat.toFixed(2); + const total = subtotal + vat; + if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(total); + if (quickOrderBtn) quickOrderBtn.disabled = false; + if (placeOrderBtn) placeOrderBtn.disabled = false; } if (quickOrderBtn) { quickOrderBtn.addEventListener('click', () => { - if (validateOrder() && paymentSelectionModal) { + if (cart.length > 0 && paymentSelectionModal) { + renderPaymentMethods(); paymentSelectionModal.show(); } }); } - // --- Place Order (Pay Later) Flow --- if (placeOrderBtn) { placeOrderBtn.addEventListener('click', () => { - if (validateOrder()) { - Swal.fire({ - title: 'Place Order?', - text: "Place order without immediate payment?", - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#ffc107', - cancelButtonColor: '#6c757d', - confirmButtonText: 'Yes, place order' - }).then((result) => { - if (result.isConfirmed) { - processOrder(null, 'Pay Later'); - } - }); - } + if (cart.length > 0) processOrder(null, 'Pay Later'); }); } + function renderPaymentMethods() { + if (!paymentMethodsContainer) return; + paymentMethodsContainer.innerHTML = ''; + if (typeof PAYMENT_TYPES !== 'undefined') { + PAYMENT_TYPES.forEach(pt => { + const col = document.createElement('div'); + col.className = 'col-6'; + col.innerHTML = ``; + paymentMethodsContainer.appendChild(col); + }); + } + } + window.processOrder = function(paymentTypeId, paymentTypeName) { const orderTypeInput = document.querySelector('input[name="order_type"]:checked'); const orderType = orderTypeInput ? orderTypeInput.value : 'takeaway'; const subtotal = cart.reduce((acc, item) => acc + (item.price * item.quantity), 0); - const vat = parseFloat(cartVatInput ? cartVatInput.value : 0) || 0; - const totalAmount = Math.max(0, subtotal + vat); - const custId = selectedCustomerId ? selectedCustomerId.value : null; - + const vatRate = parseFloat(settings.vat_rate) || 0; + const vat = subtotal * (vatRate / 100); + const orderData = { order_id: currentOrderId, table_number: (orderType === 'dine-in') ? currentTableId : null, order_type: orderType, - customer_id: custId || null, - outlet_id: new URLSearchParams(window.location.search).get('outlet_id') || 1, + customer_id: selectedCustomerId ? selectedCustomerId.value : null, + outlet_id: CURRENT_OUTLET ? CURRENT_OUTLET.id : 1, payment_type_id: paymentTypeId, - total_amount: totalAmount, - vat: vat, - redeem_loyalty: isLoyaltyRedemption, - items: cart.map(item => ({ - product_id: item.id, - quantity: item.quantity, - unit_price: item.price, - variant_id: item.variant_id - })) + total_amount: subtotal + vat, + vat: vat, items: cart.map(item => ({ product_id: item.id, quantity: item.quantity, unit_price: item.price, variant_id: item.variant_id })) }; - - // Disable buttons - if (paymentMethodsContainer) { - const btns = paymentMethodsContainer.querySelectorAll('button'); - btns.forEach(b => b.disabled = true); - } - if (quickOrderBtn) quickOrderBtn.disabled = true; - if (placeOrderBtn) placeOrderBtn.disabled = true; - fetch('api/order.php', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(orderData) - }) + fetch('api/order.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) }) .then(res => res.json()) .then(data => { - if (paymentMethodsContainer) { - const btns = paymentMethodsContainer.querySelectorAll('button'); - btns.forEach(b => b.disabled = false); - } - if (quickOrderBtn) quickOrderBtn.disabled = false; - if (placeOrderBtn) placeOrderBtn.disabled = false; - - if (paymentSelectionModal) paymentSelectionModal.hide(); - if (data.success) { - // Print Receipt - printReceipt({ - orderId: data.order_id, - customer: currentCustomer, - items: [...cart], - total: totalAmount, - vat: vat, - orderType: orderType, - tableNumber: (orderType === 'dine-in') ? currentTableName : null, - date: new Date().toLocaleString(), - paymentMethod: paymentTypeName, - loyaltyRedeemed: isLoyaltyRedemption - }); - - cart = []; - if (cartVatInput) cartVatInput.value = 0; - currentOrderId = null; - isLoyaltyRedemption = false; - updateCart(); - if (clearCustomerBtn) clearCustomerBtn.click(); showToast(`Order #${data.order_id} placed!`, 'success'); - } else { - showToast(`Error: ${data.error}`, 'danger'); - } - }) - .catch(err => { - if (paymentMethodsContainer) { - const btns = paymentMethodsContainer.querySelectorAll('button'); - btns.forEach(b => b.disabled = false); - } - if (quickOrderBtn) quickOrderBtn.disabled = false; - if (placeOrderBtn) placeOrderBtn.disabled = false; - - if (paymentSelectionModal) paymentSelectionModal.hide(); - showToast('Network Error', 'danger'); + clearCart(); + if (paymentSelectionModal) paymentSelectionModal.hide(); + if (clearCustomerBtn) clearCustomerBtn.click(); + } else showToast(data.error, 'danger'); }); }; - - function showToast(msg, type = 'primary') { - let icon = 'info'; - if (type === 'success') icon = 'success'; - if (type === 'danger') icon = 'error'; - if (type === 'warning') icon = 'warning'; - - Toast.fire({ - icon: icon, - title: msg - }); - } - - window.reprintCurrentReceipt = function() { - if (!currentOrderId) return; - - const subtotal = cart.reduce((acc, item) => acc + (item.price * item.quantity), 0); - const vat = parseFloat(cartVatInput ? cartVatInput.value : 0) || 0; - const totalAmount = Math.max(0, subtotal + vat); - const orderType = document.querySelector('input[name="order_type"]:checked').value; - - printReceipt({ - orderId: currentOrderId, - customer: currentCustomer, - items: [...cart], - total: totalAmount, - vat: vat, - orderType: orderType, - tableNumber: (orderType === 'dine-in') ? currentTableName : null, - date: new Date().toLocaleString() + ' (Reprint)', - paymentMethod: 'Previously Settled', - loyaltyRedeemed: isLoyaltyRedemption - }); - }; - - function printReceipt(data) { - const width = 400; - const height = 800; - const left = (screen.width - width) / 2; - const top = (screen.height - height) / 2; - - const win = window.open('', '_blank', `width=${width},height=${height},top=${top},left=${left}`); - - if (!win) { - alert('Please allow popups for this website to print receipts.'); - return; - } - - const tr = { - 'Order': 'الطلب', - 'Type': 'النوع', - 'Date': 'التاريخ', - 'Staff': 'الموظف', - 'Table': 'طاولة', - 'Payment': 'الدفع', - 'ITEM': 'الصنف', - 'TOTAL': 'المجموع', - 'Subtotal': 'المجموع الفرعي', - 'VAT': 'ضريبة القيمة المضافة', - 'Tax Included': 'شامل الضريبة', - 'THANK YOU FOR YOUR VISIT!': 'شكراً لزيارتكم!', - 'Please come again.': 'يرجى زيارتنا مرة أخرى.', - 'Customer Details': 'تفاصيل العميل', - 'Tel': 'هاتف', - 'takeaway': 'سفري', - 'dine-in': 'محلي', - 'delivery': 'توصيل', - 'VAT No': 'الرقم الضريبي', - 'CTR No': 'رقم السجل التجاري' - }; - - const itemsHtml = data.items.map(item => ` - - -
${item.name}
- ${item.name_ar ? `
${item.name_ar}
` : ''} - ${item.variant_name ? `
(${item.variant_name})
` : ''} -
${item.quantity} x ${formatCurrency(item.price)}
- - ${formatCurrency(item.quantity * item.price)} - - `).join(''); - - const customerHtml = data.customer ? ` -
-
- Customer Details - ${tr['Customer Details']} -
-
-
${data.customer.name}
- ${data.customer.phone ? `
Tel: ${data.customer.phone}
` : ''} - ${data.customer.address ? `
${data.customer.address}
` : ''} -
- ` : ''; - - const tableHtml = data.tableNumber ? ` -
- Table: ${data.tableNumber} - ${tr['Table']}: ${data.tableNumber} -
` : ''; - - const paymentHtml = data.paymentMethod ? ` -
- Payment: ${data.paymentMethod} - ${tr['Payment']}: ${data.paymentMethod} -
` : ''; - - const loyaltyHtml = data.loyaltyRedeemed ? `
* Loyalty Reward Applied *
` : ''; - - const subtotal = data.total - data.vat; - const baseUrl = (typeof BASE_URL !== 'undefined') ? BASE_URL : ''; - const logoHtml = settings.logo_url ? `` : ''; - const outletName = (typeof CURRENT_OUTLET !== 'undefined') ? CURRENT_OUTLET.name : ''; - const staffName = (typeof CURRENT_USER !== 'undefined') ? CURRENT_USER.name : ''; - - const html = ` - - - Receipt #${data.orderId} - - - - -
- ${logoHtml} -

${settings.company_name}

-
${outletName}
-
${settings.address}
-
Tel: ${settings.phone}
- ${settings.vat_number ? `
VAT No / الرقم الضريبي: ${settings.vat_number}
` : ''} - ${settings.ctr_number ? `
CTR No / رقم السجل: ${settings.ctr_number}
` : ''} -
- -
- -
-
- Order: #${data.orderId} - ${tr['Order']}: #${data.orderId} -
-
- Type: ${data.orderType.toUpperCase()} - ${tr['Type']}: ${tr[data.orderType] || data.orderType} -
-
- Date: ${data.date} - ${tr['Date']}: ${data.date} -
-
- Staff: ${staffName} - ${tr['Staff']}: ${staffName} -
-
- - ${tableHtml} - ${paymentHtml} - ${loyaltyHtml} - -
- ${customerHtml} - - - - - - - - - - ${itemsHtml} - -
- ITEM / الصنف - - TOTAL / المجموع -
- -
- -
- - - - - - ${Math.abs(data.vat) > 0 ? ` - - - - ` : ''} - - - - -
Subtotal / ${tr['Subtotal']}${formatCurrency(subtotal)}
${data.vat < 0 ? 'Discount' : 'VAT'} / ${tr['VAT']}${data.vat < 0 ? '-' : '+'}${formatCurrency(Math.abs(data.vat))}
TOTAL / ${tr['TOTAL']}${formatCurrency(data.total)}
-
- -
- - - - - - - `; - - win.document.write(html); - win.document.close(); - } - - // Initialize logic - const urlParams = new URLSearchParams(window.location.search); - if (!urlParams.has('order_type')) { - const otTakeaway = document.getElementById('ot-takeaway'); - if (otTakeaway) { - otTakeaway.checked = true; - } - } - checkOrderType(); - - // --- Add Customer Logic --- - const addCustomerModalEl = document.getElementById('addCustomerModal'); - const saveCustomerBtn = document.getElementById('save-new-customer'); - const newCustomerName = document.getElementById('new-customer-name'); - const newCustomerPhone = document.getElementById('new-customer-phone'); - - if (saveCustomerBtn && newCustomerName && newCustomerPhone) { - saveCustomerBtn.addEventListener('click', () => { - const name = newCustomerName.value.trim(); - const phone = newCustomerPhone.value.trim(); - - if (name === '') { - showToast('Name is required', 'warning'); - return; - } - - saveCustomerBtn.disabled = true; - saveCustomerBtn.textContent = 'Saving...'; - - fetch('api/create_customer.php', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: name, phone: phone }) - }) - .then(res => res.json()) - .then(data => { - saveCustomerBtn.disabled = false; - saveCustomerBtn.textContent = 'Save Customer'; - - if (data.success) { - if (addCustomerModalEl) { - const modal = bootstrap.Modal.getInstance(addCustomerModalEl); - if (modal) modal.hide(); - } - selectCustomer(data.customer); - showToast('Customer created successfully', 'success'); - } else { - showToast(data.error || 'Error creating customer', 'danger'); - } - }) - .catch(err => { - saveCustomerBtn.disabled = false; - saveCustomerBtn.textContent = 'Save Customer'; - showToast('Network error', 'danger'); - }); - }); - } -}); +}); \ No newline at end of file diff --git a/pos.php b/pos.php index be6cc9a..e9dc924 100644 --- a/pos.php +++ b/pos.php @@ -85,21 +85,47 @@ if (!$loyalty_settings) { -
-