38929-vm/pos.php
Flatlogic Bot 57a6e7ed99 modify pos
2026-03-04 03:40:50 +00:00

827 lines
38 KiB
PHP

<?php
$title = 'pos';
require_once __DIR__ . '/includes/header.php';
// Global check in header.php handles 'view' permission.
$edit_order_id = $_GET['edit_order_id'] ?? null;
if ($edit_order_id && !has_permission('edit')) {
header('Location: pos.php?error=no_edit_permission');
exit;
}
$branch_id = $_SESSION['branch_id'] ?? null;
if ($branch_id === 'all') $branch_id = null;
// Loyalty Settings
$loyalty_enabled = get_setting('loyalty_enabled', '0');
$loyalty_points_per_currency = get_setting('loyalty_points_per_currency', '1');
$loyalty_currency_per_point = get_setting('loyalty_currency_per_point', '0.05');
// Handle Edit Order
$edit_order = null;
$edit_items = [];
if ($edit_order_id) {
$stmt = db()->prepare("SELECT * FROM orders WHERE id = ?");
if ($branch_id) {
$stmt = db()->prepare("SELECT * FROM orders WHERE id = ? AND branch_id = ?");
$stmt->execute([$edit_order_id, $branch_id]);
} else {
$stmt->execute([$edit_order_id]);
}
$edit_order = $stmt->fetch();
if ($edit_order) {
$stmt = db()->prepare("SELECT oi.*, i.name_en, i.name_ar, i.vat_percent, s.name_en as service_en, s.name_ar as service_ar
FROM order_items oi
JOIN items i ON oi.item_id = i.id
JOIN services s ON oi.service_id = s.id
WHERE oi.order_id = ?");
$stmt->execute([$edit_order_id]);
$edit_items_raw = $stmt->fetchAll();
foreach ($edit_items_raw as $ei) {
$edit_items[] = [
'item_id' => $ei['item_id'],
'service_id' => $ei['service_id'],
'name' => $lang === 'en' ? $ei['name_en'] : $ei['name_ar'],
'service_name' => $lang === 'en' ? $ei['service_en'] : $ei['service_ar'],
'price' => (float)$ei['unit_price'],
'qty' => (int)$ei['quantity'],
'vat_percent' => (float)$ei['vat_percent']
];
}
}
}
// Get all categories
$categories = db()->query("SELECT * FROM categories WHERE is_deleted = 0 ORDER BY name_en ASC")->fetchAll();
// Get all items with details
$stmt = db()->prepare("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
FROM items i
LEFT JOIN categories c ON i.category_id = c.id
WHERE i.is_deleted = 0
ORDER BY i.name_en ASC");
$stmt->execute();
$items_raw = $stmt->fetchAll();
// Get all services
$services_raw = db()->query("SELECT * FROM services WHERE is_deleted = 0 ORDER BY name_en ASC")->fetchAll();
// Get all prices (globally shared)
$stmt = db()->prepare("SELECT * FROM prices");
$stmt->execute();
$prices_raw = $stmt->fetchAll();
$prices = [];
foreach ($prices_raw as $p) {
$prices[$p['item_id']][$p['service_id']] = (float)$p['price'];
}
$items = [];
foreach ($items_raw as $i) {
$item_services = [];
foreach ($services_raw as $s) {
if (isset($prices[$i['id']][$s['id']])) {
$item_services[] = [
'id' => $s['id'],
'name_en' => $s['name_en'],
'name_ar' => $s['name_ar'],
'price' => $prices[$i['id']][$s['id']]
];
}
}
if (!empty($item_services)) {
$items[$i['id']] = [
'id' => $i['id'],
'name_en' => $i['name_en'],
'name_ar' => $i['name_ar'],
'category_id' => $i['category_id'],
'image_url' => $i['image_url'],
'vat_percent' => (float)($i['vat_percent'] ?? 15),
'services' => $item_services
];
}
}
// Get all customers
$stmt = db()->prepare("SELECT id, name_en, name_ar, phone, loyalty_points FROM customers ORDER BY name_en ASC");
$stmt->execute();
$customers = $stmt->fetchAll();
$pageTitle = $edit_order ? ($lang == 'en' ? 'Edit Order #' . $edit_order['order_number'] : 'تعديل طلب رقم ' . $edit_order['order_number']) : ($lang == 'en' ? 'Point of Sale' : 'نقطة البيع');
?>
<div class="container-fluid py-4">
<div class="row g-4">
<!-- Left: Items & Categories -->
<div class="col-lg-8">
<div class="mb-4 d-flex align-items-center justify-content-between">
<h4 class="fw-bold mb-0"><?= $pageTitle ?></h4>
<div class="input-group w-50 shadow-sm rounded-4 overflow-hidden">
<span class="input-group-text bg-white border-0"><i class="bi bi-search"></i></span>
<input type="text" id="itemSearch" class="form-control border-0 py-2 shadow-none" placeholder="<?= $lang == 'en' ? 'Search items...' : 'بحث عن المنتجات...' ?>">
</div>
</div>
<!-- Categories Scroller -->
<div class="mb-4 overflow-auto d-flex pb-2 hide-scrollbar" style="white-space: nowrap;">
<button class="btn btn-primary rounded-pill px-4 cat-filter me-2 shadow-sm" data-cat="all">
<?= $lang == 'en' ? 'All' : 'الكل' ?>
</button>
<?php foreach($categories as $cat): ?>
<button class="btn btn-white border rounded-pill px-4 cat-filter me-2 shadow-sm" data-cat="<?= $cat['id'] ?>">
<?= $lang == 'en' ? $cat['name_en'] : $cat['name_ar'] ?>
</button>
<?php endforeach; ?>
</div>
<div class="row g-3" id="itemsList">
<?php foreach($items as $item): ?>
<div class="col-6 col-md-4 col-xl-3 item-card-wrapper"
data-cat="<?= $item['category_id'] ?>"
data-name-en="<?= strtolower($item['name_en']) ?>"
data-name-ar="<?= $item['name_ar'] ?>">
<div class="card h-100 border-0 shadow-sm rounded-4 item-card pointer overflow-hidden transition-all" onclick="showOptions(<?= $item['id'] ?>)">
<div class="position-relative bg-light" style="height: 160px; border-radius: 1rem 1rem 0 0; overflow: hidden;">
<?php if($item['image_url']): ?>
<img src="<?= $item['image_url'] ?>?v=<?= time() ?>" class="w-100 h-100" style="object-fit: contain; padding: 10px;">
<?php else: ?>
<div class="w-100 h-100 d-flex align-items-center justify-content-center opacity-25">
<i class="bi bi-box" style="font-size: 3rem;"></i>
</div>
<?php endif; ?>
<div class="position-absolute top-0 end-0 m-2">
<span class="badge bg-white text-dark shadow-sm rounded-pill px-2 py-1 small" style="font-size: 0.65rem;">
<?= $item['vat_percent'] ?>% VAT
</span>
</div>
</div>
<div class="card-body p-3 text-center">
<h6 class="card-title mb-1 text-truncate fw-bold small"><?= $lang == 'en' ? $item['name_en'] : $item['name_ar'] ?></h6>
<p class="small text-muted mb-0" style="font-size: 0.75rem;"><?= count($item['services']) ?> <?= $lang == 'en' ? 'Services' : 'خدمات' ?></p>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Right: Cart -->
<div class="col-lg-4">
<div class="card border-0 shadow-sm rounded-4 h-100 d-flex flex-column" style="min-height: 80vh;">
<div class="card-header bg-white py-3 border-0">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-bold"><?= $lang == 'en' ? 'Current Order' : 'الطلب الحالي' ?></h5>
<button class="btn btn-sm btn-outline-danger rounded-3 border-0" onclick="clearCart()">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
<div class="px-3 pb-3">
<div class="d-flex gap-2 mb-2">
<!-- Persistent Customer Search -->
<div class="position-relative flex-grow-1" id="customerSearchWrapper">
<div class="input-group shadow-sm rounded-3 overflow-hidden border">
<span class="input-group-text bg-white border-0"><i class="bi bi-person"></i></span>
<input type="text" class="form-control border-0 py-2 shadow-none" id="customerSearchInput" placeholder="<?= $lang == 'en' ? 'Walk-in Customer' : 'عميل عابر' ?>" autocomplete="off">
<button class="btn btn-white border-0 d-none" type="button" id="clearCustomerBtn">
<i class="bi bi-x-circle-fill text-muted"></i>
</button>
</div>
<div class="position-absolute w-100 shadow-lg bg-white rounded-3 mt-1 d-none" id="customerResults" style="z-index: 1050; max-height: 300px; overflow-y: auto; border: 1px solid #eee;">
<div class="p-2 border-bottom">
<button class="btn btn-light btn-sm w-100 text-start rounded-2 customer-result-item" data-id="" data-name="<?= $lang == 'en' ? 'Walk-in Customer' : 'عميل عابر' ?>" data-search="walk-in" data-points="0">
<?= $lang == 'en' ? 'Walk-in Customer' : 'عميل عابر' ?>
</button>
</div>
<div id="customerResultsList">
<?php foreach($customers as $c):
$cname = ($lang == 'en' ? $c['name_en'] : $c['name_ar']) ?: $c['name_en'];
$searchStr = strtolower($c['name_en'] . ' ' . ($c['name_ar'] ?? '') . ' ' . $c['phone']);
?>
<div class="p-1">
<button class="btn btn-white btn-sm w-100 text-start rounded-2 customer-result-item p-2" type="button" data-id="<?= $c['id'] ?>" data-name="<?= $cname ?>" data-phone="<?= $c['phone'] ?>" data-search="<?= $searchStr ?>" data-points="<?= $c['loyalty_points'] ?>">
<div class="fw-bold small text-dark"><?= $cname ?></div>
<div class="text-muted small" style="font-size: 0.7rem;"><?= $c['phone'] ?></div>
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<input type="hidden" id="customerId" value="<?= $edit_order ? $edit_order['customer_id'] : '' ?>">
</div>
<?php if (has_permission('add', 'customers.php')): ?>
<button class="btn btn-primary rounded-3" data-bs-toggle="modal" data-bs-target="#addCustomerModal">
<i class="bi bi-plus-lg"></i>
</button>
<?php endif; ?>
</div>
<!-- Loyalty Info Display -->
<div id="loyaltyDisplay" class="d-none bg-primary bg-opacity-10 rounded-3 p-2 mb-2 d-flex align-items-center justify-content-between border border-primary border-opacity-25">
<div class="d-flex align-items-center">
<i class="bi bi-star-fill text-primary me-2"></i>
<span class="small fw-bold text-primary"><?= $lang == 'en' ? 'Loyalty Points' : 'نقاط الولاء' ?></span>
</div>
<span class="badge bg-primary rounded-pill" id="customerPoints">0</span>
</div>
</div>
<div class="card-body p-0 flex-grow-1 overflow-auto" id="cartContainer">
<div id="cartItems" class="p-3"></div>
<div id="emptyCart" class="text-center py-5 opacity-50">
<i class="bi bi-cart3 display-1 d-block mb-3"></i>
<p><?= $lang == 'en' ? 'Your cart is empty' : 'السلة فارغة' ?></p>
</div>
</div>
<div class="card-footer bg-white border-top-0 p-3">
<div class="bg-light rounded-4 p-3 mb-3">
<div class="d-flex justify-content-between mb-2 small text-muted">
<span><?= $lang == 'en' ? 'Subtotal' : 'المجموع الفرعي' ?></span>
<span id="cartSubtotal">0.000 <?= currency() ?></span>
</div>
<div class="d-flex justify-content-between mb-2 small text-muted">
<span><?= $lang == 'en' ? 'VAT' : 'الضريبة' ?></span>
<span id="cartVat">0.000 <?= currency() ?></span>
</div>
<!-- Loyalty Discount Display -->
<div id="loyaltyDiscountRow" class="d-none d-flex justify-content-between mb-2 small text-success fw-bold">
<span><?= $lang == 'en' ? 'Loyalty Discount' : 'خصم الولاء' ?></span>
<span id="cartLoyaltyDiscount">-0.000 <?= currency() ?></span>
</div>
<div class="d-flex justify-content-between pt-2 border-top border-secondary border-opacity-10 mt-2">
<h5 class="mb-0 fw-bold"><?= $lang == 'en' ? 'Total' : 'الإجمالي' ?></h5>
<h5 id="cartTotal" class="mb-0 fw-bold text-primary">0.000 <?= currency() ?></h5>
</div>
</div>
<!-- Loyalty Redemption UI -->
<?php if ($loyalty_enabled == '1'): ?>
<div id="loyaltyRedeemUI" class="d-none mb-3">
<div class="input-group shadow-sm rounded-3 overflow-hidden border">
<span class="input-group-text bg-white border-0 small"><i class="bi bi-star text-primary"></i></span>
<input type="number" id="pointsToRedeem" class="form-control border-0 py-2 shadow-none small" placeholder="<?= $lang == 'en' ? 'Redeem Points' : 'استبدال النقاط' ?>" min="0">
<button class="btn btn-primary btn-sm px-3" onclick="applyPoints()">
<?= $lang == 'en' ? 'Apply' : 'تطبيق' ?>
</button>
</div>
<div id="redeemHint" class="small text-muted mt-1" style="font-size: 0.7rem;"></div>
</div>
<?php endif; ?>
<?php if (($edit_order && has_permission('edit')) || (!$edit_order && has_permission('add'))): ?>
<button class="btn btn-primary w-100 py-3 rounded-4 fw-bold shadow-sm" onclick="checkout()">
<i class="bi bi-credit-card me-2"></i> <?= $edit_order ? ($lang == 'en' ? 'Update Order' : 'تحديث الطلب') : ($lang == 'en' ? 'Complete Order' : 'إتمام الطلب') ?>
</button>
<?php else: ?>
<button class="btn btn-secondary w-100 py-3 rounded-4 fw-bold shadow-sm" disabled>
<i class="bi bi-lock me-2"></i> <?= $lang == 'en' ? 'No Permission' : 'لا تملك صلاحية' ?>
</button>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<!-- Selection Modal -->
<div class="modal fade" id="selectionModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 rounded-4 shadow-lg">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold" id="selectionItemName"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<p class="text-muted small mb-3"><?= $lang == 'en' ? 'Select service type:' : 'اختر نوع الخدمة:' ?></p>
<div class="row g-2" id="optionsList"></div>
</div>
</div>
</div>
</div>
<!-- Add Customer Modal -->
<div class="modal fade" id="addCustomerModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 rounded-4 shadow-lg">
<div class="modal-header border-0 pb-0 p-4">
<h5 class="modal-title fw-bold"><?= $lang == 'en' ? 'Add New Customer' : 'إضافة عميل جديد' ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-4">
<form id="addCustomerForm">
<div class="mb-3">
<label class="form-label small"><?= $lang == 'en' ? 'Name (English)' : 'الاسم (إنجليزي)' ?></label>
<input type="text" name="name_en" class="form-control rounded-3" required>
</div>
<div class="mb-3">
<label class="form-label small"><?= $lang == 'en' ? 'Name (Arabic)' : 'الاسم (عربي)' ?></label>
<input type="text" name="name_ar" class="form-control rounded-3">
</div>
<div class="mb-3">
<label class="form-label small"><?= $lang == 'en' ? 'Phone Number' : 'رقم الجوال' ?></label>
<input type="tel" name="phone" class="form-control rounded-3" required>
</div>
<div class="mb-3">
<label class="form-label small"><?= $lang == 'en' ? 'Email (Optional)' : 'البريد (اختياري)' ?></label>
<input type="email" name="email" class="form-control rounded-3">
</div>
<button type="button" class="btn btn-primary w-100 py-2 rounded-3" onclick="saveCustomer()">
<?= $lang == 'en' ? 'Save Customer' : 'حفظ العميل' ?>
</button>
</form>
</div>
</div>
</div>
</div>
<!-- Payment Modal -->
<div class="modal fade" id="paymentModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 rounded-4 shadow-lg">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold"><?= $lang == 'en' ? 'Payment' : 'الدفع' ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<div class="text-center mb-4">
<h3 class="fw-bold text-primary mb-1" id="paymentTotalAmount">0.000</h3>
<div class="text-muted small"><?= $lang == 'en' ? 'Total Amount' : 'المبلغ الإجمالي' ?></div>
</div>
<div class="row g-3">
<div class="col-6">
<button class="btn btn-outline-primary w-100 py-3 rounded-3 d-flex flex-column align-items-center" onclick="completeCheckout('cash')">
<i class="bi bi-cash-stack mb-1 fs-4"></i>
<span class="small fw-bold"><?= $lang == 'en' ? 'Cash' : 'نقداً' ?></span>
</button>
</div>
<div class="col-6">
<button class="btn btn-outline-primary w-100 py-3 rounded-3 d-flex flex-column align-items-center" onclick="completeCheckout('card')">
<i class="bi bi-credit-card mb-1 fs-4"></i>
<span class="small fw-bold"><?= $lang == 'en' ? 'Card' : 'بطاقة' ?></span>
</button>
</div>
<div class="col-6">
<button class="btn btn-outline-primary w-100 py-3 rounded-3 d-flex flex-column align-items-center" onclick="completeCheckout('transfer')">
<i class="bi bi-arrow-left-right mb-1 fs-4"></i>
<span class="small fw-bold"><?= $lang == 'en' ? 'Transfer' : 'تحويل' ?></span>
</button>
</div>
<div class="col-6">
<button class="btn btn-outline-secondary w-100 py-3 rounded-3 d-flex flex-column align-items-center" onclick="completeCheckout('pay_later')">
<i class="bi bi-clock-history mb-1 fs-4"></i>
<span class="small fw-bold"><?= $lang == 'en' ? 'Pay Later' : 'الدفع لاحقاً' ?></span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Hidden Iframe for Printing -->
<script>
const itemsData = <?= json_encode((object)$items) ?>;
const lang = '<?= $lang ?>';
const currencyLabel = '<?= currency() ?>';
const decimalPrecision = <?= decimals() ?>;
const editOrderId = <?= $edit_order_id ?: 'null' ?>;
const loyaltyEnabled = <?= $loyalty_enabled ?>;
const pointsPerCurrency = <?= $loyalty_points_per_currency ?>;
const currencyPerPoint = <?= $loyalty_currency_per_point ?>;
let cart = <?= json_encode($edit_items) ?>;
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 = "<?= $edit_order['customer_id'] ?>";
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'); }
}
</script>
<style>
.pointer { cursor: pointer; }
.item-card:hover { transform: translateY(-5px); }
.transition-all { transition: all 0.3s ease; }
.hide-scrollbar::-webkit-scrollbar { display: none; }
.hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.btn-white { background: white; }
.customer-result-item:hover { background-color: #f8f9fa; }
</style>
<?php require_once __DIR__ . '/includes/footer.php'; ?>