38682-vm/admin/order_view.php
2026-02-24 03:07:06 +00:00

468 lines
22 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../includes/functions.php';
$pdo = db();
require_permission('orders_view');
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if (!$id) {
header("Location: orders.php");
exit;
}
// Fetch Order Details
$stmt = $pdo->prepare("SELECT o.*, ot.name as outlet_name, pt.name as payment_type_name,
c.name as customer_name, c.phone as customer_phone, c.email as customer_email, c.address as customer_address,
u.username as created_by_username
FROM orders o
LEFT JOIN outlets ot ON o.outlet_id = ot.id
LEFT JOIN payment_types pt ON o.payment_type_id = pt.id
LEFT JOIN customers c ON o.customer_id = c.id
LEFT JOIN users u ON o.user_id = u.id
WHERE o.id = ?");
$stmt->execute([$id]);
$order = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$order) {
die("Order not found.");
}
// Fetch Order Items
$stmt = $pdo->prepare("SELECT oi.*, p.name as product_name, pv.name as variant_name
FROM order_items oi
JOIN products p ON oi.product_id = p.id
LEFT JOIN product_variants pv ON oi.variant_id = pv.id
WHERE oi.order_id = ?");
$stmt->execute([$id]);
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
include 'includes/header.php';
// Calculate subtotal from items to be sure
$subtotal = 0;
foreach ($items as $item) {
$subtotal += $item['unit_price'] * $item['quantity'];
}
?>
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="fw-bold mb-0">Order #<?= $order['id'] ?></h2>
<p class="text-muted mb-0">Placed on <?= date('M d, Y H:i', strtotime($order['created_at'])) ?></p>
</div>
<div class="d-flex gap-2">
<a href="orders.php" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Back to List
</a>
<button onclick="printThermalReceipt()" class="btn btn-warning border-warning"><i class="bi bi-printer-fill"></i> Thermal Receipt</button>
<button onclick="window.print()" class="btn btn-light border">
<i class="bi bi-printer"></i> Print Receipt
</button>
<?php if (has_permission('orders_add')): ?>
<a href="order_edit.php?id=<?= $order['id'] ?>" class="btn btn-primary">
<i class="bi bi-pencil"></i> Edit Order
</a>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col-md-8">
<!-- Order Items -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3">
<h5 class="card-title mb-0 fw-bold">Order Items</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead class="bg-light text-muted small text-uppercase">
<tr>
<th class="ps-4">Product</th>
<th class="text-center">Price</th>
<th class="text-center">Qty</th>
<th class="text-end pe-4">Total</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<tr>
<td class="ps-4">
<div class="fw-bold text-dark"><?= htmlspecialchars($item['product_name']) ?></div>
<?php if ($item['variant_name']): ?>
<small class="text-muted">Variant: <?= htmlspecialchars($item['variant_name']) ?></small>
<?php endif; ?>
</td>
<td class="text-center"><?= format_currency($item['unit_price']) ?></td>
<td class="text-center"><?= $item['quantity'] ?></td>
<td class="text-end pe-4 fw-bold"><?= format_currency($item['unit_price'] * $item['quantity']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot class="bg-light">
<tr>
<td colspan="3" class="text-end py-3 ps-4">
<div class="text-muted mb-1">Subtotal</div>
<?php if ($order['discount'] > 0): ?>
<div class="text-muted mb-1">Discount</div>
<?php endif; ?>
<div class="text-muted mb-1">VAT / Tax</div>
<h5 class="fw-bold mb-0 text-dark">Total Amount</h5>
</td>
<td class="text-end py-3 pe-4">
<div class="mb-1"><?= format_currency($subtotal) ?></div>
<?php if ($order['discount'] > 0): ?>
<div class="mb-1 text-danger">-<?= format_currency($order['discount']) ?></div>
<?php endif; ?>
<div class="mb-1"><?= format_currency(0) ?></div>
<h5 class="fw-bold mb-0 text-primary"><?= format_currency($order['total_amount']) ?></h5>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<!-- Additional Info -->
<div class="card border-0 shadow-sm">
<div class="card-body">
<h5 class="fw-bold mb-3">Internal Notes</h5>
<p class="text-muted"><?= htmlspecialchars($order['notes'] ?? 'No notes provided for this order.') ?></p>
</div>
</div>
</div>
<div class="col-md-4">
<!-- Status & Payment -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h6 class="text-muted small text-uppercase fw-bold mb-3">Order Status</h6>
<div class="d-flex align-items-center mb-4">
<span class="badge rounded-pill fs-6 px-3 py-2 status-<?= $order['status'] ?>">
<?= ucfirst($order['status']) ?>
</span>
<span class="ms-3 text-muted small">Last updated: <?= date('M d, H:i', strtotime($order['updated_at'] ?? $order['created_at'])) ?></span>
</div>
<hr>
<h6 class="text-muted small text-uppercase fw-bold mb-3 mt-4">Payment Information</h6>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Method:</span>
<span class="fw-bold text-dark"><?= htmlspecialchars($order['payment_type_name'] ?? 'Unpaid') ?></span>
</div>
<div class="d-flex justify-content-between">
<span class="text-muted">Status:</span>
<span class="badge bg-success bg-opacity-10 text-success border border-success">Paid</span>
</div>
</div>
</div>
<!-- Order Details -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h6 class="text-muted small text-uppercase fw-bold mb-3">Order Details</h6>
<div class="mb-3">
<label class="text-muted small d-block">Outlet</label>
<div class="fw-bold"><?= htmlspecialchars($order['outlet_name'] ?? 'N/A') ?></div>
</div>
<div class="mb-3">
<label class="text-muted small d-block">Order Type</label>
<div class="fw-bold"><?= ucfirst($order['order_type']) ?></div>
</div>
<?php if ($order['order_type'] === 'dine-in'): ?>
<div class="mb-3">
<label class="text-muted small d-block">Table Number</label>
<div class="fw-bold">Table <?= htmlspecialchars((string)$order['table_number']) ?></div>
</div>
<?php endif; ?>
<div class="mb-0">
<label class="text-muted small d-block">Processed By</label>
<div class="fw-bold"><?= htmlspecialchars($order['created_by_username'] ?? 'System') ?></div>
</div>
</div>
</div>
<!-- Customer Info -->
<div class="card border-0 shadow-sm">
<div class="card-body">
<h6 class="text-muted small text-uppercase fw-bold mb-3">Customer Information</h6>
<?php if ($order['customer_name']): ?>
<div class="d-flex align-items-center mb-3">
<div class="bg-primary bg-opacity-10 text-primary p-2 rounded-circle me-3">
<i class="bi bi-person fs-4"></i>
</div>
<div>
<div class="fw-bold"><?= htmlspecialchars($order['customer_name']) ?></div>
<small class="text-muted">Customer ID: #<?= $order['customer_id'] ?></small>
</div>
</div>
<?php if ($order['customer_phone']): ?>
<div class="mb-2">
<i class="bi bi-telephone text-muted me-2"></i>
<a href="tel:<?= $order['customer_phone'] ?>" class="text-decoration-none text-dark"><?= htmlspecialchars($order['customer_phone'] ?? '') ?></a>
</div>
<?php endif; ?>
<?php if ($order['customer_email']): ?>
<div class="mb-0">
<i class="bi bi-envelope text-muted me-2"></i>
<a href="mailto:<?= $order['customer_email'] ?>" class="text-decoration-none text-dark"><?= htmlspecialchars($order['customer_email'] ?? '') ?></a>
</div>
<?php endif; ?>
<?php else: ?>
<div class="text-center py-3">
<i class="bi bi-person-x fs-1 text-muted opacity-25"></i>
<p class="text-muted small mb-0 mt-2">No customer attached to this order (Guest)</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php include 'includes/footer.php'; ?>
<script>
const COMPANY_SETTINGS = <?= json_encode(get_company_settings()) ?>;
const CURRENT_USER = { name: '<?= addslashes($order['created_by_username'] ?? 'System') ?>' };
const CURRENT_OUTLET = { name: '<?= addslashes($order['outlet_name'] ?? 'N/A') ?>' };
const BASE_URL = '<?= get_base_url() ?>';
function formatCurrency(amount) {
const symbol = COMPANY_SETTINGS.currency_symbol || '$';
const decimals = parseInt(COMPANY_SETTINGS.currency_decimals || 2);
return symbol + parseFloat(amount).toFixed(decimals);
}
function printThermalReceipt() {
const data = {
orderId: '<?= $order['id'] ?>',
customer: <?= $order['customer_name'] ? json_encode(['name' => $order['customer_name'], 'phone' => $order['customer_phone'], 'address' => $order['customer_address'] ?? '']) : 'null' ?>,
items: <?= json_encode(array_map(function($i) {
return [
'name' => $i['product_name'],
'variant_name' => $i['variant_name'],
'quantity' => $i['quantity'],
'price' => $i['unit_price']
];
}, $items)) ?>,
total: <?= (float)$order['total_amount'] ?>,
discount: <?= (float)$order['discount'] ?>,
orderType: '<?= $order['order_type'] ?>',
tableNumber: '<?= $order['table_number'] ?>',
date: '<?= date('M d, Y H:i', strtotime($order['created_at'])) ?>',
paymentMethod: '<?= $order['payment_type_name'] ?? 'Unpaid' ?>',
loyaltyRedeemed: <?= ($order['discount'] >= $subtotal && $order['discount'] > 0) ? 'true' : 'false' ?>
};
const width = 400;
const height = 800;
const left = (screen.width - width) / 2;
const top = (screen.height - height) / 2;
const win = window.open('', 'Receipt', `width=${width},height=${height},top=${top},left=${left}`);
const tr = {
'Order': 'الطلب',
'Type': 'النوع',
'Date': 'التاريخ',
'Staff': 'الموظف',
'Table': 'طاولة',
'Payment': 'الدفع',
'ITEM': 'الصنف',
'TOTAL': 'المجموع',
'Subtotal': 'المجموع الفرعي',
'Discount': 'الخصم',
'Tax Included': 'شامل الضريبة',
'THANK YOU FOR YOUR VISIT!': 'شكراً لزيارتكم!',
'Please come again.': 'يرجى زيارتنا مرة أخرى.',
'Customer Details': 'تفاصيل العميل',
'Tel': 'هاتف',
'takeaway': 'سفري',
'dine-in': 'محلي',
'delivery': 'توصيل',
'VAT No': 'الرقم الضريبي',
'CTR No': 'رقم السجل التجاري'
};
const itemsHtml = data.items.map(item => `
<tr>
<td style="padding: 5px 0; border-bottom: 1px solid #eee;">
<div style="font-weight: bold;">${item.name}</div>
${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: right; vertical-align: middle; font-weight: bold;">${formatCurrency(item.quantity * item.price)}</td>
</tr>
`).join('');
const customerHtml = data.customer ? `
<div style="margin-bottom: 10px; border: 1px solid #eee; padding: 8px; border-radius: 4px;">
<div style="font-weight: bold; text-transform: uppercase; font-size: 10px; color: #666; margin-bottom: 3px;">
<span style="float: left;">Customer Details</span>
<span style="float: right;">${tr['Customer Details']}</span>
<div style="clear: both;"></div>
</div>
<div style="font-weight: bold;">${data.customer.name}</div>
${data.customer.phone ? `<div>Tel: ${data.customer.phone}</div>` : ''}
${data.customer.address ? `<div style="font-size: 11px;">${data.customer.address}</div>` : ''}
</div>
` : '';
const tableHtml = data.tableNumber && data.orderType === 'dine-in' ? `
<div style="display: flex; justify-content: space-between;">
<span><strong>Table:</strong> ${data.tableNumber}</span>
<span><strong>${tr['Table']}:</strong> ${data.tableNumber}</span>
</div>` : '';
const paymentHtml = data.paymentMethod ? `
<div style="display: flex; justify-content: space-between;">
<span><strong>Payment:</strong> ${data.paymentMethod}</span>
<span><strong>${tr['Payment']}:</strong> ${data.paymentMethod}</span>
</div>` : '';
const loyaltyHtml = data.loyaltyRedeemed ? `<div style="color: #d63384; font-weight: bold; margin: 5px 0; text-align: center;">* Loyalty Reward Applied *</div>` : '';
const vatRate = parseFloat(COMPANY_SETTINGS.vat_rate) || 0;
const subtotal = data.total + data.discount;
const vatAmount = vatRate > 0 ? (data.total * (vatRate / (100 + vatRate))) : 0;
const logoHtml = COMPANY_SETTINGS.logo_url ? `<img src="${BASE_URL}${COMPANY_SETTINGS.logo_url}" style="max-height: 80px; max-width: 150px; margin-bottom: 10px;">` : '';
const html = `
<html dir="ltr">
<head>
<title>Receipt #${data.orderId}</title>
<style>
body {
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
margin: 0;
padding: 15px;
color: #000;
line-height: 1.4;
}
.header { text-align: center; margin-bottom: 15px; }
.header h2 { margin: 0 0 5px 0; font-size: 20px; font-weight: bold; }
.header div { font-size: 12px; }
table { width: 100%; border-collapse: collapse; }
.divider { border-bottom: 2px dashed #000; margin: 10px 0; }
.thick-divider { border-bottom: 2px solid #000; margin: 10px 0; }
.totals td { padding: 3px 0; }
.footer { text-align: center; margin-top: 25px; font-size: 12px; }
.order-info { font-size: 11px; margin-bottom: 10px; }
.order-info-row { display: flex; justify-content: space-between; margin-bottom: 2px; }
.rtl { direction: rtl; unicode-bidi: embed; }
@media print {
body { width: 80mm; padding: 5mm; }
@page { margin: 0; }
}
</style>
</head>
<body>
<div class="header">
${logoHtml}
<h2>${COMPANY_SETTINGS.company_name}</h2>
<div style="font-weight: bold;">${CURRENT_OUTLET.name}</div>
<div>${COMPANY_SETTINGS.address}</div>
<div>Tel: ${COMPANY_SETTINGS.phone}</div>
${COMPANY_SETTINGS.vat_number ? `<div style="margin-top: 4px;">VAT No / الرقم الضريبي: ${COMPANY_SETTINGS.vat_number}</div>` : ''}
${COMPANY_SETTINGS.ctr_number ? `<div>CTR No / رقم السجل: ${COMPANY_SETTINGS.ctr_number}</div>` : ''}
</div>
<div class="divider"></div>
<div class="order-info">
<div class="order-info-row">
<span><strong>Order:</strong> #${data.orderId}</span>
<span><strong>${tr['Order']}:</strong> #${data.orderId}</span>
</div>
<div class="order-info-row">
<span><strong>Type:</strong> ${data.orderType.toUpperCase()}</span>
<span><strong>${tr['Type']}:</strong> ${tr[data.orderType] || data.orderType}</span>
</div>
<div class="order-info-row">
<span><strong>Date:</strong> ${data.date}</span>
<span><strong>${tr['Date']}:</strong> ${data.date}</span>
</div>
<div class="order-info-row">
<span><strong>Staff:</strong> ${CURRENT_USER.name}</span>
<span><strong>${tr['Staff']}:</strong> ${CURRENT_USER.name}</span>
</div>
</div>
${tableHtml}
${paymentHtml}
${loyaltyHtml}
<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>
<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>
${data.discount > 0 ? `
<tr>
<td>Discount / ${tr['Discount']}</td>
<td style="text-align: right">-${formatCurrency(data.discount)}</td>
</tr>` : ''}
${vatRate > 0 ? `
<tr>
<td style="font-size: 11px;">Tax Incl. (${vatRate}%) / ${tr['Tax Included']}</td>
<td style="text-align: right">${formatCurrency(vatAmount)}</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>
<div class="thick-divider"></div>
<div class="footer">
<div style="font-weight: bold; font-size: 14px; margin-bottom: 2px;">THANK YOU FOR YOUR VISIT!</div>
<div style="font-weight: bold; font-size: 14px; margin-bottom: 5px;" class="rtl">${tr['THANK YOU FOR YOUR VISIT!']}</div>
<div>Please come again.</div>
<div class="rtl">${tr['Please come again.']}</div>
${COMPANY_SETTINGS.email ? `<div style="margin-top: 5px; font-size: 10px;">${COMPANY_SETTINGS.email}</div>` : ''}
<div style="margin-top: 15px; font-size: 10px; color: #888;">Powered by Flatlogic POS</div>
</div>
<script>
window.onload = function() {
window.print();
setTimeout(function() { window.close(); }, 1500);
}
</script>
</body>
</html>
`;
win.document.write(html);
win.document.close();
}
</script>