diff --git a/assets/js/main.js b/assets/js/main.js index cc7eb83..e769b68 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -89,7 +89,7 @@ document.addEventListener('DOMContentLoaded', () => { let currentCustomer = null; const paymentModalEl = document.getElementById('paymentSelectionModal'); - const paymentSelectionModal = paymentModalEl ? new bootstrap.Modal(paymentModalEl) : null; + const paymentSelectionModal = paymentModalEl ? new bootstrap.Modal(paymentSelectionModal) : null; const paymentMethodsContainer = document.getElementById('payment-methods-container'); const productSearchInput = document.getElementById('product-search'); @@ -698,13 +698,12 @@ document.addEventListener('DOMContentLoaded', () => { customer_id: selectedCustomerId ? selectedCustomerId.value : null, outlet_id: CURRENT_OUTLET ? CURRENT_OUTLET.id : 1, payment_type_id: paymentTypeId, - total_amount: isLoyaltyRedemption ? 0 : (subtotal + totalVat), // If loyalty redeemed, total is 0 for this transaction + total_amount: isLoyaltyRedemption ? 0 : (subtotal + totalVat), vat: isLoyaltyRedemption ? 0 : totalVat, items: itemsData, redeem_loyalty: isLoyaltyRedemption }; - // Prepare receipt data before clearing cart const receiptData = { orderId: null, customer: currentCustomer ? { name: currentCustomer.name, phone: currentCustomer.phone, address: currentCustomer.address || '' } : null, @@ -712,7 +711,7 @@ document.addEventListener('DOMContentLoaded', () => { name: item.name, variant_name: item.variant_name, quantity: item.quantity, - price: isLoyaltyRedemption ? 0 : item.price, // If loyalty redeemed, price is 0 for this transaction + price: isLoyaltyRedemption ? 0 : item.price, vat_percent: isLoyaltyRedemption ? 0 : item.vat_percent, vat_amount: isLoyaltyRedemption ? 0 : ((item.price * item.quantity) * (item.vat_percent / 100)) })), @@ -726,6 +725,12 @@ document.addEventListener('DOMContentLoaded', () => { loyaltyRedeemed: isLoyaltyRedemption }; + // Clear UI immediately for responsiveness + const tempOrderId = currentOrderId; + clearCart(); + if (paymentSelectionModal) paymentSelectionModal.hide(); + if (clearCustomerBtn) clearCustomerBtn.click(); + fetch('api/order.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) }) .then(res => res.json()) .then(data => { @@ -734,35 +739,36 @@ document.addEventListener('DOMContentLoaded', () => { // --- PRINTING LOGIC --- const cashierPrinterIp = CURRENT_OUTLET.cashier_printer_ip; - const isLocalCashierIp = cashierPrinterIp && /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/.test(cashierPrinterIp); + const kitchenPrinterIp = CURRENT_OUTLET.kitchen_printer_ip; - // Show browser receipt ONLY IF: - // 1. No cashier printer is configured - // 2. OR the configured cashier printer is a local IP (which we can't reach from cloud) - // This prevents duplicate printing when a valid network printer is used. + const isLocalCashierIp = cashierPrinterIp && /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/.test(cashierPrinterIp); + const isLocalKitchenIp = kitchenPrinterIp && /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/.test(kitchenPrinterIp); + + // 1. Browser Cashier Print (if needed) if (!cashierPrinterIp || isLocalCashierIp) { printThermalReceipt(receiptData); } else { - // Only try network printing if it's NOT a local IP - // (prevents useless 1s timeout for cashier print if we already browser-printed) triggerNetworkPrint(data.order_id, 'cashier'); } - // Kitchen is usually always a network printer, so we always try it - triggerNetworkPrint(data.order_id, 'kitchen'); + // 2. Kitchen Network Print (ONLY if NOT local IP) + // If it's local, server can't reach it anyway, so skip the fetch to api/print.php + if (kitchenPrinterIp && !isLocalKitchenIp) { + triggerNetworkPrint(data.order_id, 'kitchen'); + } showToast(`${_t('order_placed')} #${data.order_id}`, 'success'); - clearCart(); - if (paymentSelectionModal) paymentSelectionModal.hide(); - if (clearCustomerBtn) clearCustomerBtn.click(); - } else showToast(data.error, 'danger'); + } else { + showToast(data.error, 'danger'); + // Optional: should we restore the cart if it failed? + // For now, let's keep it cleared but show the error. + } }); }; window.triggerNetworkPrint = function(orderId, type) { if (!orderId) return; - // Check if printer IP is configured for this outlet const printerIp = (type === 'kitchen') ? CURRENT_OUTLET.kitchen_printer_ip : CURRENT_OUTLET.cashier_printer_ip; if (!printerIp) return; @@ -774,25 +780,16 @@ document.addEventListener('DOMContentLoaded', () => { .then(res => res.json()) .then(data => { if (!data.success) { - // Skip toast for local IPs as we know they are unreachable from cloud const isLocalIp = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/.test(printerIp); if (!isLocalIp) { showToast(`Printer Error (${type}): ${data.error}`, 'warning'); - } else { - console.warn(`Printer Error (${type}) for local IP ${printerIp}: ${data.error}`); } } }) .catch(err => console.error(`Network Print Fetch Error (${type}):`, err)); }; - /** - * Prints a thermal receipt using a hidden iframe for a smoother experience. - * To achieve completely silent printing (Direct Print), run Chrome with: - * chrome.exe --kiosk-printing --kiosk - */ window.printThermalReceipt = function(data) { - // Create or get the hidden iframe let iframe = document.getElementById('print-iframe'); if (!iframe) { iframe = document.createElement('iframe'); @@ -849,7 +846,9 @@ document.addEventListener('DOMContentLoaded', () => { const loyaltyHtml = data.loyaltyRedeemed ? `