Autosave: 20260419-042924

This commit is contained in:
Flatlogic Bot 2026-04-19 04:29:17 +00:00
parent ee93351390
commit 02afd76d75
3 changed files with 370 additions and 207 deletions

View File

@ -152,3 +152,18 @@ body.auth-body {
[dir="ltr"] .modal-header .btn-close {
right: 1rem;
}
/* Fix form-select arrow position in RTL mode */
[dir="rtl"] .form-select {
background-position: left 0.75rem center;
padding-right: 0.75rem;
padding-left: 2.25rem;
}
[dir="rtl"] .form-select-sm {
padding-right: 0.5rem;
padding-left: 2rem;
}
[dir="rtl"] .form-select-lg {
padding-right: 1rem;
padding-left: 3rem;
}

525
sale.php
View File

@ -25,177 +25,281 @@ require __DIR__ . '/includes/header.php';
?>
<style>
/* Full Page Borderless Invoice */
.invoice-page {
/* Formal A4 Invoice Styles */
.invoice-container {
background: #fff;
min-height: 80vh;
max-width: 900px; margin: 0 auto; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.08); overflow: hidden;
padding: 4rem 3rem;
position: relative;
}
.invoice-ribbon {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 12px;
background: linear-gradient(90deg, #212529, #6c757d);
max-width: 210mm; /* A4 width */
margin: 2rem auto;
padding: 2cm;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border: 1px solid #ddd;
color: #333;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px; /* Smaller, formal text */
line-height: 1.5;
}
.invoice-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 4rem;
border-bottom: 2px solid #333;
padding-bottom: 1.5rem;
margin-bottom: 2rem;
}
.company-logo-info {
display: flex;
align-items: center;
gap: 1.5rem;
}
.invoice-logo {
width: 80px;
height: 80px;
background: #f8f9fa;
border: 1px solid #eee;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.invoice-logo svg {
width: 50px;
height: 50px;
fill: none;
stroke: #333;
stroke-width: 2;
}
.company-details h2 {
font-weight: 800;
color: #212529;
font-size: 2.5rem;
letter-spacing: -1px;
margin-bottom: 0.5rem;
font-size: 18px;
font-weight: bold;
margin: 0 0 5px 0;
text-transform: uppercase;
color: #000;
}
.company-details p {
color: #adb5bd;
margin-bottom: 0.25rem;
font-size: 1rem;
margin: 2px 0;
color: #555;
}
.invoice-meta {
.invoice-title-meta {
text-align: <?= current_lang() === 'ar' ? 'left' : 'right' ?>;
}
.invoice-meta h1 {
font-size: 4rem;
font-weight: 900;
color: #f8f9fa;
text-transform: uppercase;
letter-spacing: 2px;
line-height: 1;
margin-bottom: 1rem;
}
.meta-box-row {
display: flex;
justify-content: <?= current_lang() === 'ar' ? 'flex-start' : 'flex-end' ?>;
gap: 2rem;
margin-bottom: 0.5rem;
}
.meta-label {
color: #adb5bd;
font-size: 0.9rem;
font-weight: 600;
.invoice-title-meta h1 {
font-size: 24px;
font-weight: bold;
margin: 0 0 15px 0;
text-transform: uppercase;
color: #000;
letter-spacing: 1px;
}
.meta-val {
font-weight: 700;
color: #212529;
font-size: 1.1rem;
.meta-grid {
display: grid;
grid-template-columns: auto auto;
gap: 5px 15px;
text-align: <?= current_lang() === 'ar' ? 'right' : 'left' ?>;
}
<?php if (current_lang() === 'ar'): ?>
.meta-grid { direction: rtl; }
<?php endif; ?>
.meta-label {
font-weight: bold;
color: #666;
}
.meta-val {
font-weight: bold;
color: #000;
}
.invoice-parties {
display: flex;
justify-content: space-between;
margin-bottom: 4rem;
padding-top: 3rem;
border-top: 1px dashed #dee2e6;
margin-bottom: 2rem;
}
.party-box {
flex: 1;
width: 48%;
}
.party-title {
font-size: 0.85rem;
color: #adb5bd;
font-weight: 700;
font-weight: bold;
border-bottom: 1px solid #ccc;
padding-bottom: 5px;
margin-bottom: 10px;
color: #000;
text-transform: uppercase;
margin-bottom: 1rem;
letter-spacing: 1px;
}
.party-info h4 {
font-weight: 800;
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: #212529;
font-size: 14px;
font-weight: bold;
margin: 0 0 5px 0;
color: #000;
}
.party-info p {
color: #6c757d;
margin-bottom: 0;
font-size: 1.1rem;
margin: 0;
color: #555;
}
.invoice-table {
.invoice-table-wrapper {
margin-bottom: 2rem;
}
.formal-table {
width: 100%;
margin-bottom: 4rem;
border-collapse: collapse;
}
.invoice-table th {
color: #adb5bd;
font-weight: 600;
padding: 1rem 0;
border-bottom: 2px solid #212529;
.formal-table th,
.formal-table td {
border: 1px solid #ccc;
padding: 8px 10px;
vertical-align: top;
}
.formal-table th {
background-color: #f9f9f9;
font-weight: bold;
color: #000;
text-transform: uppercase;
font-size: 0.85rem;
letter-spacing: 1px;
}
.invoice-table td {
padding: 1.5rem 0;
border-bottom: 1px solid #f8f9fa;
vertical-align: middle;
.formal-table th.text-right,
.formal-table td.text-right {
text-align: right;
}
.invoice-table tr:last-child td { border-bottom: 1px solid #dee2e6; }
.totals-section {
display: flex;
justify-content: flex-end;
margin-bottom: 4rem;
.formal-table th.text-center,
.formal-table td.text-center {
text-align: center;
}
.totals-box {
width: 100%;
max-width: 400px;
.item-name {
font-weight: bold;
color: #000;
margin-bottom: 3px;
}
.totals-row {
.item-sku {
color: #666;
font-size: 11px;
}
.invoice-summary {
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
font-size: 1.2rem;
color: #6c757d;
align-items: flex-start;
}
.totals-row.grand-total {
font-size: 2.5rem;
font-weight: 900;
color: #212529;
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 2px solid #212529;
margin-bottom: 0;
line-height: 1.2;
.invoice-notes {
width: 50%;
font-size: 12px;
}
.notes-title {
font-weight: bold;
text-transform: uppercase;
margin-bottom: 5px;
}
.totals-box {
width: 40%;
}
.totals-table {
width: 100%;
border-collapse: collapse;
}
.totals-table td {
padding: 6px 10px;
border-bottom: 1px solid #eee;
}
.totals-table tr:last-child td {
border-bottom: none;
}
.total-label {
font-weight: bold;
color: #666;
}
.total-amount {
text-align: right;
font-weight: bold;
color: #000;
}
.grand-total-row td {
font-size: 16px;
border-top: 2px solid #000;
padding-top: 10px;
}
.grand-total-row .total-label {
color: #000;
text-transform: uppercase;
}
.invoice-footer {
margin-top: 3rem;
padding-top: 1.5rem;
border-top: 1px solid #ccc;
display: flex;
justify-content: space-between;
align-items: flex-end;
color: #adb5bd;
font-size: 0.95rem;
border-top: 1px dashed #dee2e6;
padding-top: 3rem;
font-size: 12px;
color: #666;
}
.qr-placeholder {
width: 120px;
height: 120px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect width="100" height="100" fill="%23f8f9fa"/><path d="M20 20h20v20H20zM60 20h20v20H60zM20 60h20v20H20zM50 50h10v10H50zM70 60h10v10H70zM60 70h10v10H60z" fill="%23dee2e6"/></svg>') center/cover;
.qr-code {
width: 80px;
height: 80px;
background: #fff;
border: 1px solid #ccc;
padding: 5px;
}
.qr-code img {
width: 100%;
height: 100%;
display: block;
}
.print-actions {
position: sticky;
top: 1rem;
z-index: 100;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
padding: 1rem;
border-radius: 100px;
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
background: rgba(255, 255, 255, 0.95);
padding: 15px 25px;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
border: 1px solid #ddd;
max-width: 210mm;
margin: 0 auto 2rem auto;
}
/* Print Styles */
@media print {
body { background: #fff; margin: 0; padding: 0; }
.main-sidebar, .main-header, .print-actions, .alert { display: none !important; }
body { background: #fff; }
.main-sidebar, .main-header, .print-actions, .alert, .footer-section { display: none !important; }
.main-content { margin: 0 !important; padding: 0 !important; width: 100% !important; }
.invoice-page { padding: 0; margin: 0; min-height: auto; }
@page { size: A4; margin: 1.5cm; }
.invoice-container {
box-shadow: none;
border: none;
margin: 0;
padding: 0;
max-width: 100%;
width: 100%;
}
@page { size: A4; margin: 15mm; }
}
</style>
@ -203,52 +307,54 @@ require __DIR__ . '/includes/header.php';
<?php if ($dbError): ?>
<div class="alert alert-warning"><?= h($dbError) ?></div>
<?php elseif (!$sale): ?>
<div class="empty-state bg-white rounded-4 shadow-sm p-5 text-center">
<i class="bi bi-file-earmark-x fs-1 text-muted d-block mb-3"></i>
<div class="alert alert-info text-center mt-4">
<h4><?= h(tr('الفاتورة غير موجودة', 'Sale not found')) ?></h4>
<p class="text-muted"><?= h(tr('قد تكون الفاتورة خارج صلاحية هذا الحساب أو لم تعد موجودة.', 'The sale may be outside this account scope or no longer exists.')) ?></p>
<a class="btn btn-outline-secondary mt-3 rounded-pill px-4" href="<?= h(url_for('sales.php')) ?>"><?= h(tr('العودة إلى المبيعات', 'Back to sales')) ?></a>
<p><?= h(tr('قد تكون الفاتورة خارج صلاحية هذا الحساب أو لم تعد موجودة.', 'The sale may be outside this account scope or no longer exists.')) ?></p>
<a class="btn btn-outline-secondary mt-3" href="<?= h(url_for('sales.php')) ?>"><?= h(tr('العودة إلى المبيعات', 'Back to sales')) ?></a>
</div>
<?php else: ?>
<!-- Print Actions (Hidden when printing) -->
<div class="print-actions d-flex justify-content-between align-items-center mb-4 mx-auto" style="max-width: 800px;">
<a href="sales.php" class="btn btn-link text-muted text-decoration-none">
<!-- Print Actions -->
<div class="print-actions d-flex justify-content-between align-items-center">
<a href="sales.php" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-<?= current_lang() === 'ar' ? 'right' : 'left' ?> me-1"></i> <?= h(tr('رجوع للسجل', 'Back to ledger')) ?>
</a>
<button onclick="window.print()" class="btn btn-dark rounded-pill px-4 shadow-sm fs-6">
<i class="bi bi-printer me-2"></i><?= h(tr('طباعة الفاتورة (A4)', 'Print Invoice (A4)')) ?>
<button onclick="window.print()" class="btn btn-dark btn-sm px-4">
<i class="bi bi-printer me-2"></i><?= h(tr('طباعة الفاتورة', 'Print Invoice')) ?>
</button>
</div>
<!-- Invoice A4 Document -->
<div class="invoice-page">
<div class="invoice-ribbon"></div>
<div class="invoice-header flex-column flex-md-row">
<div class="company-details mb-4 mb-md-0">
<h2><?= h($companyName) ?></h2>
<p><?= h($companyAddress) ?></p>
<p>VAT NO: <?= h($companyVat) ?></p>
<p><?= h($companyEmail) ?> &nbsp;&bull;&nbsp; <?= h($companyPhone) ?></p>
<!-- Formal A4 Invoice -->
<div class="invoice-container">
<div class="invoice-header">
<div class="company-logo-info">
<div class="invoice-logo">
<!-- Formal Box Logo Placeholder -->
<svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="company-details">
<h2><?= h($companyName) ?></h2>
<p><?= h($companyAddress) ?></p>
<p><?= h(tr('الرقم الضريبي:', 'VAT NO:')) ?> <?= h($companyVat) ?></p>
<p><?= h($companyPhone) ?> | <?= h($companyEmail) ?></p>
</div>
</div>
<div class="invoice-meta">
<div class="invoice-title-meta">
<h1><?= ($sale['status'] ?? 'completed') === 'order' ? h(tr('طلب حجز', 'ORDER')) : h(tr('فاتورة ضريبية', 'TAX INVOICE')) ?></h1>
<div class="meta-box-row">
<span class="meta-label"><?= h(tr('رقم الفاتورة', 'Invoice No.')) ?></span>
<span class="meta-val">#<?= h($sale['receipt_no']) ?></span>
</div>
<div class="meta-box-row">
<span class="meta-label"><?= h(tr('تاريخ الإصدار', 'Date Issued')) ?></span>
<span class="meta-val"><?= h(date('Y-m-d', strtotime((string) $sale['sale_date']))) ?></span>
</div>
<div class="meta-box-row">
<span class="meta-label"><?= h(tr('طريقة الدفع', 'Payment')) ?></span>
<span class="meta-val"><?= h(ucfirst((string) $sale['payment_method'])) ?></span>
</div>
<div class="meta-box-row">
<span class="meta-label"><?= h(tr('الحالة', 'Status')) ?></span>
<span class="meta-val"><?= ($sale['status'] ?? 'completed') === 'order' ? h(tr('طلب حجز (غير مدفوع)\", 'Order (Unpaid)\")) : h(tr('مدفوعة', 'Paid')) ?></span>
<div class="meta-grid">
<div class="meta-label"><?= h(tr('رقم الفاتورة', 'Invoice No.')) ?>:</div>
<div class="meta-val">#<?= h($sale['receipt_no']) ?></div>
<div class="meta-label"><?= h(tr('تاريخ الإصدار', 'Date Issued')) ?>:</div>
<div class="meta-val"><?= h(date('Y-m-d H:i', strtotime((string) $sale['sale_date']))) ?></div>
<div class="meta-label"><?= h(tr('طريقة الدفع', 'Payment')) ?>:</div>
<div class="meta-val"><?= h(ucfirst((string) $sale['payment_method'])) ?></div>
<div class="meta-label"><?= h(tr('الحالة', 'Status')) ?>:</div>
<div class="meta-val"><?= ($sale['status'] ?? 'completed') === 'order' ? h(tr('غير مدفوعة', 'Unpaid')) : h(tr('مدفوعة', 'Paid')) ?></div>
</div>
</div>
</div>
@ -258,72 +364,91 @@ require __DIR__ . '/includes/header.php';
<div class="party-title"><?= h(tr('فاتورة إلى', 'Invoice To')) ?></div>
<div class="party-info">
<h4><?= h((string) ($sale['customer_name'] ?: tr('عميل نقدي', 'Walk-in Customer'))) ?></h4>
<p><?= h(tr('الفرع:', 'Branch:')) ?> <?= h(branch_label((string) $sale['branch_code'])) ?></p>
</div>
</div>
<div class="party-box text-<?= current_lang() === 'ar' ? 'left' : 'right' ?>">
<div class="party-title"><?= h(tr('بواسطة', 'Served By')) ?></div>
<div class="party-box">
<div class="party-title"><?= h(tr('معلومات المتجر', 'Store Details')) ?></div>
<div class="party-info">
<h4><?= h((string) $sale['cashier_name']) ?></h4>
<p><?= h(tr('موظف مبيعات', 'Sales Rep')) ?></p>
<p><strong><?= h(tr('الفرع:', 'Branch:')) ?></strong> <?= h(branch_label((string) $sale['branch_code'])) ?></p>
<p><strong><?= h(tr('بواسطة:', 'Served By:')) ?></strong> <?= h((string) $sale['cashier_name']) ?></p>
</div>
</div>
</div>
<table class="invoice-table">
<thead>
<tr>
<th width="50%" class="text-<?= current_lang() === 'ar' ? 'right' : 'left' ?>"><?= h(tr('وصف الصنف', 'Item Description')) ?></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-<?= current_lang() === 'ar' ? 'left' : 'right' ?>"><?= h(tr('الإجمالي', 'Line Total')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($sale['items'] as $item): ?>
<div class="invoice-table-wrapper">
<table class="formal-table">
<thead>
<tr>
<td>
<div class="fw-bold fs-5 text-dark"><?= h(current_lang() === 'ar' ? ($item['name_ar'] ?? $item['sku']) : ($item['name_en'] ?? $item['sku'])) ?></div>
<div class="text-muted">SKU: <?= h($item['sku']) ?></div>
</td>
<td class="text-center fs-5"><?= h(number_format((float) ($item['price'] ?? 0), 3)) ?></td>
<td class="text-center fs-5 fw-bold"><?= h((string) ($item['qty'] ?? 0)) ?></td>
<td class="text-<?= current_lang() === 'ar' ? 'left' : 'right' ?> fs-4 fw-bold text-dark"><?= h(number_format((float) ($item['line_total'] ?? 0), 3)) ?></td>
<th width="5%" class="text-center">#</th>
<th width="45%" class="<?= current_lang() === 'ar' ? 'text-right' : 'text-left' ?>"><?= h(tr('وصف الصنف', 'Item Description')) ?></th>
<th width="15%" class="text-center"><?= h(tr('الكمية', 'Qty')) ?></th>
<th width="15%" class="text-center"><?= h(tr('سعر الوحدة', 'Unit Price')) ?></th>
<th width="20%" class="<?= current_lang() === 'ar' ? 'text-left' : 'text-right' ?>"><?= h(tr('الإجمالي', 'Line Total')) ?></th>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="totals-section">
<div class="totals-box">
<div class="totals-row">
<span><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></span>
<span class="text-dark fw-bold"><?= h(number_format((float) $sale['subtotal'], 3)) ?></span>
</div>
<div class="totals-row">
<span><?= h(tr('ضريبة القيمة المضافة (15%)', 'VAT (15%)')) ?></span>
<span class="text-success"><?= h(tr('شامل', 'Inclusive')) ?></span>
</div>
<div class="totals-row grand-total">
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
<span><?= h(number_format((float) $sale['total_amount'], 3)) ?> <small class="fs-5 text-muted"><?= h(tr('ر.ع', 'OMR')) ?></small></span>
</div>
</div>
</thead>
<tbody>
<?php $i = 1; foreach ($sale['items'] as $item): ?>
<tr>
<td class="text-center"><?= $i++ ?></td>
<td>
<div class="item-name"><?= h(current_lang() === 'ar' ? ($item['name_ar'] ?? $item['sku']) : ($item['name_en'] ?? $item['sku'])) ?></div>
<div class="item-sku"><?= h(tr('رمز الصنف:', 'SKU:')) ?> <?= h($item['sku']) ?></div>
</td>
<td class="text-center"><?= h((string) ($item['qty'] ?? 0)) ?></td>
<td class="text-center"><?= h(number_format((float) ($item['price'] ?? 0), 3)) ?></td>
<td class="<?= current_lang() === 'ar' ? 'text-left' : 'text-right' ?>"><?= h(number_format((float) ($item['line_total'] ?? 0), 3)) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (!empty($sale['notes'])): ?>
<div class="mb-5 px-4 py-3 bg-light" style="border-left: 4px solid #dee2e6;">
<strong class="text-uppercase text-muted fs-6 d-block mb-1"><?= h(tr('ملاحظات', 'Notes')) ?></strong>
<span class="fs-5 text-dark"><?= h((string) $sale['notes']) ?></span>
<div class="invoice-summary">
<div class="invoice-notes">
<?php if (!empty($sale['notes'])): ?>
<div class="notes-title"><?= h(tr('ملاحظات', 'Notes')) ?></div>
<p><?= nl2br(h((string) $sale['notes'])) ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="totals-box">
<table class="totals-table">
<tr>
<td class="total-label"><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></td>
<td class="total-amount"><?= h(number_format((float) $sale['subtotal'], 3)) ?></td>
</tr>
<tr>
<td class="total-label"><?= h(tr('ضريبة القيمة المضافة (15%)', 'VAT (15%)')) ?></td>
<td class="total-amount"><?= h(tr('شامل', 'Inclusive')) ?></td>
</tr>
<tr class="grand-total-row">
<td class="total-label"><?= h(tr('الإجمالي', 'Total')) ?></td>
<td class="total-amount"><?= h(number_format((float) $sale['total_amount'], 3)) ?> <?= h(tr('ر.ع', 'OMR')) ?></td>
</tr>
</table>
</div>
</div>
<div class="invoice-footer">
<div>
<h5 class="fw-bold text-dark mb-1"><?= h(tr('شكراً لتعاملكم معنا!', 'Thank you for your business!')) ?></h5>
<p class="mb-0"><?= h(tr('هذه الفاتورة معتمدة ضريبياً، يُرجى الاحتفاظ بها لضمان حقوقك.', 'This is a certified tax invoice. Please keep it for your records.')) ?></p>
<div class="footer-text">
<p style="margin-bottom: 5px; font-weight: bold; color: #000;">
<?= h(tr('شكراً لتعاملكم معنا!', 'Thank you for your business!')) ?>
</p>
<p style="margin: 0;">
<?= h(tr('هذه الفاتورة معتمدة ضريبياً، يُرجى الاحتفاظ بها لضمان حقوقك.', 'This is a certified tax invoice. Please keep it for your records.')) ?>
</p>
</div>
<!-- Simple QR code simulation using an inline SVG to keep it formal and clean -->
<div class="qr-code">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect width="100" height="100" fill="#fff"/>
<path d="M10 10h20v20H10zM70 10h20v20H70zM10 70h20v20H10z" fill="#000"/>
<path d="M15 15h10v10H15zM75 15h10v10H75zM15 75h10v10H15z" fill="#fff"/>
<path d="M35 10h10v10H35zM50 10h10v10H50zM35 25h10v10H35zM50 25h20v10H50zM75 35h15v10H75zM10 35h20v10H10zM35 40h10v20H35zM50 50h20v20H50zM10 50h15v10H10zM75 60h15v30H75zM35 70h10v20H35zM50 80h10v10H50z" fill="#000"/>
</svg>
</div>
<div class="qr-placeholder"></div>
</div>
</div>

View File

@ -7,6 +7,16 @@ $activeNav = 'sales';
$mode = isset($_GET['mode']) && in_array($_GET['mode'], ['pos', 'normal'], true) ? $_GET['mode'] : null;
$branch = isset($_GET['branch']) && array_key_exists($_GET['branch'], branches()) ? $_GET['branch'] : null;
$search = $_GET['q'] ?? '';
$statusFilter = $_GET['status'] ?? '';
if (isset($_GET['mark_paid']) && is_numeric($_GET['mark_paid'])) {
try {
$id = (int)$_GET['mark_paid'];
db()->prepare("UPDATE sales_orders SET status = 'completed' WHERE id = ?")->execute([$id]);
} catch(Throwable $e) {}
header("Location: sales.php");
exit;
}
$dbError = null;
$sales = [];
@ -37,6 +47,12 @@ try {
$where .= ' AND (receipt_no LIKE :search OR cashier_name LIKE :search OR customer_name LIKE :search)';
$params[':search'] = "%$search%";
}
if ($statusFilter === 'order') {
$where .= " AND status = 'order' ";
} elseif ($statusFilter === 'completed') {
$where .= " AND status = 'completed' ";
}
// Pagination counts
$countSql = 'SELECT COUNT(*) FROM sales_orders' . $where;
@ -72,9 +88,9 @@ require __DIR__ . '/includes/header.php';
<div class="small text-muted"><?= h(tr('ابحث بصرياً في أحدث المبيعات مع صلاحيات حسب الدور والفرع.', 'Scan the latest sales with role and branch scoping.')) ?></div>
</div>
<div class="d-flex gap-2 flex-wrap">
<a class="btn btn-sm <?= $mode === null ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('sales.php', ['q' => $search])) ?>"><?= h(tr('الكل', 'All')) ?></a>
<a class="btn btn-sm <?= $mode === 'pos' ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('sales.php', ['mode' => 'pos', 'q' => $search])) ?>">POS</a>
<a class="btn btn-sm <?= $mode === 'normal' ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('sales.php', ['mode' => 'normal', 'q' => $search])) ?>"><?= h(tr('بيع عادي', 'Normal')) ?></a>
<a class="btn btn-sm <?= $mode === null ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('sales.php', ['q' => $search, 'status' => $statusFilter])) ?>"><?= h(tr('الكل', 'All')) ?></a>
<a class="btn btn-sm <?= $mode === 'pos' ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('sales.php', ['mode' => 'pos', 'q' => $search, 'status' => $statusFilter])) ?>">POS</a>
<a class="btn btn-sm <?= $mode === 'normal' ? 'btn-dark' : 'btn-outline-secondary' ?>" href="<?= h(url_for('sales.php', ['mode' => 'normal', 'q' => $search, 'status' => $statusFilter])) ?>"><?= h(tr('بيع عادي', 'Normal')) ?></a>
</div>
</div>
@ -82,8 +98,13 @@ require __DIR__ . '/includes/header.php';
<?php if($mode): ?>
<input type="hidden" name="mode" value="<?= h($mode) ?>">
<?php endif; ?>
<div class="input-group" style="max-width: 400px;">
<input type="text" name="q" class="form-control" placeholder="<?= h(tr('بحث بالإيصال أو الكاشير...', 'Search receipt or cashier...')) ?>" value="<?= h($search) ?>">
<div class="input-group" style="max-width: 600px;">
<select name="status" class="form-select" style="max-width: 150px;">
<option value=""><?= h(tr('كل الحالات', 'All Statuses')) ?></option>
<option value="completed" <?= $statusFilter === 'completed' ? 'selected' : '' ?>><?= h(tr('مدفوع', 'Paid')) ?></option>
<option value="order" <?= $statusFilter === 'order' ? 'selected' : '' ?>><?= h(tr('طلب حجز', 'Order')) ?></option>
</select>
<input type="text" name="q" class="form-control" placeholder="<?= h(tr('بحث بالإيصال، الكاشير، العميل أو الهاتف...', 'Search receipt, cashier, customer or phone...')) ?>" value="<?= h($search) ?>">
<button class="btn btn-outline-secondary" type="submit"><i class="bi bi-search"></i></button>
</div>
</form>
@ -108,6 +129,7 @@ require __DIR__ . '/includes/header.php';
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الفرع', 'Branch')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('النوع', 'Type')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الكاشير', 'Cashier')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('العميل', 'Customer')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الإجمالي', 'Total')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الحالة', 'Status')) ?></th>
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('التاريخ', 'Date')) ?></th>
@ -124,6 +146,7 @@ require __DIR__ . '/includes/header.php';
<td><?= h(branch_label((string) $sale['branch_code'])) ?></td>
<td><span class="badge text-bg-light border"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span></td>
<td><?= h((string) $sale['cashier_name']) ?></td>
<td><?= h((string) ($sale['customer_name'] ?: '-')) ?></td>
<td class="fw-semibold"><?= h(currency((float) $sale['total_amount'])) ?></td>
<td>
<?php if (($sale['status'] ?? 'completed') === 'order'): ?>
@ -160,7 +183,7 @@ require __DIR__ . '/includes/header.php';
<ul class="pagination justify-content-center mb-0">
<?php for($i=1; $i<=$totalPages; $i++): ?>
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
<a class="page-link" href="<?= h(url_for('sales.php', ['p' => $i, 'q' => $search, 'mode' => $mode])) ?>"><?= $i ?></a>
<a class="page-link" href="<?= h(url_for('sales.php', ['p' => $i, 'q' => $search, 'mode' => $mode, 'status' => $statusFilter])) ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
</ul>
@ -217,4 +240,4 @@ function mockDelete() {
}
</script>
<?php require __DIR__ . '/includes/footer.php'; ?>
<?php require __DIR__ . '/includes/footer.php'; ?>