39728-vm/includes/sale_form.php
2026-04-20 17:20:25 +00:00

753 lines
30 KiB
PHP

<?php
require_once __DIR__ . '/app.php';
$user = require_roles(['owner', 'manager', 'cashier']);
$pageTitle = $saleMode === 'normal' ? tr('إنشاء فاتورة ضريبية', 'Create Tax Invoice') : tr('نقاط البيع', 'POS Sale');
$activeNav = $saleMode === 'normal' ? 'normal' : 'pos';
$error = '';
$catalog = catalog();
$allowedBranches = get_user_branches($user);
try {
$customers = db()->query('SELECT id, name, phone FROM customers ORDER BY name ASC')->fetchAll();
} catch (Throwable $e) {
$customers = [];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$branchCode = trim((string) ($_POST['branch_code'] ?? ''));
$customerId = isset($_POST['customer_id']) && $_POST['customer_id'] !== '' ? (int)$_POST['customer_id'] : null;
$customerName = trim((string) ($_POST['customer_name'] ?? ''));
$paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash'));
$paymentStatus = ($paymentMethod === 'pay_later') ? 'unpaid' : 'paid';
$saleStatus = trim((string) ($_POST['sale_status'] ?? 'completed'));
$notes = trim((string) ($_POST['notes'] ?? ''));
$cartJson = (string) ($_POST['cart_json'] ?? '[]');
$items = json_decode($cartJson, true);
if (!in_array($branchCode, $allowedBranches, true)) {
$error = tr('اختر فرعاً صالحاً لهذه الصلاحية.', 'Choose a valid branch for this role.');
} elseif (!in_array($paymentMethod, ['cash', 'card', 'transfer', 'pay_later'], true)) {
$error = tr('اختر طريقة دفع صحيحة.', 'Choose a valid payment method.');
} elseif ($saleStatus === 'order' && !$customerId) {
$error = tr('يجب اختيار عميل للطلب المسبق.', 'You must select a customer for a pre-order.');
} elseif ($paymentMethod === 'pay_later' && !$customerId) {
$error = tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.');
} elseif (!is_array($items) || $items === []) {
$error = tr('أضف صنفاً واحداً على الأقل إلى الفاتورة.', 'Add at least one item to the invoice.');
} else {
$normalized = [];
$subtotal = 0.0;
$totalVat = 0.0;
$itemCount = 0;
foreach ($items as $item) {
$sku = (string) ($item['sku'] ?? '');
$qty = (int) ($item['qty'] ?? 0);
if (!isset($catalog[$sku]) || $qty < 1) {
continue;
}
$product = $catalog[$sku];
$editedName = trim((string) ($item['name'] ?? ''));
$finalName = $editedName !== ''
? $editedName
: trim((string) ($product['name_' . current_lang()] ?? ''));
if ($finalName === '') {
$finalName = trim((string) ($product['name_ar'] ?? ''));
}
if ($finalName === '') {
$finalName = trim((string) ($product['name_en'] ?? ''));
}
if ($finalName === '') {
$finalName = $sku;
}
$price = isset($item['price']) && is_numeric($item['price'])
? max(0, (float) $item['price'])
: (float) $product['price'];
$lineTotal = $price * $qty;
$vatPercent = (float) ($product['vat'] ?? 0);
// Assuming price is inclusive of VAT:
$itemVat = $lineTotal * ($vatPercent / 100);
$totalVat += $itemVat;
$normalized[] = [
'sku' => $sku,
'name_ar' => $finalName,
'name_en' => $finalName,
'qty' => $qty,
'price' => $price,
'line_total' => $lineTotal,
'vat_percent' => $vatPercent,
'vat_amount' => $itemVat
];
$subtotal += $lineTotal;
$itemCount += $qty;
}
if ($normalized === []) {
$error = tr('الفاتورة غير صالحة بعد التحقق من الأصناف.', 'The invoice is invalid after product validation.');
} else {
$cashierName = current_lang() === 'ar' ? $user['name_ar'] : $user['name_en'];
$saleId = create_sale([
'receipt_no' => receipt_code(),
'sale_mode' => $saleMode,
'branch_code' => $branchCode,
'cashier_username' => $user['username'],
'cashier_name' => $cashierName,
'role_name' => $user['role'],
'customer_id' => $customerId,
'customer_name' => $customerName !== '' ? $customerName : null,
'payment_method' => $paymentMethod,
'payment_status' => $paymentStatus,
'items' => $normalized,
'item_count' => $itemCount,
'subtotal' => $subtotal,
'vat_amount' => $totalVat,
'total_amount' => $subtotal + $totalVat,
'status' => $saleStatus,
'notes' => $notes !== '' ? $notes : null,
]);
set_flash('success', $saleMode === 'normal'
? tr('تم حفظ الفاتورة بنجاح.', 'Invoice saved successfully.')
: tr('تم حفظ عملية POS بنجاح.', 'POS sale saved successfully.'));
redirect_to('sale.php', ['id' => $saleId]);
}
}
}
require __DIR__ . '/header.php';
?>
<style>
.smart-form-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border: 1px solid #edf2f9;
margin-bottom: 2rem;
}
.smart-form-header {
padding: 1.5rem 2rem;
border-bottom: 1px solid #edf2f9;
background-color: #fcfdfd;
border-radius: 12px 12px 0 0;
}
.smart-form-body {
padding: 2rem;
}
.section-title {
font-size: 1.1rem;
font-weight: 600;
color: #2c3e50;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-label {
font-weight: 500;
color: #495057;
margin-bottom: 0.4rem;
}
.custom-input {
border: 1px solid #ced4da;
border-radius: 8px;
padding: 0.6rem 1rem;
font-size: 0.95rem;
transition: all 0.2s ease-in-out;
}
.custom-input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25);
}
.search-wrapper {
position: relative;
max-width: 600px;
margin-bottom: 2rem;
}
.search-icon {
position: absolute;
top: 50%;
left: 1rem;
transform: translateY(-50%);
color: #6c757d;
}
[dir="rtl"] .search-icon {
left: auto;
right: 1rem;
}
.search-input {
padding-left: 2.5rem;
}
[dir="rtl"] .search-input {
padding-left: 1rem;
padding-right: 2.5rem;
}
.item-search-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #fff;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
display: none;
border: 1px solid #edf2f9;
margin-top: 0.5rem;
}
.item-search-dropdown.show { display: block; }
.search-item-row {
padding: 0.75rem 1rem;
cursor: pointer;
border-bottom: 1px solid #edf2f9;
transition: background 0.15s;
}
.search-item-row:hover { background: #f8f9fa; }
.search-item-row:last-child { border-bottom: none; }
.table-modern {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border: 1px solid #edf2f9;
border-radius: 8px;
overflow: hidden;
}
.table-modern th {
background: #f8f9fa;
padding: 1rem;
font-weight: 600;
color: #495057;
border-bottom: 1px solid #edf2f9;
font-size: 0.9rem;
}
.table-modern td {
padding: 1rem;
vertical-align: middle;
border-bottom: 1px solid #edf2f9;
}
.table-modern tr:last-child td {
border-bottom: none;
}
.qty-control {
width: 80px;
text-align: center;
border: 1px solid #ced4da;
border-radius: 6px;
padding: 0.4rem;
}
.btn-remove {
color: #dc3545;
background: rgba(220, 53, 69, 0.1);
border: none;
width: 32px;
height: 32px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.btn-remove:hover {
background: #dc3545;
color: #fff;
}
.totals-box {
background: #f8f9fa;
border-radius: 8px;
padding: 1.5rem;
border: 1px solid #edf2f9;
}
.totals-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.75rem;
color: #495057;
}
.totals-row.grand-total {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #dee2e6;
font-size: 1.25rem;
font-weight: 700;
color: #212529;
margin-bottom: 0;
}
.line-input {
min-width: 0;
}
.line-price-input {
min-width: 96px;
}
</style>
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h3 class="fw-bold mb-0 text-dark"><?= h($pageTitle) ?></h3>
<a href="sales.php" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left"></i> <?= h(tr('عودة للمبيعات', 'Back to Sales')) ?>
</a>
</div>
<?php if ($error !== ''): ?>
<div class="alert alert-danger rounded-3 shadow-sm mb-4"><i class="bi bi-exclamation-triangle-fill me-2"></i><?= h($error) ?></div>
<?php endif; ?>
<form method="post" id="smart-sale-form">
<input type="hidden" name="cart_json" id="cart_json" value="[]">
<div class="row">
<div class="col-lg-8">
<!-- Items Section -->
<div class="smart-form-card">
<div class="smart-form-header">
<div class="section-title mb-0">
<i class="bi bi-cart-plus text-primary"></i> <?= h(tr('عناصر الفاتورة', 'Invoice Items')) ?>
</div>
</div>
<div class="smart-form-body">
<!-- Search Bar -->
<div class="search-wrapper">
<i class="bi bi-search search-icon"></i>
<input type="text" id="itemSearchInput" class="form-control custom-input search-input form-control-lg" placeholder="<?= h(tr('ابحث بالاسم أو الباركود...', 'Search by name or barcode...')) ?>" autocomplete="off">
<div id="itemDropdown" class="item-search-dropdown"></div>
</div>
<!-- Table -->
<div class="table-responsive">
<table class="table-modern" id="invoiceTable">
<thead>
<tr>
<th width="45%"><?= h(tr('المنتج', 'Product')) ?></th>
<th width="15%" class="text-center"><?= h(tr('السعر', 'Price')) ?></th>
<th width="15%" class="text-center"><?= h(tr('الكمية', 'Qty')) ?></th>
<th width="20%" class="text-center"><?= h(tr('الإجمالي', 'Total')) ?></th>
<th width="5%"></th>
</tr>
</thead>
<tbody id="invoiceLines">
<tr id="emptyInvoiceRow">
<td colspan="5" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-1 d-block mb-2 text-light"></i>
<?= h(tr('لم يتم إضافة أي منتجات بعد.', 'No products added yet.')) ?>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<!-- Settings Section -->
<div class="smart-form-card">
<div class="smart-form-header">
<div class="section-title mb-0">
<i class="bi bi-receipt text-primary"></i> <?= h(tr('تفاصيل الفاتورة', 'Invoice Details')) ?>
</div>
</div>
<div class="smart-form-body">
<div class="mb-3">
<label class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
<select class="form-select custom-input" name="branch_code" <?= count($allowedBranches) === 1 ? 'readonly' : '' ?>>
<?php foreach ($allowedBranches as $branchCode): ?>
<option value="<?= h($branchCode) ?>"><?= h(branch_label($branchCode)) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3 position-relative">
<label class="form-label"><?= h(tr('العميل', 'Customer')) ?></label>
<div class="input-group">
<input type="hidden" id="formCustomerId" name="customer_id">
<input type="text" id="formCustomer" name="customer_name" class="form-control custom-input" style="border-right-width: 1px;" placeholder="<?= h(tr('بحث (اسم أو هاتف)', 'Search (Name or Phone)')) ?>" autocomplete="off">
<button class="btn btn-outline-primary px-3" style="border-radius: 0 8px 8px 0;" type="button" onclick="openNewCustomerModal()" title="<?= h(tr('إضافة عميل', 'Add Customer')) ?>">
<i class="bi bi-person-plus-fill"></i>
</button>
</div>
<div id="formCustomerDropdown" class="item-search-dropdown w-100" style="top: 100%;"></div>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('نوع العملية', 'Entry Type')) ?></label>
<select class="form-select custom-input" name="sale_status">
<option value="completed"><?= h(tr('فاتورة بيع (تم الدفع)', 'Sale Bill (Paid)')) ?></option>
<option value="order"><?= h(tr('طلب مسبق (دفع لاحق)', 'Order (Pay Later)')) ?></option>
</select>
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('طريقة الدفع', 'Payment Method')) ?></label>
<select class="form-select custom-input" name="payment_method">
<option value="cash"><?= h(tr('نقداً', 'Cash')) ?></option>
<option value="card"><?= h(tr('بطاقة ائتمان', 'Credit Card')) ?></option>
<option value="transfer"><?= h(tr('تحويل بنكي', 'Bank Transfer')) ?></option>
<option value="pay_later"><?= h(tr('آجل (Pay Later)', 'Pay Later')) ?></option>
</select>
</div>
<div class="mb-4">
<label class="form-label"><?= h(tr('ملاحظات (اختياري)', 'Notes (Optional)')) ?></label>
<textarea class="form-control custom-input" name="notes" rows="2" placeholder="<?= h(tr('أي ملاحظات إضافية...', 'Any additional notes...')) ?>"></textarea>
</div>
<!-- Summary -->
<div class="totals-box mb-4">
<div class="totals-row">
<span><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></span>
<span id="displaySubtotal" class="fw-medium">0.000</span>
</div>
<div class="totals-row">
<span><?= h(tr('الضريبة (مضافة)', 'VAT (Added)')) ?></span>
<span id="displayVat" class="text-muted">0.000</span>
</div>
<div class="totals-row grand-total">
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
<span id="displayTotal" class="text-primary">0.000 <?= h(tr('ر.ع', 'OMR')) ?></span>
</div>
</div>
<button type="submit" class="btn btn-primary w-100 py-2 fs-5 rounded-3 shadow-sm">
<i class="bi bi-check-circle me-1"></i> <?= h(tr('حفظ الفاتورة', 'Save Invoice')) ?>
</button>
</div>
</div>
</div>
</div>
</form>
</div>
<!-- New Customer Modal -->
<div class="modal fade" id="newCustomerModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content border-0 shadow-lg" style="border-radius: 16px;">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold"><?= h(tr('إضافة عميل', 'Add Customer')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label text-muted small mb-1"><?= h(tr('الاسم', 'Name')) ?> <span class="text-danger">*</span></label>
<input type="text" id="ncName" class="form-control rounded-3">
</div>
<div class="mb-3">
<label class="form-label text-muted small mb-1"><?= h(tr('رقم الهاتف', 'Phone')) ?></label>
<input type="text" id="ncPhone" class="form-control rounded-3" dir="ltr">
</div>
<div class="d-grid mt-4">
<button class="btn btn-primary rounded-pill fw-semibold shadow-sm" onclick="saveNewCustomer()"><?= h(tr('حفظ العميل', 'Save Customer')) ?></button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
const catalogData = <?= json_encode($catalog, JSON_UNESCAPED_UNICODE) ?>;
const catalogArray = Object.values(catalogData);
let invoiceItems = {};
const searchInput = document.getElementById('itemSearchInput');
const dropdown = document.getElementById('itemDropdown');
const tbody = document.getElementById('invoiceLines');
const emptyRow = document.getElementById('emptyInvoiceRow');
const cartJson = document.getElementById('cart_json');
const currencySuffix = ' <?= h(tr('ر.ع', 'OMR')) ?>';
// Customers Logic
let customersData = <?= json_encode($customers, JSON_UNESCAPED_UNICODE) ?>;
const custInput = document.getElementById('formCustomer');
const custDropdown = document.getElementById('formCustomerDropdown');
custInput.addEventListener('input', function() {
const q = this.value.toLowerCase().trim();
custDropdown.innerHTML = '';
if (q.length < 2) {
document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show');
return;
}
const matches = customersData.filter(c =>
c.name.toLowerCase().includes(q) ||
(c.phone && c.phone.toLowerCase().includes(q))
).slice(0, 5);
if (matches.length > 0) {
matches.forEach(c => {
const div = document.createElement('div');
div.className = 'search-item-row';
div.innerHTML = `<strong>${c.name}</strong> ${c.phone ? '<small class="text-muted ms-2">'+c.phone+'</small>' : ''}`;
div.onclick = function() {
custInput.value = c.name + (c.phone ? ' - ' + c.phone : '');
document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show');
};
custDropdown.appendChild(div);
});
custDropdown.classList.add('show');
} else {
document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show');
}
});
document.addEventListener('click', function(e) {
if (!custInput.contains(e.target) && !custDropdown.contains(e.target)) {
document.getElementById('formCustomerId').value = c.id; custDropdown.classList.remove('show');
}
});
let newCustomerModalObj = null;
function openNewCustomerModal() {
if (!newCustomerModalObj) {
newCustomerModalObj = new bootstrap.Modal(document.getElementById('newCustomerModal'));
}
document.getElementById('ncName').value = '';
document.getElementById('ncPhone').value = '';
newCustomerModalObj.show();
}
async function saveNewCustomer() {
const name = document.getElementById('ncName').value.trim();
const phone = document.getElementById('ncPhone').value.trim();
if (!name) {
Swal.fire({icon: 'warning', text: '<?= h(tr('الاسم مطلوب', 'Name is required')) ?>'});
return;
}
const formData = new FormData();
formData.append('name', name);
formData.append('phone', phone);
try {
const res = await fetch('api/customers.php', {
method: 'POST',
body: formData
});
const data = await res.json();
if (data.success) {
customersData.push(data.customer);
custInput.value = data.customer.name + (data.customer.phone ? ' - ' + data.customer.phone : '');
document.getElementById('formCustomerId').value = data.customer.id;
newCustomerModalObj.hide();
const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 });
Toast.fire({ icon: 'success', title: '<?= h(tr('تم إضافة العميل', 'Customer added')) ?>' });
} else {
Swal.fire({icon: 'warning', text: data.error});
}
} catch(err) {
Swal.fire({icon: 'warning', text: 'Error saving customer'});
}
}
// Search logic
searchInput.addEventListener('input', function() {
const q = this.value.toLowerCase().trim();
dropdown.innerHTML = '';
if (q === '') {
dropdown.classList.remove('show');
return;
}
const matches = catalogArray.filter(item => {
const nameAr = (item.name_ar || '').toLowerCase();
const nameEn = (item.name_en || '').toLowerCase();
const sku = (item.sku || '').toLowerCase();
return nameAr.includes(q) || nameEn.includes(q) || sku.includes(q);
}).slice(0, 6);
if (matches.length > 0) {
matches.forEach(item => {
const div = document.createElement('div');
div.className = 'search-item-row d-flex justify-content-between align-items-center';
const name = '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en;
div.innerHTML = `
<div>
<div class="fw-medium text-dark">${name}</div>
<div class="text-muted small">SKU: ${item.sku}</div>
</div>
<div class="fw-semibold text-primary">${parseFloat(item.price).toFixed(3)}</div>
`;
div.onclick = () => {
addItemToInvoice(item.sku);
searchInput.value = '';
dropdown.classList.remove('show');
searchInput.focus();
};
dropdown.appendChild(div);
});
dropdown.classList.add('show');
} else {
dropdown.classList.remove('show');
}
});
// Barcode scanner integration on enter
searchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
const q = this.value.trim();
if(q === '') return;
const match = catalogArray.find(item => item.sku === q);
if (match) {
addItemToInvoice(match.sku);
searchInput.value = '';
dropdown.classList.remove('show');
}
}
});
document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target) && !dropdown.contains(e.target)) {
dropdown.classList.remove('show');
}
});
function addItemToInvoice(sku) {
if (invoiceItems[sku]) {
invoiceItems[sku].qty += 1;
} else {
const item = catalogData[sku];
invoiceItems[sku] = {
sku: sku,
name: '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en,
price: parseFloat(item.price),
qty: 1
};
}
renderInvoice();
}
function changeItemName(sku, newName) {
if (!invoiceItems[sku]) {
return;
}
const fallbackName = '<?= current_lang() ?>' === 'ar'
? (catalogData[sku].name_ar || catalogData[sku].name_en || sku)
: (catalogData[sku].name_en || catalogData[sku].name_ar || sku);
const nextName = String(newName || '').trim();
invoiceItems[sku].name = nextName !== '' ? nextName : fallbackName;
renderInvoice();
}
function changeItemPrice(sku, newPrice) {
if (!invoiceItems[sku]) {
return;
}
const parsed = parseFloat(newPrice);
invoiceItems[sku].price = Number.isFinite(parsed) && parsed >= 0
? Math.round(parsed * 1000) / 1000
: (parseFloat(catalogData[sku].price) || 0);
renderInvoice();
}
function changeQty(sku, newQty) {
const qty = parseInt(newQty);
if (isNaN(qty) || qty < 1) {
delete invoiceItems[sku];
} else {
invoiceItems[sku].qty = qty;
}
renderInvoice();
}
function removeItem(sku) {
delete invoiceItems[sku];
renderInvoice();
}
function escapeHtml(value) {
return String(value ?? '').replace(/[&<>"']/g, function(char) {
return ({'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'})[char] || char;
});
}
function renderInvoice() {
const skus = Object.keys(invoiceItems);
if (skus.length === 0) {
tbody.innerHTML = '';
tbody.appendChild(emptyRow);
updateTotals(0, 0);
cartJson.value = '[]';
return;
}
tbody.innerHTML = '';
let totalAmount = 0;
let totalVat = 0;
const cartData = [];
skus.forEach(sku => {
const item = invoiceItems[sku];
const lineTotal = item.qty * item.price;
const vatPercent = parseFloat(catalogData[sku].vat) || 0;
const itemVat = lineTotal * (vatPercent / 100);
totalVat += itemVat;
totalAmount += lineTotal;
cartData.push({ sku: item.sku, name: item.name, price: item.price, qty: item.qty });
const safeSku = escapeHtml(item.sku);
const safeName = escapeHtml(item.name);
const tr = document.createElement('tr');
tr.innerHTML = `
<td>
<input type="text" class="form-control form-control-sm line-input fw-medium mb-2" value="${safeName}" onchange="changeItemName('${sku}', this.value)" onkeyup="if(event.key==='Enter') changeItemName('${sku}', this.value)">
<div class="text-muted small">SKU: ${safeSku}</div>
</td>
<td class="text-center align-middle">
<input type="number" class="form-control form-control-sm text-center line-input line-price-input mx-auto" min="0" step="0.001" value="${item.price.toFixed(3)}" onchange="changeItemPrice('${sku}', this.value)" onkeyup="if(event.key==='Enter') changeItemPrice('${sku}', this.value)">
</td>
<td class="text-center align-middle">
<input type="number" class="qty-control mx-auto fw-medium" min="1" value="${item.qty}" onchange="changeQty('${sku}', this.value)" onkeyup="if(event.key==='Enter') changeQty('${sku}', this.value)">
</td>
<td class="text-center fw-semibold text-dark align-middle">${lineTotal.toFixed(3)}</td>
<td class="text-center align-middle">
<button type="button" class="btn-remove mx-auto" onclick="removeItem('${sku}')" title="<?= h(tr('إزالة', 'Remove')) ?>">
<i class="bi bi-trash"></i>
</button>
</td>
`;
tbody.appendChild(tr);
});
updateTotals(totalAmount, totalVat);
cartJson.value = JSON.stringify(cartData);
}
function updateTotals(total, vat) {
const subtotal = total;
const finalTotal = subtotal + vat;
document.getElementById('displaySubtotal').innerText = subtotal.toFixed(3);
document.getElementById('displayVat').innerText = vat.toFixed(3);
document.getElementById('displayTotal').innerText = finalTotal.toFixed(3) + currencySuffix;
}
// Intercept form submission to check if items exist
document.getElementById('smart-sale-form').addEventListener('submit', function(e) {
const paymentMethod = document.querySelector('select[name="payment_method"]').value;
const saleStatus = document.querySelector('select[name="sale_status"]').value;
const customerId = document.getElementById('formCustomerId').value;
if (Object.keys(invoiceItems).length === 0) {
e.preventDefault();
Swal.fire({icon: 'warning', text: '<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>'});
} else if (saleStatus === 'order' && !customerId) {
e.preventDefault();
Swal.fire({icon: 'warning', text: '<?= h(tr('يجب اختيار عميل للطلب المسبق.', 'You must select a customer for a pre-order.')) ?>'});
} else if (paymentMethod === 'pay_later' && !customerId) {
e.preventDefault();
Swal.fire({icon: 'warning', text: '<?= h(tr('يجب اختيار عميل مسجل للدفع الآجل.', 'You must select a registered customer for pay later.')) ?>'});
}
});
</script>
<?php require __DIR__ . '/footer.php'; ?>