39728-vm/eid_orders.php
2026-05-05 04:54:23 +00:00

678 lines
34 KiB
PHP
Raw Permalink 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('eid_orders', 'show');
ensure_sales_table();
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'mark_as_paid') {
$id = (int)$_POST['id'];
try {
$sale = fetch_sale($id);
if ($sale && $sale['order_type'] === 'eid') {
$summary = sale_payment_summary($sale);
if ($summary['due_amount'] > 0.0005) {
apply_sale_payment($id, $summary['due_amount'], false);
set_flash('success', tr('تم تحويل حالة الدفع إلى مدفوع بنجاح.', 'Payment status updated to paid successfully.'));
} else {
set_flash('info', tr('الفاتورة مدفوعة مسبقاً.', 'Invoice is already paid.'));
}
}
} catch (Exception $e) {
set_flash('danger', tr('تعذر تحديث حالة الدفع.', 'Failed to update payment status.'));
}
redirect_to('eid_orders.php', $_GET);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update_delivery_status') {
$id = (int)$_POST['id'];
$newStatus = trim((string)($_POST['status'] ?? 'pending'));
$allowedStatuses = array_keys(eid_delivery_status_options());
if (in_array($newStatus, $allowedStatuses, true)) {
$stmt = db()->prepare("UPDATE sales_orders SET delivery_status = :status WHERE id = :id AND order_type = 'eid'");
$stmt->execute([':status' => $newStatus, ':id' => $id]);
set_flash('success', tr('تم تحديث حالة التجهيز بنجاح.', 'Prep status updated successfully.'));
}
redirect_to('eid_orders.php', $_GET);
}
$canDeleteEidOrders = $user['role'] === 'owner' || has_permission('sales', 'del');
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete_eid_order') {
if (!$canDeleteEidOrders) {
set_flash('danger', tr('ليس لديك صلاحية حذف الفواتير.', 'You do not have permission to delete invoices.'));
redirect_to('eid_orders.php', $_GET);
}
$id = (int)($_POST['id'] ?? 0);
try {
$sale = fetch_sale($id);
if (!$sale || ($sale['order_type'] ?? '') !== 'eid') {
set_flash('warning', tr('فاتورة العيد غير موجودة.', 'Eid invoice was not found.'));
} else {
$stmt = db()->prepare("DELETE FROM sales_orders WHERE id = :id AND order_type = 'eid'");
$stmt->execute([':id' => $id]);
set_flash('success', tr('تم حذف فاتورة العيد بنجاح.', 'Eid invoice deleted successfully.'));
}
} catch (Throwable $e) {
set_flash('danger', tr('تعذر حذف فاتورة العيد.', 'Failed to delete Eid invoice.'));
}
redirect_to('eid_orders.php', $_GET);
}
$activeNav = 'eid_orders';
$pageTitle = tr('طلبات العيد', 'Eid Orders');
$metaDescription = tr('متابعة طلبات العيد مع الفلاتر والمدى الزمني.', 'Track Eid orders with filters and date range.');
$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'] ?? ''));
$sort = trim((string) ($_GET['sort'] ?? 'delivery_date'));
$dir = strtolower(trim((string) ($_GET['dir'] ?? 'asc')));
$sortMap = [
'receipt_no' => 'receipt_no',
'customer' => 'customer_name',
'branch' => 'branch_code',
'delivery_date' => 'COALESCE(delivery_date, DATE(sale_date))',
'delivery_status' => 'delivery_status',
'item_count' => 'item_count',
'total_amount' => 'total_amount',
];
if (!isset($sortMap[$sort])) {
$sort = 'delivery_date';
}
if (!in_array($dir, ['asc', 'desc'], true)) {
$dir = 'asc';
}
$page = max(1, (int) ($_GET['p'] ?? 1));
$limit = 50;
$offset = ($page - 1) * $limit;
$allowedBranches = $user && $user['role'] !== 'owner' ? get_user_branches($user) : [];
$deliveryOptions = eid_delivery_status_options();
$dbError = null;
$totalPages = 1;
$orders = [];
$summary = [
'total_orders' => 0,
'total_items' => 0,
'total_amount' => 0,
'prep_orders' => 0,
];
try {
$params = [':order_type' => 'eid'];
$where = " WHERE order_type = :order_type AND NOT (delivery_status = 'delivered' AND COALESCE(due_amount, 0) <= 0.0005) ";
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 = '';
}
$summarySql = "SELECT COUNT(*) AS total_orders, COALESCE(SUM(item_count), 0) AS total_items, COALESCE(SUM(total_amount), 0) AS total_amount, COALESCE(SUM(CASE WHEN delivery_status IN ('pending', 'preparing') THEN 1 ELSE 0 END), 0) AS prep_orders FROM sales_orders" . $where;
$summaryStmt = db()->prepare($summarySql);
foreach ($params as $key => $value) {
$summaryStmt->bindValue($key, $value);
}
$summaryStmt->execute();
$summary = $summaryStmt->fetch() ?: $summary;
$countSql = 'SELECT COUNT(*) FROM sales_orders' . $where;
$countStmt = db()->prepare($countSql);
foreach ($params as $key => $value) {
$countStmt->bindValue($key, $value);
}
$countStmt->execute();
$total = (int) $countStmt->fetchColumn();
$totalPages = max(1, (int) ceil($total / $limit));
$sortDirection = strtoupper($dir);
$primarySort = $sortMap[$sort] . ' ' . $sortDirection;
$secondarySort = $sort === 'delivery_date'
? ', sale_date ' . $sortDirection . ', id ' . $sortDirection
: ', COALESCE(delivery_date, DATE(sale_date)) ' . $sortDirection . ', sale_date ' . $sortDirection . ', id ' . $sortDirection;
$sql = "SELECT sales_orders.*, (
SELECT COUNT(*)
FROM sales_orders AS seq
WHERE seq.order_type = sales_orders.order_type
AND seq.sale_mode = sales_orders.sale_mode
AND seq.id <= sales_orders.id
) AS mode_serial_no
FROM sales_orders" . $where . ' ORDER BY ' . $primarySort . $secondarySort . ' LIMIT :limit OFFSET :offset';
$stmt = db()->prepare($sql);
foreach ($params as $key => $value) {
$stmt->bindValue($key, $value);
}
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$orders = $stmt->fetchAll();
foreach ($orders as &$order) {
$order['items'] = json_decode((string) ($order['items_json'] ?? '[]'), true) ?: [];
$order['payment_summary'] = sale_payment_summary($order);
$itemPreview = [];
foreach ($order['items'] as $item) {
$name = trim((string) ($item['name'] ?? $item['name_ar'] ?? $item['name_en'] ?? $item['sku'] ?? ''));
$qty = max(0, (int) ($item['qty'] ?? 0));
if ($name === '') {
continue;
}
$itemPreview[] = $qty > 0 ? $name . ' ×' . $qty : $name;
if (count($itemPreview) >= 2) {
break;
}
}
$remainingItems = max(0, count($order['items']) - count($itemPreview));
$order['item_preview'] = $itemPreview;
$order['item_preview_more'] = $remainingItems;
}
unset($order);
} catch (Throwable $e) {
$dbError = $e->getMessage();
}
$queryState = static function (array $extra = []) use ($search, $branch, $mode, $paymentStatus, $deliveryStatus, $dateFrom, $dateTo, $sort, $dir): array {
$params = [
'q' => $search,
'branch' => $branch,
'mode' => $mode,
'payment_status' => $paymentStatus,
'delivery_status' => $deliveryStatus,
'date_from' => $dateFrom,
'date_to' => $dateTo,
'sort' => $sort,
'dir' => $dir,
];
foreach ($extra as $key => $value) {
$params[$key] = $value;
}
return array_filter($params, static fn($value) => $value !== null && $value !== '');
};
$sortUrl = static function (string $column) use ($sort, $dir, $queryState): string {
$nextDir = $sort === $column && $dir === 'asc' ? 'desc' : 'asc';
return url_for('eid_orders.php', $queryState(['sort' => $column, 'dir' => $nextDir, 'p' => 1]));
};
$sortIcon = static function (string $column) use ($sort, $dir): string {
if ($sort !== $column) {
return 'bi-arrow-down-up text-muted';
}
return $dir === 'asc' ? 'bi-sort-down-alt' : 'bi-sort-up';
};
require __DIR__ . '/includes/header.php';
?>
<section class="mb-4">
<div class="d-flex flex-wrap justify-content-between align-items-center gap-3 mb-3">
<div>
<h1 class="h4 mb-1"><i class="bi bi-stars me-2"></i><?= h(tr('طلبات العيد', 'Eid Orders')) ?></h1>
</div>
<div class="d-flex gap-2 flex-wrap">
<a class="btn btn-sm btn-dark" href="<?= h(url_for('eid_sale.php')) ?>"><i class="bi bi-plus-circle me-1"></i><?= h(tr('طلب عيد جديد', 'New Eid Order')) ?></a>
<a class="btn btn-sm btn-outline-dark" href="<?= h(url_for('eid_print.php', $queryState())) ?>"><i class="bi bi-printer me-1"></i><?= h(tr('طباعة ملخص التجهيز', 'Print prep summary')) ?></a>
</div>
</div>
<?php $hasAdvancedFilters = $mode !== null || $paymentStatus !== ''; ?>
<form class="mb-4" method="GET" action="eid_orders.php">
<div class="d-flex justify-content-end align-items-center mb-3">
<details class="eid-advanced-toggle" <?= $hasAdvancedFilters ? 'open' : '' ?>>
<summary class="btn btn-sm btn-outline-secondary">
<i class="bi bi-sliders me-1"></i><?= h(tr('فلاتر إضافية', 'More filters')) ?>
</summary>
<div class="eid-advanced-panel">
<div class="row g-2">
<div class="col-12 col-md-6">
<label class="form-label mb-1" for="eid-mode"><?= h(tr('القناة', 'Channel')) ?></label>
<select id="eid-mode" class="form-select form-select-sm" 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-12 col-md-6">
<label class="form-label mb-1" for="eid-payment-status"><?= h(tr('حالة الدفع', 'Payment')) ?></label>
<select id="eid-payment-status" class="form-select form-select-sm" 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>
</div>
</details>
</div>
<div class="row g-3 align-items-end">
<div class="col-12 col-md-3">
<label class="form-label mb-1" for="eid-search"><?= h(tr('بحث سريع', 'Quick search')) ?></label>
<input id="eid-search" type="text" class="form-control form-control-sm" name="q" value="<?= h($search) ?>" placeholder="<?= h(tr('بحث...', 'Search...')) ?>">
</div>
<div class="col-12 col-md-2">
<label class="form-label mb-1" for="eid-branch"><?= h(tr('الفرع', 'Branch')) ?></label>
<select id="eid-branch" class="form-select form-select-sm" 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(branch_label($branchCode)) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 col-md-2">
<label class="form-label mb-1" for="eid-delivery-status"><?= h(tr('التجهيز', 'Prep')) ?></label>
<select id="eid-delivery-status" class="form-select form-select-sm" 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-12 col-md-3">
<label class="form-label mb-1"><?= h(tr('فترة التسليم', 'Delivery window')) ?></label>
<div class="d-flex gap-2">
<input id="eid-date-from" type="date" class="form-control form-control-sm" name="date_from" value="<?= h($dateFrom) ?>" aria-label="<?= h(tr('من تاريخ', 'From date')) ?>">
<input id="eid-date-to" type="date" class="form-control form-control-sm" name="date_to" value="<?= h($dateTo) ?>" aria-label="<?= h(tr('إلى تاريخ', 'To date')) ?>">
</div>
</div>
<div class="col-12 col-md-2 d-flex gap-2">
<button type="submit" class="btn btn-sm btn-dark w-50"><i class="bi bi-funnel me-1"></i><?= h(tr('تطبيق', 'Apply')) ?></button>
<a class="btn btn-sm btn-outline-secondary w-50" href="<?= h(url_for('eid_orders.php')) ?>"><?= h(tr('إعادة ضبط', 'Reset')) ?></a>
</div>
</div>
</form>
<div class="row g-2 mb-3">
<div class="col-6 col-md-3">
<div class="card border-0 p-3 bg-primary-subtle">
<div class="small text-muted mb-1"><?= h(tr('إجمالي الطلبات', 'Total orders')) ?></div>
<div class="fs-5 fw-bold"><?= (int) ($summary['total_orders'] ?? 0) ?></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card border-0 p-3 bg-info-subtle">
<div class="small text-muted mb-1"><?= h(tr('عدد القطع', 'Total items')) ?></div>
<div class="fs-5 fw-bold"><?= (int) ($summary['total_items'] ?? 0) ?></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card border-0 p-3 bg-warning-subtle">
<div class="small text-muted mb-1"><?= h(tr('قيد التجهيز', 'Pending prep')) ?></div>
<div class="fs-5 fw-bold"><?= (int) ($summary['prep_orders'] ?? 0) ?></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card border-0 p-3 bg-success-subtle">
<div class="small text-muted mb-1"><?= h(tr('إجمالي القيمة', 'Total value')) ?></div>
<div class="fs-5 fw-bold"><?= h(number_format((float) ($summary['total_amount'] ?? 0), 3)) ?></div>
</div>
</div>
</div>
<?php if ($dbError): ?>
<div class="alert alert-danger"><?= h($dbError) ?></div>
<?php elseif ($orders === []): ?>
<div class="text-center py-5 text-muted">
<div class="mb-3"><i class="bi bi-calendar-heart" style="font-size: 2rem;"></i></div>
<h2 class="h5"><?= h(tr('لا توجد طلبات عيد حتى الآن', 'No Eid orders yet')) ?></h2>
<p class="mb-0"><?= h(tr('لا توجد طلبات عيد حتى الآن. ابدأ بإنشاء أول طلب عيد ليظهر هنا مع الفلاتر والتواريخ.', 'There are no Eid orders yet. Create your first Eid order to see it here with filters and dates.')) ?></p>
<a class="btn btn-dark mt-3" href="<?= h(url_for('eid_sale.php')) ?>"><i class="bi bi-plus-circle me-1"></i><?= h(tr('إنشاء طلب عيد', 'Create Eid Order')) ?></a>
</div>
<?php else: ?>
<div class="d-flex justify-content-end mb-2">
<span class="badge rounded-pill text-bg-light border px-2 py-1 text-muted fw-normal">
<?= h((int) ($summary['total_orders'] ?? 0)) ?> <?= h(tr('طلب', 'orders')) ?>
</span>
</div>
<div class="table-responsive" style="min-height: 400px; padding-bottom: 180px;">
<table class="table table-sm table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>
<a class="text-decoration-none text-dark fw-semibold d-inline-flex align-items-center gap-1" href="<?= h($sortUrl('receipt_no')) ?>">
<?= h(tr('الفاتورة', 'Invoice')) ?>
<i class="bi <?= h($sortIcon('receipt_no')) ?>"></i>
</a>
</th>
<th>
<a class="text-decoration-none text-dark fw-semibold d-inline-flex align-items-center gap-1" href="<?= h($sortUrl('customer')) ?>">
<?= h(tr('العميل', 'Customer')) ?>
<i class="bi <?= h($sortIcon('customer')) ?>"></i>
</a>
</th>
<th><?= h(tr('الهاتف', 'Phone')) ?></th>
<th>
<a class="text-decoration-none text-dark fw-semibold d-inline-flex align-items-center gap-1" href="<?= h($sortUrl('delivery_date')) ?>">
<?= h(tr('تسليم', 'Delivery')) ?>
<i class="bi <?= h($sortIcon('delivery_date')) ?>"></i>
</a>
</th>
<th>
<a class="text-decoration-none text-dark fw-semibold d-inline-flex align-items-center gap-1" href="<?= h($sortUrl('delivery_status')) ?>">
<?= h(tr('حالة التجهيز', 'Prep status')) ?>
<i class="bi <?= h($sortIcon('delivery_status')) ?>"></i>
</a>
</th>
<th>
<a class="text-decoration-none text-dark fw-semibold d-inline-flex align-items-center gap-1" href="<?= h($sortUrl('branch')) ?>">
<?= h(tr('الفرع', 'Branch')) ?>
<i class="bi <?= h($sortIcon('branch')) ?>"></i>
</a>
</th>
<th>
<a class="text-decoration-none text-dark fw-semibold d-inline-flex align-items-center gap-1" href="<?= h($sortUrl('item_count')) ?>">
<?= h(tr('الأصناف', 'Items')) ?>
<i class="bi <?= h($sortIcon('item_count')) ?>"></i>
</a>
</th>
<th class="text-end">
<a class="text-decoration-none text-dark fw-semibold d-inline-flex align-items-center gap-1 justify-content-end" href="<?= h($sortUrl('total_amount')) ?>">
<?= h(tr('الإجمالي', 'Total')) ?>
<i class="bi <?= h($sortIcon('total_amount')) ?>"></i>
</a>
</th>
<th class="text-end"><?= h(tr('المدفوع', 'Paid')) ?></th>
<th class="text-end"><?= h(tr('المتبقي', 'Remaining')) ?></th>
<th><?= h(tr('الدفع', 'Payment')) ?></th>
<th class="text-end"><?= h(tr('إجراءات', 'Actions')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<?php $effectiveDelivery = $order['delivery_date'] ?: date('Y-m-d', strtotime((string) $order['sale_date'])); ?>
<tr>
<td>
<?php
$receiptReference = trim((string) ($order['receipt_no'] ?? ''));
$eidSerialValue = (int) ($order['eid_serial_no'] ?? 0);
?>
<div class="fw-semibold">#<?= h($receiptReference !== '' ? $receiptReference : (string) ($order['id'] ?? '')) ?></div>
<div class="small text-muted">
<?php if ($eidSerialValue > 0): ?>
<?= h(tr('التسلسل الموسمي', 'Season serial')) ?>: <?= h(eid_serial_label($eidSerialValue)) ?>
<span aria-hidden="true">·</span>
<?php endif; ?>
<?= h(sale_mode_label((string) ($order['sale_mode'] ?? 'normal'))) ?>
</div>
</td>
<?php
$rawCustomerName = (string) ($order['customer_name'] ?: tr('عميل نقدي', 'Walk-in customer'));
$displayPhone = '';
if (str_contains($rawCustomerName, ' - ')) {
$parts = explode(' - ', $rawCustomerName);
$lastPart = trim(end($parts));
if (preg_match('/^[0-9+\s]+$/', $lastPart)) {
$displayPhone = $lastPart;
array_pop($parts);
$rawCustomerName = trim(implode(' - ', $parts));
}
}
?>
<td>
<div class="fw-semibold"><?= h($rawCustomerName) ?></div>
</td>
<td dir="ltr" class="text-start">
<?php
$phone = ltrim(preg_replace('/[^0-9]/', '', $displayPhone), '0');
if ($phone !== '') {
if (str_starts_with($phone, '968') && strlen($phone) > 8) {
$phone = substr($phone, 3);
}
echo h($phone);
} else {
echo '-';
}
?>
</td>
<td>
<div class="fw-semibold"><?= h(date('Y-m-d', strtotime((string) $effectiveDelivery))) ?></div>
</td>
<td>
<span class="badge <?= h(eid_delivery_status_badge_class((string) ($order['delivery_status'] ?? 'pending'))) ?> px-2 py-1"><?= h(eid_delivery_status_label((string) ($order['delivery_status'] ?? 'pending'))) ?></span>
</td>
<td>
<span class="badge rounded-pill text-bg-light border"><?= h(branch_label((string) $order['branch_code'])) ?></span>
</td>
<td>
<span class="badge rounded-pill text-bg-light border px-2 py-1 fw-semibold">
<?= (int) ($order['item_count'] ?? 0) ?> <?= h(tr('صنف', 'items')) ?>
</span>
</td>
<td class="text-end fw-semibold"><?= h(number_format((float) ($order['total_amount'] ?? 0), 3)) ?></td>
<td class="text-end text-success fw-semibold"><?= h(number_format((float) (($order['payment_summary']['paid_amount'] ?? 0)), 3)) ?></td>
<td class="text-end fw-semibold <?= (($order['payment_summary']['due_amount'] ?? 0) > 0.0005) ? 'text-danger' : 'text-success' ?>"><?= h(number_format((float) (($order['payment_summary']['due_amount'] ?? 0)), 3)) ?></td>
<td>
<span class="badge <?= h(payment_status_badge_class((string) (($order['payment_summary']['payment_status'] ?? ($order['payment_status'] ?? 'paid'))))) ?> px-2 py-1">
<?= h(payment_status_label((string) (($order['payment_summary']['payment_status'] ?? ($order['payment_status'] ?? 'paid'))))) ?>
</span>
</td>
<td>
<div class="d-flex justify-content-end gap-2">
<?php if ((($order['payment_summary']['payment_status'] ?? ($order['payment_status'] ?? 'unpaid'))) !== 'paid'): ?>
<button type="button" class="btn btn-sm btn-outline-success" onclick="receivePayment(<?= (int) $order['id'] ?>, <?= json_encode((float) ($order['total_amount'] ?? 0)) ?>, <?= json_encode((float) (($order['payment_summary']['paid_amount'] ?? 0))) ?>, <?= json_encode((float) (($order['payment_summary']['due_amount'] ?? 0))) ?>, <?= (($order['status'] ?? 'completed') === 'order') ? 'true' : 'false' ?>)" title="<?= h(tr('استلام دفعة', 'Receive Payment')) ?>">
<i class="bi bi-cash"></i>
</button>
<?php endif; ?>
<div class="dropdown">
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-boundary="window" aria-expanded="false" title="<?= h(tr('تغيير الحالة', 'Change status')) ?>">
<i class="bi bi-arrow-repeat"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow-sm">
<?php foreach (eid_delivery_status_options() as $val => $lbl): ?>
<?php if ($val === ($order['delivery_status'] ?? 'pending')) continue; ?>
<li>
<form method="post" action="">
<input type="hidden" name="action" value="update_delivery_status">
<input type="hidden" name="id" value="<?= h($order['id']) ?>">
<input type="hidden" name="status" value="<?= h($val) ?>">
<button class="dropdown-item" type="submit"><?= h($lbl) ?></button>
</form>
</li>
<?php endforeach; ?>
</ul>
</div>
<a class="btn btn-sm btn-light border text-primary" href="<?= h(url_for('sale.php', ['id' => $order['id']])) ?>" title="<?= h(tr('تفاصيل', 'Detail')) ?>"><i class="bi bi-eye"></i></a>
<a class="btn btn-sm btn-outline-secondary" href="<?= h(url_for('edit_sale.php', ['id' => $order['id']])) ?>" title="<?= h(tr('تعديل', 'Edit')) ?>"><i class="bi bi-pencil"></i></a>
<?php if ($canDeleteEidOrders): ?>
<form method="post" action="" class="d-inline-block js-eid-delete-form">
<input type="hidden" name="action" value="delete_eid_order">
<input type="hidden" name="id" value="<?= h($order['id']) ?>">
<button type="button" class="btn btn-sm btn-outline-danger" onclick="confirmDeleteEidOrder(this.form)" title="<?= h(tr('حذف', 'Delete')) ?>">
<i class="bi bi-trash"></i>
</button>
</form>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if ($totalPages > 1): ?>
<nav class="mt-4" aria-label="<?= h(tr('صفحات طلبات العيد', 'Eid order pages')) ?>">
<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('eid_orders.php', $queryState(['p' => $i]))) ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
</ul>
</nav>
<?php endif; ?>
<?php endif; ?>
</section>
<script>
function formatPaymentPopupAmount(value) {
return Number(value || 0).toFixed(3);
}
async function receivePayment(id, totalAmount, paidAmount, dueAmount, completeOrder = false) {
const popupHtml = `
<div class="text-start">
<div class="row g-2 mb-3">
<div class="col-4">
<div class="border rounded-3 p-2 h-100 bg-light">
<div class="small text-muted"><?= h(tr('إجمالي الفاتورة', 'Total amount')) ?></div>
<div class="fw-bold text-dark">${formatPaymentPopupAmount(totalAmount)}</div>
</div>
</div>
<div class="col-4">
<div class="border rounded-3 p-2 h-100 bg-light">
<div class="small text-muted"><?= h(tr('المدفوع سابقاً', 'Already paid')) ?></div>
<div class="fw-bold text-primary">${formatPaymentPopupAmount(paidAmount)}</div>
</div>
</div>
<div class="col-4">
<div class="border rounded-3 p-2 h-100 bg-light">
<div class="small text-muted"><?= h(tr('المتبقي الحالي', 'Current remaining')) ?></div>
<div class="fw-bold text-danger">${formatPaymentPopupAmount(dueAmount)}</div>
</div>
</div>
</div>
<label for="swal-payment-amount" class="form-label fw-semibold mb-2"><?= h(tr('المبلغ المطلوب دفعه الآن', 'Amount to pay now')) ?></label>
<input id="swal-payment-amount" type="number" class="swal2-input mt-0" min="0.001" step="0.001" max="${formatPaymentPopupAmount(dueAmount)}" value="${formatPaymentPopupAmount(dueAmount)}">
<div class="d-flex justify-content-between align-items-center rounded-3 border px-3 py-2 bg-light mt-3">
<span class="small text-muted"><?= h(tr('المتبقي بعد الدفعة', 'Remaining after payment')) ?></span>
<strong id="swal-payment-remaining" class="text-success">0.000</strong>
</div>
</div>`;
const { isConfirmed, value: paymentAmount } = await Swal.fire({
title: '<?= h(tr('استلام دفعة', 'Receive Payment')) ?>',
html: popupHtml,
showCancelButton: true,
confirmButtonColor: '#198754',
confirmButtonText: '<?= h(tr('حفظ الدفعة', 'Save Payment')) ?>',
cancelButtonText: '<?= h(tr('إلغاء', 'Cancel')) ?>',
focusConfirm: false,
didOpen: () => {
const input = document.getElementById('swal-payment-amount');
const remainingEl = document.getElementById('swal-payment-remaining');
const updateRemaining = () => {
const amount = parseFloat(input.value || '0');
const safeAmount = Number.isFinite(amount) ? amount : 0;
const remaining = Math.max(dueAmount - safeAmount, 0);
remainingEl.textContent = formatPaymentPopupAmount(remaining);
remainingEl.className = remaining > 0.0005 ? 'text-danger' : 'text-success';
};
input.addEventListener('input', updateRemaining);
input.focus();
input.select();
updateRemaining();
},
preConfirm: () => {
const input = document.getElementById('swal-payment-amount');
const amount = parseFloat(input.value || '0');
if (!amount || amount <= 0) {
Swal.showValidationMessage('<?= h(tr('أدخل مبلغاً صحيحاً.', 'Enter a valid amount.')) ?>');
return false;
}
if (amount - dueAmount > 0.0005) {
Swal.showValidationMessage('<?= h(tr('المبلغ لا يمكن أن يتجاوز المتبقي.', 'Amount cannot exceed the due balance.')) ?>');
return false;
}
return formatPaymentPopupAmount(amount);
}
});
if (!isConfirmed || !paymentAmount) {
return;
}
const formData = new FormData();
formData.append('sale_id', String(id));
formData.append('payment_amount', String(paymentAmount));
if (completeOrder) {
formData.append('complete_order', '1');
}
const response = await fetch('api/sales_payment.php', { method: 'POST', body: formData });
const data = await response.json();
if (data.success) {
await Swal.fire({ icon: 'success', text: data.message, confirmButtonText: 'OK' });
window.location.reload();
} else {
Swal.fire({ icon: 'error', text: data.error || '<?= h(tr('تعذر تسجيل الدفعة.', 'Could not record the payment.')) ?>' });
}
}
function confirmDeleteEidOrder(form) {
Swal.fire({
title: '<?= h(tr('هل أنت متأكد من حذف الفاتورة؟', 'Are you sure you want to delete this invoice?')) ?>',
text: '<?= h(tr('سيتم حذف فاتورة العيد نهائياً من النظام. استخدم هذا الخيار فقط عند الضرورة.', 'This will permanently delete the Eid invoice from the system. Use this only when necessary.')) ?>',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#dc3545',
cancelButtonText: '<?= h(tr('إلغاء', 'Cancel')) ?>',
confirmButtonText: '<?= h(tr('نعم، احذف', 'Yes, delete it')) ?>'
}).then((result) => {
if (result.isConfirmed) {
form.submit();
}
});
}
</script>
<?php require __DIR__ . '/includes/footer.php'; ?>