From 9999efc72b70fdfed24a17cec00d4881550b666f Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 25 Feb 2026 02:42:12 +0000 Subject: [PATCH] updating session and pos --- admin/includes/header.php | 13 +-- assets/js/main.js | 168 ++++++++++++++++++++++++++++++++++++-- includes/functions.php | 23 ++++++ pos.php | 31 +++---- 4 files changed, 204 insertions(+), 31 deletions(-) diff --git a/admin/includes/header.php b/admin/includes/header.php index 989a2d6..40d42f0 100644 --- a/admin/includes/header.php +++ b/admin/includes/header.php @@ -1,8 +1,4 @@ - + \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js index 3c548aa..e24e61f 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -99,7 +99,7 @@ document.addEventListener('DOMContentLoaded', () => { function formatCurrency(amount) { const symbol = settings.currency_symbol || '$'; const decimals = parseInt(settings.currency_decimals || 2); - return symbol + parseFloat(amount).toFixed(decimals); + return symbol + parseFloat(Math.abs(amount)).toFixed(decimals); } function filterProducts() { @@ -397,11 +397,11 @@ document.addEventListener('DOMContentLoaded', () => { if (data.success && data.tables.length > 0) { renderTables(data.tables); } else { - grid.innerHTML = `
${_t("none")}
`; + grid.innerHTML = `
${_t("none")}
`; } }) .catch(() => { - grid.innerHTML = `
${_t("error")}
`; + grid.innerHTML = `
${_t("error")}
`; }); } @@ -420,7 +420,7 @@ document.addEventListener('DOMContentLoaded', () => { for (const area in areas) { const areaHeader = document.createElement("div"); areaHeader.className = "col-12 mt-3"; - areaHeader.innerHTML = `
${area}
`; + areaHeader.innerHTML = `
${area}
`; grid.appendChild(areaHeader); areas[area].forEach(table => { @@ -429,7 +429,7 @@ document.addEventListener('DOMContentLoaded', () => { const statusClass = table.is_occupied ? "btn-outline-danger" : "btn-outline-success"; col.innerHTML = ` @@ -634,10 +634,36 @@ document.addEventListener('DOMContentLoaded', () => { 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, + items: cart.map(item => ({ + name: item.name, + variant_name: item.variant_name, + quantity: item.quantity, + price: item.price + })), + total: subtotal + vat, + vat: vat, + orderType: orderType, + tableNumber: (orderType === 'dine-in') ? currentTableName : null, + date: new Date().toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }), + paymentMethod: paymentTypeName || 'Unpaid', + loyaltyRedeemed: isLoyaltyRedemption + }; + fetch('api/order.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) }) .then(res => res.json()) .then(data => { if (data.success) { + receiptData.orderId = data.order_id; + + // Show receipt for Quick Pay (where paymentTypeId is provided) + if (paymentTypeId !== null) { + printThermalReceipt(receiptData); + } + showToast(`${_t('order_placed')} #${data.order_id}`, 'success'); clearCart(); if (paymentSelectionModal) paymentSelectionModal.hide(); @@ -646,6 +672,136 @@ document.addEventListener('DOMContentLoaded', () => { }); }; + window.printThermalReceipt = function(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 thermal 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.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 && data.orderType === 'dine-in' ? ` +
+ 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 logoHtml = settings.logo_url ? `` : ''; + + const html = ` + + + Receipt #${data.orderId} + + + +
+ ${logoHtml} +

${settings.company_name}

+
${CURRENT_OUTLET.name}
+
${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: ${CURRENT_USER.name}${tr['Staff']}: ${CURRENT_USER.name}
+
+ ${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(); + }; + window.openRatingQRModal = function() { const qrContainer = document.getElementById('rating-qr-container'); const ratingUrl = BASE_URL + '/rate.php'; @@ -656,4 +812,4 @@ document.addEventListener('DOMContentLoaded', () => { const modal = new bootstrap.Modal(document.getElementById('qrRatingModal')); modal.show(); }; -}); +}); \ No newline at end of file diff --git a/includes/functions.php b/includes/functions.php index 074f1f8..cfbdfb2 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -228,8 +228,31 @@ function render_pagination_controls($pagination, $extra_params = []) { function init_session() { if (session_status() === PHP_SESSION_NONE) { + // Set session lifetime to 1 week (604800 seconds) + $lifetime = 604800; + + // Ensure gc_maxlifetime is at least as long as cookie lifetime + ini_set('session.gc_maxlifetime', (string)$lifetime); + + // Set cookie parameters before session_start + $isSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443; + + session_set_cookie_params([ + 'lifetime' => $lifetime, + 'path' => '/', + 'domain' => '', + 'secure' => $isSecure, + 'httponly' => true, + 'samesite' => 'Lax' + ]); + session_start(); } + + // Refresh session expiration on each load if user is logged in + if (isset($_SESSION['user'])) { + // Optional: you could implement a last_activity check here + } } function login_user($username, $password) { diff --git a/pos.php b/pos.php index c77acdd..96fefa8 100644 --- a/pos.php +++ b/pos.php @@ -4,9 +4,7 @@ require_once __DIR__ . '/db/config.php'; require_once __DIR__ . '/includes/functions.php'; require_once __DIR__ . '/includes/lang.php'; -if (session_status() === PHP_SESSION_NONE) { - session_start(); -} +init_session(); // User requested no translations in all app except for QR order and rating. // Force English for POS. @@ -102,7 +100,7 @@ if (!$loyalty_settings) { .pos-products { background: #f8fafc; height: 100%; display: flex; flex-direction: column; } .pos-cart { background: #fff; height: 100%; border-left: 1px solid #e0e0e0; display: flex; flex-direction: column; } - .product-card { transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); cursor: pointer; border: 1px solid transparent !important; background: #fff; } + .product-card { transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); cursor: pointer; border: 1px solid transparent !important; background: #fff; aspect-ratio: 1/1; display: flex; flex-direction: column; } .product-card:active { transform: scale(0.95); } .product-card:hover { border-color: #0d6efd !important; box-shadow: 0 4px 12px rgba(0,0,0,0.08) !important; } @@ -112,19 +110,13 @@ if (!$loyalty_settings) { .search-dropdown { position: absolute; width: 100%; z-index: 1000; max-height: 200px; overflow-y: auto; display: none; } /* Compact Card adjustments */ - .card-img-container { height: 75px; position: relative; background: #f1f5f9; } + .card-img-container { flex: 1; position: relative; background: #f1f5f9; overflow: hidden; } .card-img-container img { height: 100%; width: 100%; object-fit: cover; transition: transform 0.3s; } - .product-title { font-size: 0.75rem; line-height: 1.2; height: 1.8rem; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; font-weight: 700; color: #1e293b; } - .product-price-tag { font-size: 0.85rem; color: #0d6efd; font-weight: 700; } + .product-title { font-size: 0.75rem; line-height: 1.1; height: 2.2rem; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; font-weight: 700; color: #1e293b; } + .product-price-tag { font-size: 0.8rem; color: #0d6efd; font-weight: 700; } .product-cat-name { font-size: 0.65rem; color: #94a3b8; } - /* Custom Grid for 7 columns */ - @media (min-width: 1200px) { - .row-cols-xl-7 > *, .row-cols-xxl-7 > * { flex: 0 0 auto; width: 14.285714%; } - } - @media (max-width: 576px) { - .card-img-container { height: 65px; } .pos-categories { width: 65px !important; flex: 0 0 65px !important; } .category-btn span { display: none; } .category-btn i { font-size: 1.4rem !important; margin-bottom: 0 !important; } @@ -169,7 +161,6 @@ if (!$loyalty_settings) { Logo -
@@ -200,7 +191,7 @@ if (!$loyalty_settings) {
-
+