39728-vm/eid_print.php
2026-04-23 02:06:50 +00:00

395 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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'; ?>