395 lines
20 KiB
PHP
395 lines
20 KiB
PHP
<?php
|
||
require_once __DIR__ . '/includes/app.php';
|
||
$user = require_permission('sales', 'show');
|
||
ensure_sales_table();
|
||
|
||
$activeNav = 'eid_orders';
|
||
$pageTitle = tr('طباعة تجهيزات العيد', 'Print Eid Prep Summary');
|
||
$metaDescription = tr('ملخص قابل للطباعة لتجهيزات طلبات العيد حسب التاريخ والفرع والحالة.', 'Printable Eid preparation summary by date, branch, and status.');
|
||
$metaRobots = 'noindex, nofollow';
|
||
|
||
$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 = trim((string) ($_GET['q'] ?? ''));
|
||
$paymentStatus = trim((string) ($_GET['payment_status'] ?? ''));
|
||
$deliveryStatus = trim((string) ($_GET['delivery_status'] ?? ''));
|
||
$dateFrom = trim((string) ($_GET['date_from'] ?? ''));
|
||
$dateTo = trim((string) ($_GET['date_to'] ?? ''));
|
||
$allowedBranches = $user && $user['role'] !== 'owner' ? get_user_branches($user) : [];
|
||
$deliveryOptions = eid_delivery_status_options();
|
||
$dbError = null;
|
||
$orders = [];
|
||
$itemRows = [];
|
||
$summary = [
|
||
'total_orders' => 0,
|
||
'unique_items' => 0,
|
||
'total_quantity' => 0,
|
||
'total_amount' => 0,
|
||
];
|
||
|
||
try {
|
||
$params = [':order_type' => 'eid'];
|
||
$where = ' WHERE order_type = :order_type ';
|
||
|
||
if ($mode) {
|
||
$where .= ' AND sale_mode = :sale_mode ';
|
||
$params[':sale_mode'] = $mode;
|
||
}
|
||
|
||
if ($branch) {
|
||
$where .= ' AND branch_code = :branch_code ';
|
||
$params[':branch_code'] = $branch;
|
||
}
|
||
|
||
if ($user && $user['role'] !== 'owner') {
|
||
if ($allowedBranches === []) {
|
||
$where .= ' AND 1=0 ';
|
||
} else {
|
||
$namedParams = [];
|
||
foreach ($allowedBranches as $i => $allowedBranch) {
|
||
$key = ':v_branch_' . $i;
|
||
$namedParams[] = $key;
|
||
$params[$key] = $allowedBranch;
|
||
}
|
||
$where .= ' AND branch_code IN (' . implode(', ', $namedParams) . ') ';
|
||
}
|
||
}
|
||
|
||
if ($search !== '') {
|
||
$where .= ' AND (receipt_no LIKE :search OR customer_name LIKE :search OR cashier_name LIKE :search OR notes LIKE :search) ';
|
||
$params[':search'] = '%' . $search . '%';
|
||
}
|
||
|
||
if (in_array($paymentStatus, ['paid', 'partial', 'unpaid'], true)) {
|
||
$where .= ' AND payment_status = :payment_status ';
|
||
$params[':payment_status'] = $paymentStatus;
|
||
}
|
||
|
||
if (isset($deliveryOptions[$deliveryStatus])) {
|
||
$where .= ' AND delivery_status = :delivery_status ';
|
||
$params[':delivery_status'] = $deliveryStatus;
|
||
}
|
||
|
||
if ($dateFrom !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $dateFrom)) {
|
||
$where .= ' AND DATE(COALESCE(delivery_date, sale_date)) >= :date_from ';
|
||
$params[':date_from'] = $dateFrom;
|
||
} else {
|
||
$dateFrom = '';
|
||
}
|
||
|
||
if ($dateTo !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $dateTo)) {
|
||
$where .= ' AND DATE(COALESCE(delivery_date, sale_date)) <= :date_to ';
|
||
$params[':date_to'] = $dateTo;
|
||
} else {
|
||
$dateTo = '';
|
||
}
|
||
|
||
$sql = 'SELECT * FROM sales_orders' . $where . ' ORDER BY COALESCE(delivery_date, DATE(sale_date)) ASC, sale_date ASC';
|
||
$stmt = db()->prepare($sql);
|
||
foreach ($params as $key => $value) {
|
||
$stmt->bindValue($key, $value);
|
||
}
|
||
$stmt->execute();
|
||
$orders = $stmt->fetchAll();
|
||
|
||
$itemIndex = [];
|
||
foreach ($orders as &$order) {
|
||
$decodedItems = json_decode((string) ($order['items_json'] ?? '[]'), true);
|
||
$items = is_array($decodedItems) ? $decodedItems : [];
|
||
$order['items'] = $items;
|
||
|
||
foreach ($items as $item) {
|
||
$sku = trim((string) ($item['sku'] ?? ''));
|
||
$name = trim((string) ($item['name_ar'] ?? ''));
|
||
if ($name === '') {
|
||
$name = trim((string) ($item['name_en'] ?? ''));
|
||
}
|
||
if ($name === '') {
|
||
$name = $sku !== '' ? $sku : tr('صنف بدون اسم', 'Unnamed item');
|
||
}
|
||
|
||
$key = $sku !== '' ? $sku : md5($name);
|
||
$qty = max(0, (float) ($item['qty'] ?? 0));
|
||
|
||
if (!isset($itemIndex[$key])) {
|
||
$itemIndex[$key] = [
|
||
'sku' => $sku,
|
||
'name' => $name,
|
||
'qty' => 0.0,
|
||
'order_count' => 0,
|
||
];
|
||
}
|
||
|
||
$itemIndex[$key]['qty'] += $qty;
|
||
$itemIndex[$key]['order_count']++;
|
||
}
|
||
}
|
||
unset($order);
|
||
|
||
usort($orders, static function (array $a, array $b): int {
|
||
$aDate = (string) ($a['delivery_date'] ?: substr((string) ($a['sale_date'] ?? ''), 0, 10));
|
||
$bDate = (string) ($b['delivery_date'] ?: substr((string) ($b['sale_date'] ?? ''), 0, 10));
|
||
return [$aDate, (string) ($a['receipt_no'] ?? '')] <=> [$bDate, (string) ($b['receipt_no'] ?? '')];
|
||
});
|
||
|
||
$itemRows = array_values($itemIndex);
|
||
usort($itemRows, static function (array $a, array $b): int {
|
||
if ($a['qty'] === $b['qty']) {
|
||
return strcasecmp((string) $a['name'], (string) $b['name']);
|
||
}
|
||
return $b['qty'] <=> $a['qty'];
|
||
});
|
||
|
||
$summary['total_orders'] = count($orders);
|
||
$summary['unique_items'] = count($itemRows);
|
||
$summary['total_quantity'] = array_sum(array_map(static fn(array $row): float => (float) $row['qty'], $itemRows));
|
||
$summary['total_amount'] = array_sum(array_map(static fn(array $row): float => (float) ($row['total_amount'] ?? 0), $orders));
|
||
} catch (Throwable $e) {
|
||
$dbError = $e->getMessage();
|
||
}
|
||
|
||
$filterParams = array_filter([
|
||
'q' => $search,
|
||
'branch' => $branch,
|
||
'mode' => $mode,
|
||
'payment_status' => $paymentStatus,
|
||
'delivery_status' => $deliveryStatus,
|
||
'date_from' => $dateFrom,
|
||
'date_to' => $dateTo,
|
||
], static fn($value) => $value !== null && $value !== '');
|
||
|
||
$generatedAt = date('Y-m-d H:i');
|
||
require __DIR__ . '/includes/header.php';
|
||
?>
|
||
<style>
|
||
.eid-print-shell { max-width: 1180px; margin: 0 auto; }
|
||
.eid-print-card {
|
||
background: #fff;
|
||
border: 1px solid #e8ecf4;
|
||
border-radius: 16px;
|
||
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.06);
|
||
}
|
||
.eid-print-card .table th { white-space: nowrap; }
|
||
.eid-kpi {
|
||
border: 1px solid #e9eef8;
|
||
border-radius: 14px;
|
||
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
|
||
padding: 1rem 1.1rem;
|
||
height: 100%;
|
||
}
|
||
.eid-kpi-label { color: #6b7280; font-size: 0.92rem; margin-bottom: 0.35rem; }
|
||
.eid-kpi-value { font-size: 2rem; font-weight: 700; color: #111827; }
|
||
.eid-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.35rem;
|
||
border: 1px solid #dbe3ef;
|
||
border-radius: 999px;
|
||
padding: 0.4rem 0.8rem;
|
||
background: #f8fafc;
|
||
color: #334155;
|
||
font-size: 0.88rem;
|
||
}
|
||
@media print {
|
||
body { background: #fff !important; }
|
||
.main-sidebar, .main-header, .footer-section, .d-print-none, .alert-dismissible .btn-close { display: none !important; }
|
||
.main-content { margin: 0 !important; padding: 0 !important; }
|
||
.eid-print-card { box-shadow: none; border: none; }
|
||
.table th { background: #f3f4f6 !important; -webkit-print-color-adjust: exact; color: #111 !important; }
|
||
}
|
||
</style>
|
||
|
||
<div class="eid-print-shell py-4">
|
||
<section class="eid-print-card p-4 p-lg-5 mb-4">
|
||
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap mb-4 d-print-none">
|
||
<div>
|
||
<h1 class="h3 mb-1"><i class="bi bi-printer me-2"></i><?= h(tr('ملخص تجهيزات طلبات العيد', 'Eid Order Prep Summary')) ?></h1>
|
||
<p class="text-muted mb-0"><?= h(tr('تقرير قابل للطباعة يوضح الأصناف المطلوبة وعدد الطلبات التي تحتوي كل صنف.', 'Printable report showing required items and how many orders contain each item.')) ?></p>
|
||
</div>
|
||
<div class="d-flex gap-2 flex-wrap">
|
||
<a class="btn btn-outline-secondary" href="<?= h(url_for('eid_orders.php', $filterParams)) ?>"><i class="bi bi-arrow-<?= current_lang() === 'ar' ? 'right' : 'left' ?> me-1"></i><?= h(tr('رجوع لطلبات العيد', 'Back to Eid Orders')) ?></a>
|
||
<button type="button" class="btn btn-dark" onclick="window.print()"><i class="bi bi-printer me-1"></i><?= h(tr('طباعة', 'Print')) ?></button>
|
||
</div>
|
||
</div>
|
||
|
||
<form class="row g-3 align-items-end mb-4 d-print-none" method="GET" action="eid_print.php">
|
||
<div class="col-12 col-md-3">
|
||
<label class="form-label" for="print-search"><?= h(tr('بحث', 'Search')) ?></label>
|
||
<input id="print-search" type="text" class="form-control" name="q" value="<?= h($search) ?>" placeholder="<?= h(tr('رقم الفاتورة أو العميل أو ملاحظة', 'Invoice, customer, or note')) ?>">
|
||
</div>
|
||
<div class="col-6 col-md-2">
|
||
<label class="form-label" for="print-branch"><?= h(tr('الفرع', 'Branch')) ?></label>
|
||
<select id="print-branch" class="form-select" name="branch">
|
||
<option value=""><?= h(tr('كل الفروع', 'All branches')) ?></option>
|
||
<?php foreach (branches() as $branchCode => $branchLabel): ?>
|
||
<?php if ($user['role'] !== 'owner' && !in_array($branchCode, $allowedBranches, true)) { continue; } ?>
|
||
<option value="<?= h($branchCode) ?>" <?= $branch === $branchCode ? 'selected' : '' ?>><?= h($branchLabel) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-6 col-md-2">
|
||
<label class="form-label" for="print-mode"><?= h(tr('القناة', 'Channel')) ?></label>
|
||
<select id="print-mode" class="form-select" name="mode">
|
||
<option value=""><?= h(tr('الكل', 'All')) ?></option>
|
||
<option value="normal" <?= $mode === 'normal' ? 'selected' : '' ?>><?= h(tr('فاتورة', 'Invoice')) ?></option>
|
||
<option value="pos" <?= $mode === 'pos' ? 'selected' : '' ?>>POS</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-6 col-md-2">
|
||
<label class="form-label" for="print-payment-status"><?= h(tr('حالة الدفع', 'Payment')) ?></label>
|
||
<select id="print-payment-status" class="form-select" name="payment_status">
|
||
<option value=""><?= h(tr('كل الحالات', 'All statuses')) ?></option>
|
||
<option value="paid" <?= $paymentStatus === 'paid' ? 'selected' : '' ?>><?= h(tr('مدفوع', 'Paid')) ?></option>
|
||
<option value="partial" <?= $paymentStatus === 'partial' ? 'selected' : '' ?>><?= h(tr('جزئي', 'Partial')) ?></option>
|
||
<option value="unpaid" <?= $paymentStatus === 'unpaid' ? 'selected' : '' ?>><?= h(tr('غير مدفوع', 'Unpaid')) ?></option>
|
||
</select>
|
||
</div>
|
||
<div class="col-6 col-md-3">
|
||
<label class="form-label" for="print-delivery-status"><?= h(tr('حالة التجهيز', 'Prep status')) ?></label>
|
||
<select id="print-delivery-status" class="form-select" name="delivery_status">
|
||
<option value=""><?= h(tr('كل الحالات', 'All statuses')) ?></option>
|
||
<?php foreach ($deliveryOptions as $value => $label): ?>
|
||
<option value="<?= h($value) ?>" <?= $deliveryStatus === $value ? 'selected' : '' ?>><?= h($label) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-6 col-md-2">
|
||
<label class="form-label" for="print-date-from"><?= h(tr('من تاريخ', 'From date')) ?></label>
|
||
<input id="print-date-from" type="date" class="form-control" name="date_from" value="<?= h($dateFrom) ?>">
|
||
</div>
|
||
<div class="col-6 col-md-2">
|
||
<label class="form-label" for="print-date-to"><?= h(tr('إلى تاريخ', 'To date')) ?></label>
|
||
<input id="print-date-to" type="date" class="form-control" name="date_to" value="<?= h($dateTo) ?>">
|
||
</div>
|
||
<div class="col-12 col-md-3 d-flex gap-2">
|
||
<button type="submit" class="btn btn-dark flex-fill"><i class="bi bi-funnel me-1"></i><?= h(tr('تحديث الملخص', 'Update summary')) ?></button>
|
||
<a class="btn btn-outline-secondary" href="<?= h(url_for('eid_print.php')) ?>"><?= h(tr('إعادة ضبط', 'Reset')) ?></a>
|
||
</div>
|
||
</form>
|
||
|
||
<div class="d-none d-print-block mb-4">
|
||
<div class="text-center mb-3">
|
||
<h2 class="h4 mb-1"><?= h(tr('ملخص تجهيزات طلبات العيد', 'Eid Order Prep Summary')) ?></h2>
|
||
<div class="text-muted"><?= h(tr('تاريخ الطباعة', 'Printed at')) ?>: <?= h($generatedAt) ?></div>
|
||
</div>
|
||
<div class="d-flex flex-wrap gap-2 justify-content-center">
|
||
<span class="eid-chip"><?= h(tr('عدد الطلبات', 'Orders')) ?>: <?= (int) $summary['total_orders'] ?></span>
|
||
<span class="eid-chip"><?= h(tr('الأصناف المميزة', 'Unique items')) ?>: <?= (int) $summary['unique_items'] ?></span>
|
||
<span class="eid-chip"><?= h(tr('إجمالي الكمية', 'Total quantity')) ?>: <?= h(number_format((float) $summary['total_quantity'], 0)) ?></span>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($dbError): ?>
|
||
<div class="alert alert-danger"><?= h($dbError) ?></div>
|
||
<?php else: ?>
|
||
<div class="d-flex flex-wrap gap-2 mb-4">
|
||
<span class="eid-chip"><i class="bi bi-calendar-range"></i><?= h($dateFrom !== '' || $dateTo !== '' ? (($dateFrom !== '' ? $dateFrom : '…') . ' → ' . ($dateTo !== '' ? $dateTo : '…')) : tr('كل التواريخ', 'All dates')) ?></span>
|
||
<span class="eid-chip"><i class="bi bi-shop"></i><?= h($branch ? (branches()[$branch] ?? $branch) : tr('كل الفروع', 'All branches')) ?></span>
|
||
<span class="eid-chip"><i class="bi bi-stars"></i><?= h($deliveryStatus !== '' && isset($deliveryOptions[$deliveryStatus]) ? $deliveryOptions[$deliveryStatus] : tr('كل حالات التجهيز', 'All prep statuses')) ?></span>
|
||
<span class="eid-chip"><i class="bi bi-credit-card"></i><?= h($paymentStatus !== '' ? match ($paymentStatus) { 'paid' => tr('مدفوع', 'Paid'), 'partial' => tr('جزئي', 'Partial'), 'unpaid' => tr('غير مدفوع', 'Unpaid'), default => $paymentStatus } : tr('كل حالات الدفع', 'All payment statuses')) ?></span>
|
||
</div>
|
||
|
||
<section class="row g-3 mb-4">
|
||
<div class="col-12 col-md-6 col-xl-3"><div class="eid-kpi"><div class="eid-kpi-label"><?= h(tr('إجمالي الطلبات', 'Total orders')) ?></div><div class="eid-kpi-value"><?= (int) $summary['total_orders'] ?></div></div></div>
|
||
<div class="col-12 col-md-6 col-xl-3"><div class="eid-kpi"><div class="eid-kpi-label"><?= h(tr('الأصناف المميزة', 'Unique items')) ?></div><div class="eid-kpi-value"><?= (int) $summary['unique_items'] ?></div></div></div>
|
||
<div class="col-12 col-md-6 col-xl-3"><div class="eid-kpi"><div class="eid-kpi-label"><?= h(tr('إجمالي الكمية', 'Total quantity')) ?></div><div class="eid-kpi-value"><?= h(number_format((float) $summary['total_quantity'], 0)) ?></div></div></div>
|
||
<div class="col-12 col-md-6 col-xl-3"><div class="eid-kpi"><div class="eid-kpi-label"><?= h(tr('إجمالي القيمة', 'Total value')) ?></div><div class="eid-kpi-value"><?= h(number_format((float) $summary['total_amount'], 3)) ?></div></div></div>
|
||
</section>
|
||
|
||
<section class="mb-4">
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<div>
|
||
<h2 class="h5 mb-1"><?= h(tr('ملخص الأصناف', 'Item Summary')) ?></h2>
|
||
<div class="text-muted small"><?= h(tr('عدد الطلبات لكل صنف مع الكمية الإجمالية المطلوبة للتجهيز.', 'Order count per item with total quantity required for preparation.')) ?></div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($itemRows === []): ?>
|
||
<div class="alert alert-secondary mb-0"><?= h(tr('لا توجد بيانات ضمن هذه الفلاتر.', 'No data found for the selected filters.')) ?></div>
|
||
<?php else: ?>
|
||
<div class="table-responsive">
|
||
<table class="table table-hover align-middle">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th><?= h(tr('الصنف', 'Item')) ?></th>
|
||
<th><?= h(tr('SKU', 'SKU')) ?></th>
|
||
<th class="text-center"><?= h(tr('عدد الطلبات', 'Orders count')) ?></th>
|
||
<th class="text-end"><?= h(tr('إجمالي الكمية', 'Total quantity')) ?></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($itemRows as $row): ?>
|
||
<tr>
|
||
<td class="fw-semibold"><?= h($row['name']) ?></td>
|
||
<td class="text-muted small"><?= h($row['sku'] !== '' ? $row['sku'] : '—') ?></td>
|
||
<td class="text-center fw-semibold"><?= (int) $row['order_count'] ?></td>
|
||
<td class="text-end fw-semibold"><?= h(rtrim(rtrim(number_format((float) $row['qty'], 3, '.', ''), '0'), '.')) ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
</section>
|
||
|
||
<section>
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<div>
|
||
<h2 class="h5 mb-1"><?= h(tr('الطلبات المشمولة', 'Included Orders')) ?></h2>
|
||
<div class="text-muted small"><?= h(tr('قائمة الطلبات الداخلة في هذا الملخص مع الأصناف داخل كل طلب.', 'Orders included in this summary with the items inside each order.')) ?></div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($orders === []): ?>
|
||
<div class="alert alert-secondary mb-0"><?= h(tr('لا توجد طلبات عيد ضمن هذا النطاق.', 'There are no Eid orders in this range.')) ?></div>
|
||
<?php else: ?>
|
||
<div class="table-responsive">
|
||
<table class="table table-sm align-middle">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th><?= h(tr('الفاتورة', 'Invoice')) ?></th>
|
||
<th><?= h(tr('العميل', 'Customer')) ?></th>
|
||
<th><?= h(tr('الفرع', 'Branch')) ?></th>
|
||
<th><?= h(tr('تاريخ الاستلام', 'Delivery date')) ?></th>
|
||
<th><?= h(tr('حالة التجهيز', 'Prep status')) ?></th>
|
||
<th><?= h(tr('الأصناف', 'Items')) ?></th>
|
||
<th class="text-end"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($orders as $order): ?>
|
||
<tr>
|
||
<td class="fw-semibold"><?= h($order['receipt_no'] ?? ('#' . (int) $order['id'])) ?></td>
|
||
<td><?= h($order['customer_name'] ?: tr('عميل نقدي', 'Walk-in customer')) ?></td>
|
||
<td><?= h(branches()[$order['branch_code']] ?? $order['branch_code']) ?></td>
|
||
<td><?= h($order['delivery_date'] ?: substr((string) ($order['sale_date'] ?? ''), 0, 10)) ?></td>
|
||
<td><span class="badge rounded-pill text-bg-light border"><?= h(eid_delivery_status_label((string) ($order['delivery_status'] ?? 'pending'))) ?></span></td>
|
||
<td>
|
||
<div class="small">
|
||
<?php foreach (($order['items'] ?? []) as $item): ?>
|
||
<?php
|
||
$itemName = trim((string) ($item['name_ar'] ?? ''));
|
||
if ($itemName === '') {
|
||
$itemName = trim((string) ($item['name_en'] ?? ''));
|
||
}
|
||
if ($itemName === '') {
|
||
$itemName = trim((string) ($item['sku'] ?? ''));
|
||
}
|
||
?>
|
||
<div><?= h($itemName) ?> <span class="text-muted">× <?= h(rtrim(rtrim(number_format((float) ($item['qty'] ?? 0), 3, '.', ''), '0'), '.')) ?></span></div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</td>
|
||
<td class="text-end fw-semibold"><?= h(number_format((float) ($order['total_amount'] ?? 0), 3)) ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
</section>
|
||
<?php endif; ?>
|
||
</section>
|
||
</div>
|
||
<?php require __DIR__ . '/includes/footer.php'; ?>
|