adding fat

This commit is contained in:
Flatlogic Bot 2026-02-26 15:17:34 +00:00
parent 7e5ce73bed
commit 13a3054fd1
7 changed files with 210 additions and 79 deletions

View File

@ -86,13 +86,14 @@ if (!empty($_GET['search'])) {
$where_clause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
// Calculate Total Sum and Total Commission for filtered orders
$sum_query = "SELECT SUM(total_amount) as total_sum, SUM(commission_amount) as total_commission FROM orders o $where_clause";
// Calculate Total Sum and Total Commission and Total VAT for filtered orders
$sum_query = "SELECT SUM(total_amount) as total_sum, SUM(commission_amount) as total_commission, SUM(vat) as total_vat FROM orders o $where_clause";
$stmt_sum = $pdo->prepare($sum_query);
$stmt_sum->execute($params);
$sum_data = $stmt_sum->fetch(PDO::FETCH_ASSOC);
$total_sum = (float)($sum_data['total_sum'] ?? 0);
$total_commission = (float)($sum_data['total_commission'] ?? 0);
$total_vat_sum = (float)($sum_data['total_vat'] ?? 0);
// Main Query
$query = "SELECT o.*, ot.name as outlet_name, pt.name as payment_type_name, u.username as cashier_name,
@ -107,9 +108,6 @@ $query = "SELECT o.*, ot.name as outlet_name, pt.name as payment_type_name, u.us
$orders_pagination = paginate_query($pdo, $query, $params);
$orders = $orders_pagination['data'];
// Add total sum to pagination object for rendering
$orders_pagination['total_amount_sum'] = $total_sum;
$settings = get_company_settings();
$commission_enabled = !empty($settings['commission_enabled']);
@ -170,7 +168,6 @@ include 'includes/header.php';
</div>
</div>
</div>
<?php if ($commission_enabled): ?>
<div class="col-md-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
@ -179,15 +176,14 @@ include 'includes/header.php';
<i class="bi bi-percent fs-4"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted mb-0 small text-uppercase fw-bold">Total Commission</h6>
<div class="fs-4 fw-bold text-warning"><?= format_currency($total_commission) ?></div>
<h6 class="text-muted mb-0 small text-uppercase fw-bold">Total VAT</h6>
<div class="fs-4 fw-bold text-warning"><?= format_currency($total_vat_sum) ?></div>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<div class="col-md-<?= $commission_enabled ? '3' : '4' ?>">
<div class="col-md-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="d-flex align-items-center">
@ -202,7 +198,7 @@ include 'includes/header.php';
</div>
</div>
</div>
<div class="col-md-<?= $commission_enabled ? '3' : '4' ?>">
<div class="col-md-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="d-flex align-items-center">
@ -279,7 +275,7 @@ include 'includes/header.php';
<th>Cashier</th>
<th>Customer</th>
<th>Type</th>
<th>Source</th>
<th>VAT</th>
<th>Total</th>
<?php if ($commission_enabled): ?>
<th>Commission</th>
@ -326,11 +322,7 @@ include 'includes/header.php';
<span class="badge <?= $badge ?> text-dark bg-opacity-25 border border-<?= str_replace('bg-', '', $badge) ?>"><?= ucfirst($order['order_type']) ?></span>
</td>
<td>
<?php if ($order['order_type'] === 'dine-in' && $order['table_number']): ?>
<span class="badge bg-secondary">Table <?= htmlspecialchars((string)($order['table_number'] ?? '')) ?></span>
<?php else: ?>
<span class="badge bg-light text-dark border"><?= ucfirst($order['order_type']) ?></span>
<?php endif; ?>
<span class="text-muted small"><?= format_currency($order['vat']) ?></span>
</td>
<td class="fw-bold"><?= format_currency($order['total_amount']) ?></td>
<?php if ($commission_enabled): ?>

View File

@ -15,6 +15,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$name_ar = $_POST['name_ar'] ?? '';
$category_id = (int)$_POST['category_id'];
$price = (float)$_POST['price'];
$vat_percent = (float)($_POST['vat_percent'] ?? 0);
$cost_price = (float)($_POST['cost_price'] ?? 0);
$stock_quantity = (int)($_POST['stock_quantity'] ?? 0);
$description = $_POST['description'] ?? '';
@ -48,16 +49,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if (!has_permission('products_edit')) {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to edit products.</div>';
} else {
$stmt = $pdo->prepare("UPDATE products SET name = ?, name_ar = ?, category_id = ?, price = ?, cost_price = ?, stock_quantity = ?, description = ?, image_url = ?, promo_discount_percent = ?, promo_date_from = ?, promo_date_to = ?, is_loyalty = ? WHERE id = ?");
$stmt->execute([$name, $name_ar, $category_id, $price, $cost_price, $stock_quantity, $description, $image_url, $promo_discount_percent, $promo_date_from, $promo_date_to, $is_loyalty, $id]);
$stmt = $pdo->prepare("UPDATE products SET name = ?, name_ar = ?, category_id = ?, price = ?, vat_percent = ?, cost_price = ?, stock_quantity = ?, description = ?, image_url = ?, promo_discount_percent = ?, promo_date_from = ?, promo_date_to = ?, is_loyalty = ? WHERE id = ?");
$stmt->execute([$name, $name_ar, $category_id, $price, $vat_percent, $cost_price, $stock_quantity, $description, $image_url, $promo_discount_percent, $promo_date_from, $promo_date_to, $is_loyalty, $id]);
$message = '<div class="alert alert-success">Product updated successfully!</div>';
}
} elseif ($action === 'add_product') {
if (!has_permission('products_add')) {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to add products.</div>';
} else {
$stmt = $pdo->prepare("INSERT INTO products (name, name_ar, category_id, price, cost_price, stock_quantity, description, image_url, promo_discount_percent, promo_date_from, promo_date_to, is_loyalty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $name_ar, $category_id, $price, $cost_price, $stock_quantity, $description, $image_url, $promo_discount_percent, $promo_date_from, $promo_date_to, $is_loyalty]);
$stmt = $pdo->prepare("INSERT INTO products (name, name_ar, category_id, price, vat_percent, cost_price, stock_quantity, description, image_url, promo_discount_percent, promo_date_from, promo_date_to, is_loyalty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $name_ar, $category_id, $price, $vat_percent, $cost_price, $stock_quantity, $description, $image_url, $promo_discount_percent, $promo_date_from, $promo_date_to, $is_loyalty]);
$message = '<div class="alert alert-success">Product created successfully!</div>';
}
}
@ -184,6 +185,7 @@ include 'includes/header.php';
<th class="ps-4"><?= t('product') ?></th>
<th><?= t('category') ?></th>
<th><?= t('price') ?></th>
<th><?= t('VAT') ?></th>
<th><?= t('stock') ?></th>
<th><?= t('promotion') ?></th>
<th class="text-end pe-4"><?= t('actions') ?></th>
@ -223,6 +225,9 @@ include 'includes/header.php';
<small class="text-muted"><?= t('cost') ?>: <?= format_currency($product['cost_price']) ?></small>
<?php endif; ?>
</td>
<td>
<span class="badge bg-light text-dark border"><?= (float)$product['vat_percent'] ?>%</span>
</td>
<td>
<?php if ($product['stock_quantity'] <= 5): ?>
<span class="badge bg-danger bg-opacity-10 text-danger border border-danger rounded-pill px-3"><?= $product['stock_quantity'] ?></span>
@ -256,7 +261,7 @@ include 'includes/header.php';
<?php endforeach; ?>
<?php if (empty($products)): ?>
<tr>
<td colspan="6" class="text-center py-5 text-muted">
<td colspan="7" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-1 d-block mb-2 opacity-25"></i>
<?= t('none') ?>
</td>
@ -306,7 +311,7 @@ include 'includes/header.php';
<input type="text" name="name_ar" id="productNameAr" class="form-control rounded-3 border-0 bg-light" dir="rtl">
</div>
<div class="col-md-6">
<div class="col-md-4">
<label class="form-label small fw-bold text-muted"><?= t('category') ?> <span class="text-danger">*</span></label>
<select name="category_id" id="productCategoryId" class="form-select rounded-3 border-0 bg-light" required>
<option value=""><?= t('select') ?> <?= t('category') ?></option>
@ -316,22 +321,32 @@ include 'includes/header.php';
</select>
</div>
<div class="col-md-3">
<div class="col-md-2">
<label class="form-label small fw-bold text-muted"><?= t('price') ?> <span class="text-danger">*</span></label>
<input type="number" step="0.01" name="price" id="productPrice" class="form-control rounded-3 border-0 bg-light" required>
</div>
<div class="col-md-2">
<label class="form-label small fw-bold text-muted"><?= t('VAT') ?> (%)</label>
<select name="vat_percent" id="productVatPercent" class="form-select rounded-3 border-0 bg-light">
<option value="0">0%</option>
<option value="5">5%</option>
<option value="10">10%</option>
<option value="15">15%</option>
</select>
</div>
<div class="col-md-3">
<div class="col-md-2">
<label class="form-label small fw-bold text-muted"><?= t('cost') ?> <?= t('price') ?></label>
<input type="number" step="0.01" name="cost_price" id="productCostPrice" class="form-control rounded-3 border-0 bg-light">
</div>
<div class="col-md-4">
<div class="col-md-2">
<label class="form-label small fw-bold text-muted"><?= t('stock') ?> <?= t('quantity') ?></label>
<input type="number" name="stock_quantity" id="productStockQuantity" class="form-control rounded-3 border-0 bg-light">
</div>
<div class="col-md-8">
<div class="col-12">
<label class="form-label small fw-bold text-muted"><?= t('description') ?></label>
<input type="text" name="description" id="productDescription" class="form-control rounded-3 border-0 bg-light">
</div>
@ -400,6 +415,7 @@ function prepareEditForm(p) {
document.getElementById('productNameAr').value = p.name_ar || '';
document.getElementById('productCategoryId').value = p.category_id;
document.getElementById('productPrice').value = p.price;
document.getElementById('productVatPercent').value = parseFloat(p.vat_percent || 0);
document.getElementById('productCostPrice').value = p.cost_price || '';
document.getElementById('productStockQuantity').value = p.stock_quantity || '0';
document.getElementById('productDescription').value = p.description || '';

View File

@ -74,6 +74,15 @@ $tables_pagination = paginate_query($pdo, $query);
$tables = $tables_pagination['data'];
include 'includes/header.php';
// Base URL for QR codes
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https') ? "https://" : "http://";
$host = $_SERVER['HTTP_HOST'];
// Calculate project root
$current_dir = dirname($_SERVER['PHP_SELF']); // /admin
$project_root = dirname($current_dir); // /
if ($project_root === DIRECTORY_SEPARATOR) $project_root = '';
$baseUrl = $protocol . $host . $project_root;
?>
<div class="d-flex justify-content-between align-items-center mb-4">
@ -102,6 +111,7 @@ include 'includes/header.php';
<th>Area</th>
<th>Capacity</th>
<th>Status</th>
<th>QR Code</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
@ -121,6 +131,12 @@ include 'includes/header.php';
<span class="badge bg-secondary-subtle text-secondary px-3"><?= ucfirst($table['status']) ?></span>
<?php endif; ?>
</td>
<td>
<button type="button" class="btn btn-sm btn-outline-info"
onclick="showTableQR('<?= $table['id'] ?>', '<?= htmlspecialchars($table['table_number']) ?>')">
<i class="bi bi-qr-code"></i> View QR
</button>
</td>
<td class="text-end pe-4">
<?php if (has_permission('tables_add')): ?>
<button type="button" class="btn btn-sm btn-outline-primary me-1"
@ -136,7 +152,7 @@ include 'includes/header.php';
<?php endforeach; ?>
<?php if (empty($tables)): ?>
<tr>
<td colspan="6" class="text-center py-4 text-muted">No tables found.</td>
<td colspan="7" class="text-center py-4 text-muted">No tables found.</td>
</tr>
<?php endif; ?>
</tbody>
@ -197,6 +213,33 @@ include 'includes/header.php';
</div>
</div>
</div>
<?php endif; ?>
<!-- QR Modal -->
<div class="modal fade" id="qrModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Table QR Code</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center" id="printableQR">
<h4 class="fw-bold mb-3" id="qrTableNumber">Table</h4>
<div id="qrContainer" class="mb-3">
<!-- QR Image will be loaded here -->
</div>
<p class="small text-muted mb-0">Scan to Order</p>
<div class="mt-2 small text-break text-muted" id="qrLink" style="font-size: 0.7rem;"></div>
</div>
<div class="modal-footer justify-content-center">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="printQR()">
<i class="bi bi-printer"></i> Print
</button>
</div>
</div>
</div>
</div>
<script>
function prepareAddForm() {
@ -216,7 +259,35 @@ function prepareEditForm(table) {
document.getElementById('tableCapacity').value = table.capacity || 2;
document.getElementById('tableStatus').value = table.status || 'available';
}
</script>
<?php endif; ?>
<?php include 'includes/footer.php'; ?>
function showTableQR(id, number) {
const baseUrl = '<?= $baseUrl ?>';
const qorderUrl = baseUrl + '/qorder.php?table_id=' + id;
const qrCodeUrl = "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=" + encodeURIComponent(qorderUrl);
document.getElementById('qrTableNumber').innerText = 'Table ' + number;
document.getElementById('qrContainer').innerHTML = '<img src="' + qrCodeUrl + '" alt="QR Code" class="img-fluid shadow-sm" style="max-width: 200px;">';
document.getElementById('qrLink').innerText = qorderUrl;
const modal = new bootstrap.Modal(document.getElementById('qrModal'));
modal.show();
}
function printQR() {
const content = document.getElementById('printableQR').innerHTML;
const printWindow = window.open('', '_blank');
printWindow.document.write('<html><head><title>Print Table QR</title>');
printWindow.document.write('<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">');
printWindow.document.write('<style>body { text-align: center; padding: 50px; }</style>');
printWindow.document.write('</head><body>');
printWindow.document.write(content);
printWindow.document.write('</body></html>');
printWindow.document.close();
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 500);
}
</script>
<?php include 'includes/footer.php'; ?>

View File

@ -29,11 +29,11 @@ try {
$table_number = null;
if ($order_type === 'dine-in') {
$tid = $data['table_number'] ?? null; // Front-end sends ID as table_number
$tid = $data['table_id'] ?? ($data['table_number'] ?? null); // Support both table_id and table_number as numeric ID
if ($tid) {
// Validate table exists AND belongs to the correct outlet
$stmt = $pdo->prepare(
"SELECT t.id, t.name
"SELECT t.id, t.table_number
FROM tables t
JOIN areas a ON t.area_id = a.id
WHERE t.id = ? AND a.outlet_id = ?"
@ -42,7 +42,7 @@ try {
$table = $stmt->fetch(PDO::FETCH_ASSOC);
if ($table) {
$table_id = $table['id'];
$table_number = $table['name'];
$table_number = $table['table_number'];
}
}
@ -50,7 +50,7 @@ try {
if (!$table_id) {
// Optional: try to find the first available table for this outlet
$stmt = $pdo->prepare(
"SELECT t.id, t.name
"SELECT t.id, t.table_number
FROM tables t
JOIN areas a ON t.area_id = a.id
WHERE a.outlet_id = ?
@ -60,7 +60,7 @@ try {
$table = $stmt->fetch(PDO::FETCH_ASSOC);
if ($table) {
$table_id = $table['id'];
$table_number = $table['name'];
$table_number = $table['table_number'];
}
}
}
@ -105,7 +105,8 @@ try {
}
// Total amount will be recalculated on server to be safe
$calculated_total = 0;
$calculated_subtotal = 0;
$calculated_vat = 0;
// First, process items to calculate real total and handle loyalty
$processed_items = [];
@ -126,6 +127,7 @@ try {
if (!$product) continue;
$unit_price = get_product_price($product);
$vat_percent = floatval($product['vat_percent'] ?? 0);
$variant_name = null;
// Add variant adjustment
@ -150,6 +152,7 @@ try {
'variant_name' => $variant_name,
'quantity' => $qty,
'unit_price' => $unit_price,
'vat_percent' => $vat_percent,
'is_loyalty' => (bool)$product['is_loyalty']
];
}
@ -230,14 +233,22 @@ try {
}
}
// Recalculate Subtotal and Earned Points
foreach ($processed_items as $pi) {
$calculated_total += $pi['unit_price'] * $pi['quantity'];
// Recalculate Subtotal, VAT and Earned Points
foreach ($processed_items as &$pi) {
$item_subtotal = $pi['unit_price'] * $pi['quantity'];
$item_vat = $item_subtotal * ($pi['vat_percent'] / 100);
$pi['vat_amount'] = $item_vat;
$calculated_subtotal += $item_subtotal;
$calculated_vat += $item_vat;
// Award points for PAID loyalty items
if ($pi['is_loyalty'] && $pi['unit_price'] > 0 && $pi['quantity'] > 0) {
$points_awarded += $pi['quantity'] * $points_per_product;
}
}
unset($pi);
// Award Points
if ($customer_id && $loyalty_enabled && $points_awarded > 0) {
@ -250,8 +261,7 @@ try {
$award_history_id = $pdo->lastInsertId();
}
$vat = isset($data['vat']) ? floatval($data['vat']) : 0.00;
$final_total = max(0, $calculated_total + $vat);
$final_total = max(0, $calculated_subtotal + $calculated_vat);
// User/Payment info
$user = get_logged_user();
@ -266,7 +276,7 @@ try {
$userStmt->execute([$user_id]);
$commission_rate = (float)$userStmt->fetchColumn();
if ($commission_rate > 0) {
$commission_amount = $calculated_total * ($commission_rate / 100);
$commission_amount = $calculated_subtotal * ($commission_rate / 100);
}
}
@ -288,21 +298,21 @@ try {
$stmt = $pdo->prepare("UPDATE orders SET
outlet_id = ?, table_id = ?, table_number = ?, order_type = ?,
customer_id = ?, customer_name = ?, customer_phone = ?,
payment_type_id = ?, total_amount = ?, discount = ?, user_id = ?,
payment_type_id = ?, total_amount = ?, discount = ?, vat = ?, user_id = ?,
commission_amount = ?, status = 'pending'
WHERE id = ?");
$stmt->execute([
$outlet_id, $table_id, $table_number, $order_type,
$customer_id, $customer_name, $customer_phone,
$payment_type_id, $final_total, $vat, $user_id,
$payment_type_id, $final_total, 0, $calculated_vat, $user_id,
$commission_amount, $order_id
]);
$delStmt = $pdo->prepare("DELETE FROM order_items WHERE order_id = ?");
$delStmt->execute([$order_id]);
} else {
$stmt = $pdo->prepare("INSERT INTO orders (outlet_id, table_id, table_number, order_type, customer_id, customer_name, customer_phone, payment_type_id, total_amount, discount, user_id, commission_amount, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending')");
$stmt->execute([$outlet_id, $table_id, $table_number, $order_type, $customer_id, $customer_name, $customer_phone, $payment_type_id, $final_total, $vat, $user_id, $commission_amount]);
$stmt = $pdo->prepare("INSERT INTO orders (outlet_id, table_id, table_number, order_type, customer_id, customer_name, customer_phone, payment_type_id, total_amount, discount, vat, user_id, commission_amount, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending')");
$stmt->execute([$outlet_id, $table_id, $table_number, $order_type, $customer_id, $customer_name, $customer_phone, $payment_type_id, $final_total, 0, $calculated_vat, $user_id, $commission_amount]);
$order_id = $pdo->lastInsertId();
}
@ -317,13 +327,13 @@ try {
}
// Insert Items and Update Stock
$item_stmt = $pdo->prepare("INSERT INTO order_items (order_id, product_id, product_name, variant_id, variant_name, quantity, unit_price) VALUES (?, ?, ?, ?, ?, ?, ?)");
$item_stmt = $pdo->prepare("INSERT INTO order_items (order_id, product_id, product_name, variant_id, variant_name, quantity, unit_price, vat_percent, vat_amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stock_stmt = $pdo->prepare("UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?");
$order_items_list = [];
foreach ($processed_items as $pi) {
$item_stmt->execute([$order_id, $pi['product_id'], $pi['product_name'], $pi['variant_id'], $pi['variant_name'], $pi['quantity'], $pi['unit_price']]);
$item_stmt->execute([$order_id, $pi['product_id'], $pi['product_name'], $pi['variant_id'], $pi['variant_name'], $pi['quantity'], $pi['unit_price'], $pi['vat_percent'], $pi['vat_amount']]);
// Decrement Stock
$stock_stmt->execute([$pi['quantity'], $pi['product_id']]);

View File

@ -40,6 +40,8 @@ document.addEventListener('DOMContentLoaded', () => {
const cartItemsContainer = document.getElementById('cart-items');
const cartTotalPrice = document.getElementById('cart-total-price');
const cartSubtotal = document.getElementById('cart-subtotal');
const cartVatAmount = document.getElementById('cart-vat-amount');
const cartVatRow = document.getElementById('cart-vat-row');
const cartVatInput = document.getElementById('cart-vat-input');
const quickOrderBtn = document.getElementById('quick-order-btn');
@ -504,7 +506,9 @@ document.addEventListener('DOMContentLoaded', () => {
addToCart({
id: product.id, name: product.name, name_ar: product.name_ar || "",
price: parseFloat(product.price), base_price: parseFloat(product.price),
hasVariants: false, quantity: 1, variant_id: null, variant_name: null, is_loyalty: parseInt(product.is_loyalty) === 1
hasVariants: false, quantity: 1, variant_id: null, variant_name: null,
is_loyalty: parseInt(product.is_loyalty) === 1,
vat_percent: parseFloat(product.vat_percent || 0)
});
}
};
@ -526,7 +530,9 @@ document.addEventListener('DOMContentLoaded', () => {
addToCart({
id: product.id, name: product.name, name_ar: product.name_ar || "",
price: finalPrice, base_price: parseFloat(product.price),
hasVariants: true, quantity: 1, variant_id: v.id, variant_name: v.name, is_loyalty: parseInt(product.is_loyalty) === 1
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 || 0)
});
variantSelectionModal.hide();
};
@ -572,6 +578,8 @@ document.addEventListener('DOMContentLoaded', () => {
if (cart.length === 0) {
cartItemsContainer.innerHTML = `<div class="text-center text-muted mt-5"><i class="bi bi-basket3 fs-1 text-light"></i><p class="mt-2">${_t('cart_empty')}</p></div>`;
if (cartSubtotal) cartSubtotal.innerText = formatCurrency(0);
if (cartVatAmount) cartVatAmount.innerText = formatCurrency(0);
if (cartVatRow) cartVatRow.classList.add('d-none');
if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(0);
if (quickOrderBtn) { quickOrderBtn.disabled = true; quickOrderBtn.innerText = _t('quick_pay'); }
if (placeOrderBtn) { placeOrderBtn.disabled = true; placeOrderBtn.innerText = _t('save_bill'); }
@ -580,9 +588,13 @@ document.addEventListener('DOMContentLoaded', () => {
cartItemsContainer.innerHTML = '';
let subtotal = 0;
let totalVat = 0;
cart.forEach((item, index) => {
const itemTotal = item.price * item.quantity;
const itemVat = itemTotal * (item.vat_percent / 100);
subtotal += itemTotal;
totalVat += itemVat;
const row = document.createElement('div');
row.className = 'd-flex justify-content-between align-items-center mb-3 border-bottom pb-2';
const itemName = (LANG === 'ar' && item.name_ar) ? item.name_ar : item.name;
@ -592,7 +604,7 @@ document.addEventListener('DOMContentLoaded', () => {
<div class="flex-grow-1 me-2">
<div class="fw-bold text-truncate" style="max-width: 140px;">${itemName}</div>
${otherName ? `<div class="text-muted small" style="font-size: 0.75rem;">${otherName}</div>` : ''}
<div class="small text-muted">${formatCurrency(item.price)}</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>
<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>
@ -608,11 +620,16 @@ document.addEventListener('DOMContentLoaded', () => {
});
if (cartSubtotal) cartSubtotal.innerText = formatCurrency(subtotal);
const vatRate = parseFloat(settings.vat_rate) || 0;
const vat = subtotal * (vatRate / 100);
if (cartVatInput) cartVatInput.value = vat.toFixed(2);
const total = subtotal + vat;
if (cartVatAmount) cartVatAmount.innerText = formatCurrency(totalVat);
if (cartVatRow) {
if (totalVat > 0) cartVatRow.classList.remove('d-none');
else cartVatRow.classList.add('d-none');
}
if (cartVatInput) cartVatInput.value = totalVat.toFixed(3);
const total = subtotal + totalVat;
if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(total);
if (quickOrderBtn) { quickOrderBtn.disabled = false; quickOrderBtn.innerText = _t('quick_pay'); }
if (placeOrderBtn) { placeOrderBtn.disabled = false; placeOrderBtn.innerText = _t('save_bill'); }
}
@ -648,9 +665,23 @@ document.addEventListener('DOMContentLoaded', () => {
window.processOrder = function(paymentTypeId, paymentTypeName) {
const orderTypeInput = document.querySelector('input[name="order_type"]:checked');
const orderType = orderTypeInput ? orderTypeInput.value : 'takeaway';
const subtotal = cart.reduce((acc, item) => acc + (item.price * item.quantity), 0);
const vatRate = parseFloat(settings.vat_rate) || 0;
const vat = subtotal * (vatRate / 100);
let subtotal = 0;
let totalVat = 0;
const itemsData = cart.map(item => {
const itemTotal = item.price * item.quantity;
const itemVat = itemTotal * (item.vat_percent / 100);
subtotal += itemTotal;
totalVat += itemVat;
return {
product_id: item.id,
quantity: item.quantity,
unit_price: item.price,
variant_id: item.variant_id,
vat_percent: item.vat_percent,
vat_amount: itemVat
};
});
const orderData = {
order_id: currentOrderId,
@ -659,9 +690,9 @@ document.addEventListener('DOMContentLoaded', () => {
customer_id: selectedCustomerId ? selectedCustomerId.value : null,
outlet_id: CURRENT_OUTLET ? CURRENT_OUTLET.id : 1,
payment_type_id: paymentTypeId,
total_amount: subtotal + vat,
vat: vat,
items: cart.map(item => ({ product_id: item.id, quantity: item.quantity, unit_price: item.price, variant_id: item.variant_id })),
total_amount: subtotal + totalVat,
vat: totalVat,
items: itemsData,
redeem_loyalty: isLoyaltyRedemption
};
@ -673,10 +704,13 @@ document.addEventListener('DOMContentLoaded', () => {
name: item.name,
variant_name: item.variant_name,
quantity: item.quantity,
price: item.price
price: item.price,
vat_percent: item.vat_percent,
vat_amount: (item.price * item.quantity) * (item.vat_percent / 100)
})),
total: subtotal + vat,
vat: vat,
subtotal: subtotal,
total: subtotal + totalVat,
vat: totalVat,
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' }),
@ -702,7 +736,7 @@ document.addEventListener('DOMContentLoaded', () => {
};
window.printThermalReceipt = function(data) {
const width = 400;
const width = 450;
const height = 800;
const left = (screen.width - width) / 2;
const top = (screen.height - height) / 2;
@ -717,7 +751,7 @@ document.addEventListener('DOMContentLoaded', () => {
const tr = {
'Order': 'الطلب', 'Type': 'النوع', 'Date': 'التاريخ', 'Staff': 'الموظف',
'Table': 'طاولة', 'Payment': 'الدفع', 'ITEM': 'الصنف', 'TOTAL': 'المجموع',
'Subtotal': 'المجموع الفرعي', 'VAT': 'ضريبة القيمة المضافة', 'Tax Included': 'شامل الضريبة',
'Subtotal': 'المجموع الفرعي', 'VAT': 'الضريبة', 'Tax Included': 'شامل الضريبة',
'THANK YOU FOR YOUR VISIT!': 'شكراً لزيارتكم!', 'Please come again.': 'يرجى زيارتنا مرة أخرى.',
'Customer Details': 'تفاصيل العميل', 'Tel': 'هاتف', 'takeaway': 'سفري',
'dine-in': 'محلي', 'delivery': 'توصيل', 'VAT No': 'الرقم الضريبي', 'CTR No': 'رقم السجل التجاري'
@ -730,6 +764,7 @@ document.addEventListener('DOMContentLoaded', () => {
${item.variant_name ? `<div style="font-size: 10px; color: #555;">(${item.variant_name})</div>` : ''}
<div style="font-size: 11px;">${item.quantity} x ${formatCurrency(item.price)}</div>
</td>
<td style="text-align: center; vertical-align: middle; font-size: 11px;">${item.vat_percent > 0 ? item.vat_percent + '%' : '-'}</td>
<td style="text-align: right; vertical-align: middle; font-weight: bold;">${formatCurrency(item.quantity * item.price)}</td>
</tr>
`).join('');
@ -761,7 +796,6 @@ document.addEventListener('DOMContentLoaded', () => {
const loyaltyHtml = data.loyaltyRedeemed ? `<div style="color: #d63384; font-weight: bold; margin: 5px 0; text-align: center;">* Loyalty Reward Applied *</div>` : '';
const subtotal = data.total - data.vat;
const logoHtml = settings.logo_url ? `<img src="${BASE_URL}${settings.logo_url}" style="max-height: 80px; max-width: 150px; margin-bottom: 10px;">` : '';
const html = `
@ -805,14 +839,20 @@ document.addEventListener('DOMContentLoaded', () => {
<div class="thick-divider"></div>
${customerHtml}
<table>
<thead><tr><th style="text-align: left; padding-bottom: 5px;">ITEM / الصنف</th><th style="text-align: right; padding-bottom: 5px;">TOTAL / المجموع</th></tr></thead>
<thead>
<tr>
<th style="text-align: left; padding-bottom: 5px;">ITEM / الصنف</th>
<th style="text-align: center; padding-bottom: 5px; font-size: 10px;">VAT / الضريبة</th>
<th style="text-align: right; padding-bottom: 5px;">TOTAL / المجموع</th>
</tr>
</thead>
<tbody>${itemsHtml}</tbody>
</table>
<div class="divider"></div>
<div class="totals">
<table style="width: 100%">
<tr><td>Subtotal / ${tr['Subtotal']}</td><td style="text-align: right">${formatCurrency(subtotal)}</td></tr>
${Math.abs(data.vat) > 0 ? `<tr><td>${data.vat < 0 ? 'Discount' : 'VAT'} / ${tr['VAT']}</td><td style="text-align: right">${data.vat < 0 ? '-' : '+'}${formatCurrency(Math.abs(data.vat))}</td></tr>` : ''}
<tr><td>Subtotal / ${tr['Subtotal']}</td><td style="text-align: right">${formatCurrency(data.subtotal)}</td></tr>
${Math.abs(data.vat) > 0 ? `<tr><td>VAT / ${tr['VAT']}</td><td style="text-align: right">${formatCurrency(Math.abs(data.vat))}</td></tr>` : ''}
<tr style="font-weight: bold; font-size: 18px;"><td style="padding-top: 10px;">TOTAL / ${tr['TOTAL']}</td><td style="text-align: right; padding-top: 10px;">${formatCurrency(data.total)}</td></tr>
</table>
</div>
@ -841,4 +881,4 @@ document.addEventListener('DOMContentLoaded', () => {
const modal = new bootstrap.Modal(document.getElementById('qrRatingModal'));
modal.show();
};
});
});

View File

@ -326,6 +326,11 @@ if (!$loyalty_settings) {
<span class="text-muted small">Subtotal</span>
<span class="fw-bold small" id="cart-subtotal"><?= format_currency(0) ?></span>
</div>
<div class="d-flex justify-content-between mb-1" id="cart-vat-row">
<span class="text-muted small">VAT</span>
<span class="fw-bold small" id="cart-vat-amount"><?= format_currency(0) ?></span>
<input type="hidden" id="cart-vat-input" value="0">
</div>
<div class="d-flex justify-content-between mb-3">
<span class="fs-6 fw-bold">Total</span>
<span class="fs-5 fw-bold text-primary" id="cart-total-price"><?= format_currency(0) ?></span>
@ -478,4 +483,4 @@ if (!$loyalty_settings) {
</script>
<script src="assets/js/main.js?v=<?= time() ?>"></script>
</body>
</html>
</html>

View File

@ -14,7 +14,7 @@ if ($table_id <= 0) {
// Fetch table and outlet info
$stmt = $pdo->prepare("
SELECT t.id, t.name as table_name, a.outlet_id, o.name as outlet_name
SELECT t.id, t.table_number as table_name, a.outlet_id, o.name as outlet_name
FROM tables t
JOIN areas a ON t.area_id = a.id
JOIN outlets o ON a.outlet_id = o.id
@ -303,9 +303,6 @@ foreach ($variants_raw as $v) {
document.getElementById('lang-btn').textContent = currentLang === 'en' ? 'AR' : 'EN';
if (currentLang === 'ar') {
document.body.classList.add('lang-ar');
document.body.classList.remove('both-names'); // Optional: hide EN when AR is active?
// User said "both", so maybe I should keep both-names active if I want to show both.
// But let's follow standard toggle behavior for UI.
} else {
document.body.classList.remove('lang-ar');
}
@ -458,7 +455,7 @@ foreach ($variants_raw as $v) {
const payload = {
outlet_id: OUTLET_ID,
table_number: TABLE_ID,
table_id: TABLE_ID, // Use table_id instead of table_number
order_type: 'dine-in',
customer_name: customerName,
items: cart,
@ -513,4 +510,4 @@ foreach ($variants_raw as $v) {
}
</script>
</body>
</html>
</html>