421 lines
16 KiB
JavaScript
421 lines
16 KiB
JavaScript
const itemsData = 1;
|
|
const lang = '1';
|
|
const currencyLabel = '1';
|
|
const decimalPrecision = 1;
|
|
const editOrderId = 1;
|
|
const loyaltyEnabled = 1;
|
|
const pointsPerCurrency = 1;
|
|
const currencyPerPoint = 1;
|
|
|
|
let cart = 1;
|
|
let selectionModal;
|
|
let paymentModal;
|
|
let customerLoyaltyPoints = 0;
|
|
let pointsToRedeem = 0;
|
|
|
|
// If not editing, try to load from local storage
|
|
if (!editOrderId) {
|
|
try {
|
|
const saved = localStorage.getItem('pos_cart');
|
|
if (saved) {
|
|
const parsed = JSON.parse(saved);
|
|
if (Array.isArray(parsed) && parsed.length > 0) cart = parsed;
|
|
}
|
|
} catch (e) { console.error('Cart parse error', e); }
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
if (typeof bootstrap !== 'undefined') {
|
|
const modalEl = document.getElementById('selectionModal');
|
|
if (modalEl) selectionModal = new bootstrap.Modal(modalEl);
|
|
|
|
const payModalEl = document.getElementById('paymentModal');
|
|
if (payModalEl) paymentModal = new bootstrap.Modal(payModalEl);
|
|
}
|
|
|
|
// Category filtering
|
|
document.querySelectorAll('.cat-filter').forEach(btn => {
|
|
btn.onclick = () => {
|
|
const cat = btn.getAttribute('data-cat');
|
|
document.querySelectorAll('.cat-filter').forEach(b => b.classList.remove('btn-primary'));
|
|
document.querySelectorAll('.cat-filter').forEach(b => b.classList.add('btn-white', 'border'));
|
|
btn.classList.add('btn-primary');
|
|
btn.classList.remove('btn-white', 'border');
|
|
|
|
document.querySelectorAll('.item-card-wrapper').forEach(card => {
|
|
if (cat === 'all' || card.getAttribute('data-cat') === cat) card.style.display = 'block';
|
|
else card.style.display = 'none';
|
|
});
|
|
};
|
|
});
|
|
|
|
// Search
|
|
const searchInput = document.getElementById('itemSearch');
|
|
if (searchInput) {
|
|
searchInput.addEventListener('input', function(e) {
|
|
const q = e.target.value.toLowerCase();
|
|
document.querySelectorAll('.item-card-wrapper').forEach(card => {
|
|
const en = card.getAttribute('data-name-en') || '';
|
|
const ar = card.getAttribute('data-name-ar') || '';
|
|
if (en.includes(q) || ar.includes(q)) card.style.display = 'block';
|
|
else card.style.display = 'none';
|
|
});
|
|
});
|
|
}
|
|
|
|
// Persistent Customer Search
|
|
const custSearchInput = document.getElementById('customerSearchInput');
|
|
const custResults = document.getElementById('customerResults');
|
|
const clearCustBtn = document.getElementById('clearCustomerBtn');
|
|
const custIdInput = document.getElementById('customerId');
|
|
|
|
// Handle Edit Customer Pre-fill
|
|
<?php if ($edit_order && $edit_order['customer_id']): ?>
|
|
const initialCustId = "1";
|
|
const initialCustBtn = document.querySelector(`.customer-result-item[data-id="${initialCustId}"]`);
|
|
if (initialCustBtn) {
|
|
selectCustomer(initialCustId, initialCustBtn.getAttribute('data-name'), initialCustBtn.getAttribute('data-points'));
|
|
}
|
|
<?php endif; ?>
|
|
|
|
if (custSearchInput) {
|
|
custSearchInput.addEventListener('focus', () => {
|
|
custResults.classList.remove('d-none');
|
|
});
|
|
|
|
custSearchInput.addEventListener('input', function(e) {
|
|
const q = e.target.value.toLowerCase();
|
|
custResults.classList.remove('d-none');
|
|
document.querySelectorAll('.customer-result-item').forEach(item => {
|
|
const search = item.getAttribute('data-search') || '';
|
|
if (search.includes(q)) {
|
|
item.parentElement.style.display = 'block';
|
|
} else {
|
|
item.parentElement.style.display = 'none';
|
|
}
|
|
});
|
|
});
|
|
|
|
document.addEventListener('click', function(e) {
|
|
if (!document.getElementById('customerSearchWrapper').contains(e.target)) {
|
|
custResults.classList.add('d-none');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (clearCustBtn) {
|
|
clearCustBtn.addEventListener('click', () => {
|
|
resetCustomerSelection();
|
|
});
|
|
}
|
|
|
|
custResults.addEventListener('click', function(e) {
|
|
const btn = e.target.closest('.customer-result-item');
|
|
if (btn) {
|
|
const id = btn.getAttribute('data-id');
|
|
const name = btn.getAttribute('data-name');
|
|
const points = btn.getAttribute('data-points') || 0;
|
|
selectCustomer(id, name, points);
|
|
custResults.classList.add('d-none');
|
|
}
|
|
});
|
|
|
|
updateCart();
|
|
});
|
|
|
|
function selectCustomer(id, name, points) {
|
|
const custIdInput = document.getElementById('customerId');
|
|
const custSearchInput = document.getElementById('customerSearchInput');
|
|
const clearCustBtn = document.getElementById('clearCustomerBtn');
|
|
const loyaltyDisplay = document.getElementById('loyaltyDisplay');
|
|
const loyaltyRedeemUI = document.getElementById('loyaltyRedeemUI');
|
|
const customerPointsEl = document.getElementById('customerPoints');
|
|
const redeemHint = document.getElementById('redeemHint');
|
|
|
|
custIdInput.value = id;
|
|
customerLoyaltyPoints = parseFloat(points);
|
|
|
|
if (id) {
|
|
custSearchInput.value = name;
|
|
clearCustBtn.classList.remove('d-none');
|
|
if (loyaltyEnabled) {
|
|
loyaltyDisplay.classList.remove('d-none');
|
|
loyaltyRedeemUI.classList.remove('d-none');
|
|
customerPointsEl.innerText = customerLoyaltyPoints.toFixed(2);
|
|
if (redeemHint) {
|
|
redeemHint.innerText = lang === 'en'
|
|
? `1 Point = ${currencyPerPoint} ${currencyLabel}`
|
|
: `1 نقطة = ${currencyPerPoint} ${currencyLabel}`;
|
|
}
|
|
}
|
|
} else {
|
|
resetCustomerSelection();
|
|
}
|
|
}
|
|
|
|
function resetCustomerSelection() {
|
|
const custIdInput = document.getElementById('customerId');
|
|
const custSearchInput = document.getElementById('customerSearchInput');
|
|
const clearCustBtn = document.getElementById('clearCustomerBtn');
|
|
const loyaltyDisplay = document.getElementById('loyaltyDisplay');
|
|
const loyaltyRedeemUI = document.getElementById('loyaltyRedeemUI');
|
|
|
|
custIdInput.value = '';
|
|
custSearchInput.value = '';
|
|
custSearchInput.placeholder = lang === 'en' ? 'Walk-in Customer' : 'عميل عابر';
|
|
clearCustBtn.classList.add('d-none');
|
|
loyaltyDisplay.classList.add('d-none');
|
|
loyaltyRedeemUI.classList.add('d-none');
|
|
customerLoyaltyPoints = 0;
|
|
pointsToRedeem = 0;
|
|
const ptsInput = document.getElementById('pointsToRedeem');
|
|
if (ptsInput) ptsInput.value = '';
|
|
updateCart();
|
|
}
|
|
|
|
function applyPoints() {
|
|
const ptsInput = document.getElementById('pointsToRedeem');
|
|
let val = parseFloat(ptsInput.value) || 0;
|
|
if (val > customerLoyaltyPoints) {
|
|
alert(lang === 'en' ? 'Insufficient points' : 'نقاط غير كافية');
|
|
val = customerLoyaltyPoints;
|
|
ptsInput.value = val;
|
|
}
|
|
pointsToRedeem = val;
|
|
updateCart();
|
|
}
|
|
|
|
function showOptions(itemId) {
|
|
const item = itemsData[itemId];
|
|
if (!item) return;
|
|
const itemNameEl = document.getElementById('selectionItemName');
|
|
if (itemNameEl) itemNameEl.innerText = lang === 'en' ? item.name_en : item.name_ar;
|
|
const list = document.getElementById('optionsList');
|
|
if (list) {
|
|
list.innerHTML = '';
|
|
item.services.forEach(s => {
|
|
const col = document.createElement('div');
|
|
col.className = 'col-6';
|
|
col.innerHTML = `
|
|
<button class="btn btn-outline-primary w-100 p-3 rounded-4 border-2 text-center h-100 transition-all" onclick="addToCart(${item.id}, ${s.id})">
|
|
<div class="fw-bold mb-1 small">${lang === 'en' ? s.name_en : s.name_ar}</div>
|
|
<div class="small opacity-75">${s.price.toFixed(decimalPrecision)} ${currencyLabel}</div>
|
|
</button>
|
|
`;
|
|
list.appendChild(col);
|
|
});
|
|
}
|
|
if (selectionModal) selectionModal.show();
|
|
}
|
|
|
|
function addToCart(itemId, serviceId) {
|
|
const item = itemsData[itemId];
|
|
if (!item) return;
|
|
const service = item.services.find(s => s.id === serviceId);
|
|
if (!service) return;
|
|
|
|
const existing = cart.find(i => i.item_id === itemId && i.service_id === serviceId);
|
|
if (existing) {
|
|
existing.qty++;
|
|
} else {
|
|
cart.push({
|
|
item_id: itemId,
|
|
service_id: serviceId,
|
|
name: lang === 'en' ? item.name_en : item.name_ar,
|
|
service_name: lang === 'en' ? service.name_en : service.name_ar,
|
|
price: service.price,
|
|
qty: 1,
|
|
vat_percent: item.vat_percent
|
|
});
|
|
}
|
|
if (selectionModal) selectionModal.hide();
|
|
updateCart();
|
|
}
|
|
|
|
function changeQty(index, delta) {
|
|
cart[index].qty += delta;
|
|
if (cart[index].qty <= 0) cart.splice(index, 1);
|
|
updateCart();
|
|
}
|
|
|
|
function updateCart() {
|
|
if (!editOrderId) {
|
|
localStorage.setItem('pos_cart', JSON.stringify(cart));
|
|
}
|
|
const cartList = document.getElementById('cartItems');
|
|
const emptyCart = document.getElementById('emptyCart');
|
|
if (!cartList || !emptyCart) return;
|
|
|
|
if (cart.length === 0) {
|
|
cartList.innerHTML = '';
|
|
emptyCart.style.display = 'block';
|
|
} else {
|
|
emptyCart.style.display = 'none';
|
|
cartList.innerHTML = cart.map((item, index) => `
|
|
<div class="d-flex align-items-center mb-3 bg-light p-2 rounded-3">
|
|
<div class="flex-grow-1">
|
|
<div class="fw-bold small text-dark">${item.name}</div>
|
|
<div class="text-muted" style="font-size: 0.75rem;">${item.service_name}</div>
|
|
<div class="fw-bold text-primary">${item.price.toFixed(decimalPrecision)} ${currencyLabel}</div>
|
|
</div>
|
|
<div class="d-flex align-items-center bg-white rounded-3 p-1">
|
|
<button class="btn btn-sm p-0 px-2" onclick="changeQty(${index}, -1)"><i class="bi bi-dash"></i></button>
|
|
<span class="mx-2 fw-bold">${item.qty}</span>
|
|
<button class="btn btn-sm p-0 px-2" onclick="changeQty(${index}, 1)"><i class="bi bi-plus"></i></button>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
let subtotal = 0;
|
|
let totalVat = 0;
|
|
|
|
cart.forEach(item => {
|
|
const itemSubtotal = item.price * item.qty;
|
|
subtotal += itemSubtotal;
|
|
totalVat += itemSubtotal * ((item.vat_percent || 15) / 100);
|
|
});
|
|
|
|
const subtotalEl = document.getElementById('cartSubtotal');
|
|
const vatEl = document.getElementById('cartVat');
|
|
const totalEl = document.getElementById('cartTotal');
|
|
const discountRow = document.getElementById('loyaltyDiscountRow');
|
|
const discountEl = document.getElementById('cartLoyaltyDiscount');
|
|
|
|
let totalBeforeDiscount = subtotal + totalVat;
|
|
let loyaltyDiscount = pointsToRedeem * currencyPerPoint;
|
|
|
|
if (loyaltyDiscount > totalBeforeDiscount) {
|
|
loyaltyDiscount = totalBeforeDiscount;
|
|
pointsToRedeem = loyaltyDiscount / currencyPerPoint;
|
|
}
|
|
|
|
if (loyaltyDiscount > 0) {
|
|
discountRow.classList.remove('d-none');
|
|
discountEl.innerText = '-' + loyaltyDiscount.toFixed(decimalPrecision) + ' ' + currencyLabel;
|
|
} else {
|
|
discountRow.classList.add('d-none');
|
|
}
|
|
|
|
const finalTotal = totalBeforeDiscount - loyaltyDiscount;
|
|
|
|
if (subtotalEl) subtotalEl.innerText = subtotal.toFixed(decimalPrecision) + ' ' + currencyLabel;
|
|
if (vatEl) vatEl.innerText = totalVat.toFixed(decimalPrecision) + ' ' + currencyLabel;
|
|
if (totalEl) totalEl.innerText = finalTotal.toFixed(decimalPrecision) + ' ' + currencyLabel;
|
|
}
|
|
|
|
function clearCart() {
|
|
if (confirm(lang === 'en' ? 'Clear cart?' : 'مسح السلة؟')) {
|
|
cart = [];
|
|
pointsToRedeem = 0;
|
|
const ptsInput = document.getElementById('pointsToRedeem');
|
|
if (ptsInput) ptsInput.value = '';
|
|
updateCart();
|
|
}
|
|
}
|
|
|
|
function checkout() {
|
|
if (cart.length === 0) return;
|
|
|
|
let subtotal = 0;
|
|
let totalVat = 0;
|
|
cart.forEach(item => {
|
|
const itemSubtotal = item.price * item.qty;
|
|
subtotal += itemSubtotal;
|
|
totalVat += itemSubtotal * ((item.vat_percent || 15) / 100);
|
|
});
|
|
|
|
let finalTotal = (subtotal + totalVat) - (pointsToRedeem * currencyPerPoint);
|
|
if (finalTotal < 0) finalTotal = 0;
|
|
|
|
document.getElementById('paymentTotalAmount').innerText = finalTotal.toFixed(decimalPrecision) + ' ' + currencyLabel;
|
|
if (paymentModal) paymentModal.show();
|
|
}
|
|
|
|
async function completeCheckout(paymentMethod) {
|
|
const cid = document.getElementById('customerId').value;
|
|
|
|
let subtotal = 0;
|
|
let totalVat = 0;
|
|
const itemsToSubmit = cart.map(item => {
|
|
const itemSubtotal = item.price * item.qty;
|
|
const itemVat = itemSubtotal * ((item.vat_percent || 15) / 100);
|
|
subtotal += itemSubtotal;
|
|
totalVat += itemVat;
|
|
return {
|
|
itemId: item.item_id,
|
|
serviceId: item.service_id,
|
|
variantId: null,
|
|
quantity: item.qty,
|
|
price: item.price,
|
|
vatAmount: itemVat
|
|
};
|
|
});
|
|
|
|
const totalPrice = subtotal + totalVat;
|
|
|
|
try {
|
|
const response = await fetch('api/checkout.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
order_id: editOrderId,
|
|
customer_id: cid,
|
|
items: itemsToSubmit,
|
|
vat_total: totalVat,
|
|
total_price: totalPrice,
|
|
payment_method: paymentMethod,
|
|
points_to_redeem: pointsToRedeem
|
|
})
|
|
});
|
|
const res = await response.json();
|
|
if (res.success) {
|
|
const orderId = res.order_id || editOrderId;
|
|
if (!editOrderId) { cart = []; updateCart(); localStorage.removeItem("pos_cart"); } window.location.href = "receipt.php?id=" + orderId;
|
|
} else alert(res.error);
|
|
} catch (e) { alert('Error'); }
|
|
}
|
|
|
|
|
|
async function saveCustomer() {
|
|
const form = document.getElementById('addCustomerForm');
|
|
if (!form) return;
|
|
const data = new FormData(form);
|
|
try {
|
|
const response = await fetch('api/add_customer.php', { method: 'POST', body: data });
|
|
const res = await response.json();
|
|
if (res.success) {
|
|
const id = res.customer.id;
|
|
const nameEn = res.customer.name_en;
|
|
const nameAr = res.customer.name_ar || nameEn;
|
|
const phone = res.customer.phone;
|
|
const displayName = lang === 'en' ? nameEn : nameAr;
|
|
|
|
selectCustomer(id, displayName, 0);
|
|
|
|
const list = document.getElementById('customerResultsList');
|
|
if (list) {
|
|
const searchStr = `${nameEn} ${nameAr} ${phone}`.toLowerCase();
|
|
const div = document.createElement('div');
|
|
div.className = 'p-1';
|
|
div.innerHTML = `
|
|
<button class="btn btn-white btn-sm w-100 text-start rounded-2 customer-result-item p-2" type="button" data-id="${id}" data-name="${displayName}" data-phone="${phone}" data-search="${searchStr}" data-points="0">
|
|
<div class="fw-bold small text-dark">${displayName}</div>
|
|
<div class="text-muted small" style="font-size: 0.7rem;">${phone}</div>
|
|
</button>
|
|
`;
|
|
list.prepend(div);
|
|
}
|
|
|
|
if (typeof bootstrap !== 'undefined') {
|
|
const modalEl = document.getElementById('addCustomerModal');
|
|
if (modalEl) {
|
|
const modal = bootstrap.Modal.getInstance(modalEl);
|
|
if (modal) modal.hide();
|
|
}
|
|
}
|
|
form.reset();
|
|
} else alert(res.error);
|
|
} catch (e) { alert('Error'); }
|
|
}
|