check for pos
This commit is contained in:
parent
2772970659
commit
aa6cc744d0
@ -1,4 +1,8 @@
|
||||
console.log('POS Script Loading...');
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('POS Script DOMContentLoaded');
|
||||
|
||||
// SweetAlert2 Toast Mixin
|
||||
const Toast = (typeof Swal !== 'undefined') ? Swal.mixin({
|
||||
toast: true,
|
||||
@ -89,7 +93,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
let currentCustomer = null;
|
||||
|
||||
const paymentModalEl = document.getElementById('paymentSelectionModal');
|
||||
const paymentSelectionModal = paymentModalEl ? new bootstrap.Modal(paymentSelectionModal) : null;
|
||||
const paymentSelectionModal = paymentModalEl ? new bootstrap.Modal(paymentModalEl) : null;
|
||||
const paymentMethodsContainer = document.getElementById('payment-methods-container');
|
||||
|
||||
const productSearchInput = document.getElementById('product-search');
|
||||
@ -103,7 +107,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const symbol = settings.currency_symbol || '$';
|
||||
const decimals = parseInt(settings.currency_decimals || 2);
|
||||
const position = settings.currency_position || 'before';
|
||||
const formatted = parseFloat(Math.abs(amount)).toFixed(decimals);
|
||||
const formatted = parseFloat(Math.abs(amount || 0)).toFixed(decimals);
|
||||
|
||||
if (position === 'after') {
|
||||
return formatted + ' ' + symbol;
|
||||
@ -117,15 +121,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
items.forEach(item => {
|
||||
const matchesCategory = (currentCategory == 'all' || item.dataset.category == currentCategory);
|
||||
const name = (item.dataset.name || '').toLowerCase();
|
||||
const name_ar = (item.dataset.nameAr || '').toLowerCase();
|
||||
const sku = (item.dataset.sku || '').toLowerCase();
|
||||
const matchesSearch = name.includes(currentSearchQuery) || name_ar.includes(currentSearchQuery) || sku.includes(currentSearchQuery);
|
||||
const matchesSearch = name.includes(currentSearchQuery) || sku.includes(currentSearchQuery);
|
||||
|
||||
item.style.display = (matchesCategory && matchesSearch) ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function filterCategory(categoryId, btnElement) { window.filterCategory = filterCategory;
|
||||
function filterCategory(categoryId, btnElement) {
|
||||
currentCategory = categoryId;
|
||||
document.querySelectorAll('.category-btn').forEach(btn => btn.classList.remove('active'));
|
||||
if (btnElement) {
|
||||
@ -135,7 +138,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (btn) btn.classList.add('active');
|
||||
}
|
||||
filterProducts();
|
||||
};
|
||||
}
|
||||
window.filterCategory = filterCategory;
|
||||
|
||||
document.querySelectorAll('.category-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
@ -150,11 +154,28 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function openRecallOrderModal() { window.openRecallOrderModal = openRecallOrderModal;
|
||||
const productGrid = document.getElementById('product-grid');
|
||||
if (productGrid) {
|
||||
productGrid.addEventListener('click', (e) => {
|
||||
const item = e.target.closest('.product-item');
|
||||
if (item) {
|
||||
try {
|
||||
const product = JSON.parse(item.dataset.product);
|
||||
const variants = JSON.parse(item.dataset.variants);
|
||||
handleProductClick(product, variants);
|
||||
} catch (err) {
|
||||
console.error('Error parsing product data:', err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openRecallOrderModal() {
|
||||
if (!recallModal) return;
|
||||
fetchRecallOrders();
|
||||
recallModal.show();
|
||||
};
|
||||
}
|
||||
window.openRecallOrderModal = openRecallOrderModal;
|
||||
|
||||
function fetchRecallOrders() {
|
||||
if (!recallList) return;
|
||||
@ -357,7 +378,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
item.innerHTML = `
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="fw-bold text-capitalize">${h.reason}</div>
|
||||
<span class="badge ${badgeClass}">${h.points_change > 0 ? '+' : ''}${h.points_change}</span>
|
||||
<span class="badge ${badgeClass}">${(h.points_change > 0 ? '+' : '') + h.points_change}</span>
|
||||
</div>
|
||||
<div class="text-muted small">${h.created_at}</div>
|
||||
`;
|
||||
@ -416,11 +437,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function openTableSelectionModal() { window.openTableSelectionModal = openTableSelectionModal;
|
||||
function openTableSelectionModal() {
|
||||
if (!tableSelectionModal) return;
|
||||
fetchTables();
|
||||
tableSelectionModal.show();
|
||||
};
|
||||
}
|
||||
window.openTableSelectionModal = openTableSelectionModal;
|
||||
|
||||
function fetchTables() {
|
||||
const grid = document.getElementById("tables-grid");
|
||||
@ -477,7 +499,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function selectTable(id, name) { window.selectTable = selectTable;
|
||||
function selectTable(id, name) {
|
||||
currentTableId = id;
|
||||
currentTableName = name;
|
||||
const nameDisplay = document.getElementById("selected-table-name");
|
||||
@ -489,9 +511,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
dineInInput.checked = true;
|
||||
checkOrderType();
|
||||
}
|
||||
};
|
||||
}
|
||||
window.selectTable = selectTable;
|
||||
|
||||
function checkOrderType() { window.checkOrderType = checkOrderType;
|
||||
function checkOrderType() {
|
||||
const checked = document.querySelector('input[name="order_type"]:checked');
|
||||
if (!checked) return;
|
||||
const selected = checked.value;
|
||||
@ -501,25 +524,28 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
} else {
|
||||
if (tableDisplay) tableDisplay.style.display = 'none';
|
||||
}
|
||||
};
|
||||
}
|
||||
window.checkOrderType = checkOrderType;
|
||||
|
||||
document.querySelectorAll('input[name="order_type"]').forEach(input => {
|
||||
input.addEventListener('change', checkOrderType);
|
||||
});
|
||||
|
||||
function handleProductClick(product, variants) { window.handleProductClick = handleProductClick;
|
||||
function handleProductClick(product, variants) {
|
||||
console.log('Product Clicked:', product.name);
|
||||
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),
|
||||
price: parseFloat(product.price || 0), base_price: parseFloat(product.price || 0),
|
||||
hasVariants: false, quantity: 1, variant_id: null, variant_name: null,
|
||||
is_loyalty: parseInt(product.is_loyalty) === 1,
|
||||
vat_percent: parseFloat(product.vat_percent || settings.vat_rate || 0)
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
window.handleProductClick = handleProductClick;
|
||||
|
||||
function openVariantModal(product, variants) {
|
||||
if (!variantSelectionModal) return;
|
||||
@ -531,13 +557,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
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 finalPrice = parseFloat(product.price) + parseFloat(v.price_adjustment);
|
||||
const finalPrice = parseFloat(product.price || 0) + parseFloat(v.price_adjustment || 0);
|
||||
const vName = (LANG === 'ar' && v.name_ar) ? v.name_ar : v.name;
|
||||
btn.innerHTML = `<span>${vName}</span><span class="fw-bold">${formatCurrency(finalPrice)}</span>`;
|
||||
btn.onclick = () => {
|
||||
addToCart({
|
||||
id: product.id, name: product.name, name_ar: product.name_ar || "",
|
||||
price: finalPrice, base_price: parseFloat(product.price),
|
||||
price: finalPrice, base_price: parseFloat(product.price || 0),
|
||||
hasVariants: true, quantity: 1, variant_id: v.id, variant_name: v.name,
|
||||
is_loyalty: parseInt(product.is_loyalty) === 1,
|
||||
vat_percent: parseFloat(product.vat_percent || settings.vat_rate || 0)
|
||||
@ -549,37 +575,51 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
variantSelectionModal.show();
|
||||
}
|
||||
|
||||
function addToCart(product) { window.addToCart = addToCart;
|
||||
function addToCart(product) {
|
||||
console.log('Adding to cart:', 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++;
|
||||
console.log('Incremented quantity for:', product.name);
|
||||
} else {
|
||||
cart.push({...product});
|
||||
console.log('Added new item to cart:', product.name);
|
||||
}
|
||||
updateCart();
|
||||
};
|
||||
}
|
||||
window.addToCart = addToCart;
|
||||
|
||||
function changeQuantity(index, delta) { window.changeQuantity = changeQuantity;
|
||||
function changeQuantity(index, delta) {
|
||||
if (cart[index]) {
|
||||
cart[index].quantity += delta;
|
||||
if (cart[index].quantity <= 0) cart.splice(index, 1);
|
||||
updateCart();
|
||||
}
|
||||
};
|
||||
}
|
||||
window.changeQuantity = changeQuantity;
|
||||
|
||||
function removeFromCart(index) { window.removeFromCart = removeFromCart;
|
||||
function removeFromCart(index) {
|
||||
cart.splice(index, 1);
|
||||
updateCart();
|
||||
};
|
||||
}
|
||||
window.removeFromCart = removeFromCart;
|
||||
|
||||
function clearCart() { window.clearCart = clearCart;
|
||||
function clearCart() {
|
||||
if (cart.length === 0) return;
|
||||
cart = [];
|
||||
currentOrderId = null;
|
||||
isLoyaltyRedemption = false;
|
||||
updateCart();
|
||||
showToast("Cart cleared", "success");
|
||||
};
|
||||
}
|
||||
window.clearCart = clearCart;
|
||||
|
||||
function updateCart() {
|
||||
if (!cartItemsContainer) return;
|
||||
console.log('Updating cart UI, item count:', cart.length);
|
||||
if (!cartItemsContainer) {
|
||||
console.error('Cart items container not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
updateLoyaltyUI();
|
||||
|
||||
@ -600,8 +640,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
let subtotal = 0;
|
||||
let totalVat = 0;
|
||||
cart.forEach((item, index) => {
|
||||
const itemTotal = item.price * item.quantity;
|
||||
const itemVat = itemTotal * (item.vat_percent / 100);
|
||||
const itemTotal = (item.price || 0) * (item.quantity || 0);
|
||||
const itemVat = itemTotal * ((item.vat_percent || 0) / 100);
|
||||
subtotal += itemTotal;
|
||||
totalVat += itemVat;
|
||||
|
||||
@ -612,9 +652,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
row.innerHTML = `
|
||||
<div class="flex-grow-1 me-2">
|
||||
<div class="fw-bold text-truncate" style="max-width: 140px;">${itemName}</div>
|
||||
<div class="fw-bold text-truncate" style="max-width: 140px;">${itemName || 'Product'}</div>
|
||||
${otherName ? `<div class="text-muted small" style="font-size: 0.75rem;">${otherName}</div>` : ''}
|
||||
<div class="small text-muted">${formatCurrency(item.price)} ${item.vat_percent > 0 ? `<span class="badge bg-light text-dark border ms-1" style="font-size: 0.6rem;">${item.vat_percent}% VAT</span>` : ''}</div>
|
||||
<div class="small text-muted">${formatCurrency(item.price)} ${((item.vat_percent || 0) > 0 ? `<span class="badge bg-light text-dark border ms-1" style="font-size: 0.6rem;">${item.vat_percent}% VAT</span>` : '')}</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center bg-light rounded px-1">
|
||||
<button class="btn btn-sm text-secondary p-0" style="width: 24px;" onclick="changeQuantity(${index}, -1)"><i class="bi bi-dash"></i></button>
|
||||
@ -670,7 +710,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function processOrder(paymentTypeId, paymentTypeName) { window.processOrder = processOrder;
|
||||
function processOrder(paymentTypeId, paymentTypeName) {
|
||||
const orderTypeInput = document.querySelector('input[name="order_type"]:checked');
|
||||
const orderType = orderTypeInput ? orderTypeInput.value : 'takeaway';
|
||||
|
||||
@ -764,9 +804,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// For now, let's keep it cleared but show the error.
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
window.processOrder = processOrder;
|
||||
|
||||
function triggerNetworkPrint(orderId, type) { window.triggerNetworkPrint = triggerNetworkPrint;
|
||||
function triggerNetworkPrint(orderId, type) {
|
||||
if (!orderId) return;
|
||||
|
||||
const printerIp = (type === 'kitchen') ? CURRENT_OUTLET.kitchen_printer_ip : CURRENT_OUTLET.cashier_printer_ip;
|
||||
@ -787,9 +828,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(`Network Print Fetch Error (${type}):`, err));
|
||||
};
|
||||
}
|
||||
window.triggerNetworkPrint = triggerNetworkPrint;
|
||||
|
||||
function printThermalReceipt(data) { window.printThermalReceipt = printThermalReceipt;
|
||||
function printThermalReceipt(data) {
|
||||
let iframe = document.getElementById('print-iframe');
|
||||
if (!iframe) {
|
||||
iframe = document.createElement('iframe');
|
||||
@ -847,8 +889,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const loyaltyHtml = data.loyaltyRedeemed ? `<div style="color: #d63384; font-weight: bold; margin: 5px 0; text-align: center;">* Loyalty Reward Applied *</div>` : '';
|
||||
|
||||
// We skip logo in receipt for absolute speed unless it's already cached.
|
||||
// If users really want the logo, we can re-enable it.
|
||||
const logoHtml = ''; // settings.logo_url ? `<img src="${BASE_URL}${settings.logo_url}" style="max-height: 80px; max-width: 150px; margin-bottom: 10px;">` : '';
|
||||
const logoHtml = '';
|
||||
|
||||
const vatRate = settings.vat_rate || 0;
|
||||
|
||||
@ -929,9 +970,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// Print immediately without waiting for resources
|
||||
iframe.contentWindow.focus();
|
||||
iframe.contentWindow.print();
|
||||
};
|
||||
}
|
||||
window.printThermalReceipt = printThermalReceipt;
|
||||
|
||||
function openRatingQRModal() { window.openRatingQRModal = openRatingQRModal;
|
||||
function openRatingQRModal() {
|
||||
const qrContainer = document.getElementById('rating-qr-container');
|
||||
const ratingUrl = BASE_URL + '/rate.php';
|
||||
const qrCodeUrl = "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" + encodeURIComponent(ratingUrl);
|
||||
@ -940,5 +982,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('qrRatingModal'));
|
||||
modal.show();
|
||||
};
|
||||
}
|
||||
window.openRatingQRModal = openRatingQRModal;
|
||||
});
|
||||
3
pos.php
3
pos.php
@ -247,7 +247,8 @@ $vat_rate = (float)($settings['vat_rate'] ?? 0);
|
||||
data-category="<?= $product['category_id'] ?>"
|
||||
data-name="<?= htmlspecialchars(strtolower($product['name'])) ?>"
|
||||
data-sku="<?= htmlspecialchars(strtolower($product['sku'] ?? '')) ?>"
|
||||
onclick="handleProductClick(<?= htmlspecialchars(json_encode($product), ENT_QUOTES) ?>, <?= htmlspecialchars(json_encode($variants_by_product[$product['id']] ?? []), ENT_QUOTES) ?>)">
|
||||
data-product='<?= htmlspecialchars(json_encode($product), ENT_QUOTES) ?>'
|
||||
data-variants='<?= htmlspecialchars(json_encode($variants_by_product[$product['id']] ?? []), ENT_QUOTES) ?>'>
|
||||
<div class="card h-100 border-0 shadow-sm product-card rounded-3 overflow-hidden">
|
||||
<div class="card-img-container">
|
||||
<?php if (!empty($product['image_url'])): ?>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user