886 lines
46 KiB
PHP
886 lines
46 KiB
PHP
<?php
|
|
require_once __DIR__ . '/includes/app.php';
|
|
$user = require_permission('reports', 'show');
|
|
$pageTitle = tr('التقارير', 'Reports');
|
|
$activeNav = 'reports';
|
|
|
|
$tab = $_GET['tab'] ?? 'summary';
|
|
$dbError = null;
|
|
|
|
if ($tab === 'sales') {
|
|
$dateFrom = $_GET['date_from'] ?? date('Y-m-01');
|
|
$dateTo = $_GET['date_to'] ?? date('Y-m-t');
|
|
$branchFilter = $_GET['branch'] ?? '';
|
|
|
|
$params = [];
|
|
$where = base_sales_query_filters($params, null, $branchFilter ?: null);
|
|
$where .= ' AND DATE(sale_date) >= :date_from AND DATE(sale_date) <= :date_to';
|
|
$params[':date_from'] = $dateFrom;
|
|
$params[':date_to'] = $dateTo;
|
|
|
|
$sql = 'SELECT * FROM sales_orders' . $where . ' ORDER BY sale_date DESC';
|
|
try {
|
|
$stmt = db()->prepare($sql);
|
|
foreach ($params as $k => $v) {
|
|
$stmt->bindValue($k, $v);
|
|
}
|
|
$stmt->execute();
|
|
$salesReport = $stmt->fetchAll();
|
|
} catch(Throwable $e) {
|
|
$dbError = $e->getMessage();
|
|
$salesReport = [];
|
|
}
|
|
} elseif ($tab === 'orders') {
|
|
$branchFilter = $_GET['branch'] ?? '';
|
|
$params = [];
|
|
$where = base_sales_query_filters($params, null, $branchFilter ?: null);
|
|
$where .= " AND status = 'order'";
|
|
|
|
$sql = 'SELECT * FROM sales_orders' . $where . ' ORDER BY sale_date ASC';
|
|
try {
|
|
$stmt = db()->prepare($sql);
|
|
foreach ($params as $k => $v) {
|
|
$stmt->bindValue($k, $v);
|
|
}
|
|
$stmt->execute();
|
|
$followUpOrders = $stmt->fetchAll();
|
|
|
|
$onlineStmt = db()->query("SELECT * FROM online_orders WHERE status IN ('pending', 'accepted') ORDER BY created_at ASC");
|
|
$onlineOrders = $onlineStmt->fetchAll();
|
|
|
|
} catch(Throwable $e) {
|
|
$dbError = $e->getMessage();
|
|
$followUpOrders = [];
|
|
$onlineOrders = [];
|
|
}
|
|
} elseif ($tab === 'daily') {
|
|
$reportDate = $_GET['date'] ?? date('Y-m-d');
|
|
$branchFilter = $_GET['branch'] ?? '';
|
|
|
|
$params = [];
|
|
$where = base_sales_query_filters($params, null, $branchFilter ?: null);
|
|
$where .= " AND DATE(sale_date) = :rdate AND status != 'order'";
|
|
$params[':rdate'] = $reportDate;
|
|
|
|
$dailyTotals = [
|
|
'seller' => [],
|
|
'outlet' => [],
|
|
'payment' => [],
|
|
'total' => 0
|
|
];
|
|
|
|
try {
|
|
// By Seller
|
|
$sql = "SELECT cashier_name, SUM(total_amount) as total FROM sales_orders" . $where . " GROUP BY cashier_name";
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute($params);
|
|
$dailyTotals['seller'] = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
|
|
|
// By Outlet
|
|
$sql = "SELECT branch_code, SUM(total_amount) as total FROM sales_orders" . $where . " GROUP BY branch_code";
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute($params);
|
|
$dailyTotals['outlet'] = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
|
|
|
// By Payment
|
|
$sql = "SELECT payment_method, SUM(total_amount) as total FROM sales_orders" . $where . " GROUP BY payment_method";
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute($params);
|
|
$dailyTotals['payment'] = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
|
|
|
// Grand total
|
|
$sql = "SELECT SUM(total_amount) as total FROM sales_orders" . $where;
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute($params);
|
|
$dailyTotals['total'] = (float) $stmt->fetchColumn();
|
|
|
|
} catch(Throwable $e) {
|
|
$dbError = $e->getMessage();
|
|
}
|
|
} elseif ($tab === 'expenses') {
|
|
$dateFrom = $_GET['date_from'] ?? date('Y-m-01');
|
|
$dateTo = $_GET['date_to'] ?? date('Y-m-t');
|
|
$branchFilter = $_GET['branch'] ?? '';
|
|
$categoryFilter = $_GET['category_id'] ?? '';
|
|
|
|
$params = [];
|
|
$whereConditions = ["DATE(e.expense_date) >= :date_from", "DATE(e.expense_date) <= :date_to"];
|
|
$params[':date_from'] = $dateFrom;
|
|
$params[':date_to'] = $dateTo;
|
|
|
|
if ($user['role'] !== 'owner') {
|
|
$ubranches = get_user_branches($user);
|
|
if ($branchFilter && $branchFilter === 'general') {
|
|
$whereConditions[] = "e.branch_code IS NULL";
|
|
} elseif ($branchFilter && in_array($branchFilter, $ubranches, true)) {
|
|
$whereConditions[] = "e.branch_code = :branch";
|
|
$params[':branch'] = $branchFilter;
|
|
} else {
|
|
if (empty($ubranches)) {
|
|
$whereConditions[] = "e.branch_code IS NULL";
|
|
} else {
|
|
$inQuery = implode(',', array_fill(0, count($ubranches), '?'));
|
|
// We must use numbered placeholders if mixing with named placeholders?
|
|
// PDO might not like mixing ? and :name.
|
|
// Let's create named placeholders for in query.
|
|
$namedParams = [];
|
|
foreach($ubranches as $i => $ub) {
|
|
$key = ':ubranch_' . $i;
|
|
$namedParams[] = $key;
|
|
$params[$key] = $ub;
|
|
}
|
|
$inQuery = implode(', ', $namedParams);
|
|
$whereConditions[] = "(e.branch_code IN ($inQuery) OR e.branch_code IS NULL)";
|
|
}
|
|
}
|
|
} else {
|
|
if ($branchFilter) {
|
|
if ($branchFilter === 'general') {
|
|
$whereConditions[] = "e.branch_code IS NULL";
|
|
} else {
|
|
$whereConditions[] = "e.branch_code = :branch";
|
|
$params[':branch'] = $branchFilter;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($categoryFilter) {
|
|
$whereConditions[] = "e.category_id = :category_id";
|
|
$params[':category_id'] = $categoryFilter;
|
|
}
|
|
|
|
$where = " WHERE " . implode(" AND ", $whereConditions);
|
|
|
|
$sql = "SELECT e.*, c.name_ar as category_name_ar, c.name_en as category_name_en, u.username as created_by_name
|
|
FROM expenses e
|
|
LEFT JOIN expense_categories c ON e.category_id = c.id
|
|
LEFT JOIN users u ON e.created_by = u.id
|
|
" . $where . "
|
|
ORDER BY e.expense_date DESC";
|
|
try {
|
|
$stmt = db()->prepare($sql);
|
|
foreach ($params as $k => $v) {
|
|
$stmt->bindValue($k, $v);
|
|
}
|
|
$stmt->execute();
|
|
$expensesReport = $stmt->fetchAll();
|
|
|
|
$catStmt = db()->query("SELECT id, name_ar, name_en FROM expense_categories ORDER BY name_ar");
|
|
$expenseCategories = $catStmt->fetchAll();
|
|
} catch(Throwable $e) {
|
|
$dbError = $e->getMessage();
|
|
$expensesReport = [];
|
|
$expenseCategories = [];
|
|
}
|
|
} else {
|
|
$report = ['gross' => 0.0, 'branch_totals' => [], 'payment_totals' => [], 'product_totals' => [], 'sales_count' => 0];
|
|
try {
|
|
$report = report_metrics();
|
|
} catch (Throwable $e) {
|
|
$dbError = $e->getMessage();
|
|
}
|
|
}
|
|
|
|
require __DIR__ . '/includes/header.php';
|
|
?>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4 d-print-none">
|
|
<div>
|
|
<h1 class="h3 mb-0"><?= h($pageTitle) ?></h1>
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="nav nav-tabs mb-4 d-print-none">
|
|
<li class="nav-item">
|
|
<a class="nav-link <?= $tab === 'summary' ? 'active' : '' ?>" href="reports.php?tab=summary"><?= h(tr('ملخص عام', 'General Summary')) ?></a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link <?= $tab === 'daily' ? 'active' : '' ?>" href="reports.php?tab=daily"><?= h(tr('التقرير اليومي', 'Daily Report')) ?></a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link <?= $tab === 'sales' ? 'active' : '' ?>" href="reports.php?tab=sales"><?= h(tr('تقرير المبيعات', 'Sales Report')) ?></a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link <?= $tab === 'orders' ? 'active' : '' ?>" href="reports.php?tab=orders"><?= h(tr('طلبات للمتابعة', 'Follow-up Orders')) ?></a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link<?= $tab === 'expenses' ? 'active' : '' ?>" href="reports.php?tab=expenses"><?= h(tr('تقرير المصروفات', 'Expenses Report')) ?></a>
|
|
</li>
|
|
</ul>
|
|
|
|
<?php if ($dbError): ?>
|
|
<div class="alert alert-danger d-print-none"><?= h($dbError) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($tab === 'sales'): ?>
|
|
|
|
<div class="card mb-4 d-print-none">
|
|
<div class="card-body">
|
|
<form method="GET" action="reports.php" class="row g-3 align-items-end">
|
|
<input type="hidden" name="tab" value="sales">
|
|
<div class="col-md-3">
|
|
<label class="form-label"><?= h(tr('من تاريخ', 'From Date')) ?></label>
|
|
<input type="date" name="date_from" class="form-control" value="<?= h($dateFrom) ?>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label"><?= h(tr('إلى تاريخ', 'To Date')) ?></label>
|
|
<input type="date" name="date_to" class="form-control" value="<?= h($dateTo) ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
|
|
<select name="branch" class="form-select">
|
|
<option value=""><?= h(tr('جميع الفروع', 'All Branches')) ?></option>
|
|
<?php
|
|
$availableBranches = get_user_branches_assoc($user);
|
|
foreach ($availableBranches as $code => $b):
|
|
?>
|
|
<option value="<?= h($code) ?>" <?= $branchFilter === $code ? 'selected' : '' ?>><?= h(branch_label($code)) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="submit" class="btn btn-primary w-100"><?= h(tr('بحث', 'Search')) ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-none d-print-block mb-4 text-center">
|
|
<h2><?= h(tr('تقرير المبيعات', 'Sales Report')) ?></h2>
|
|
<p><?= h(tr('الفترة من', 'Period from')) ?> <?= h($dateFrom) ?> <?= h(tr('إلى', 'to')) ?> <?= h($dateTo) ?></p>
|
|
<?php if ($branchFilter): ?>
|
|
<p><?= h(tr('الفرع:', 'Branch:')) ?> <?= h(branch_label($branchFilter)) ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="surface-card">
|
|
<div class="d-flex justify-content-between align-items-center mb-3 d-print-none">
|
|
<h3 class="h5 mb-0"><?= h(tr('نتائج التقرير', 'Report Results')) ?></h3>
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="window.print()"><i class="bi bi-printer"></i> <?= h(tr('طباعة', 'Print')) ?></button>
|
|
</div>
|
|
|
|
<?php if(empty($salesReport)): ?>
|
|
<p class="text-muted"><?= h(tr('لا توجد مبيعات في هذه الفترة.', 'No sales found in this period.')) ?></p>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-striped align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th><?= h(tr('التاريخ', 'Date')) ?></th>
|
|
<th><?= h(tr('رقم الإيصال', 'Receipt No')) ?></th>
|
|
<th><?= h(tr('الكاشير', 'Cashier')) ?></th>
|
|
<th><?= h(tr('الفرع', 'Branch')) ?></th>
|
|
<th><?= h(tr('طريقة الدفع', 'Payment Method')) ?></th>
|
|
<th><?= h(tr('الحالة', 'Status')) ?></th>
|
|
<th class="text-end"><?= h(tr('المجموع', 'Subtotal')) ?></th>
|
|
<th class="text-end"><?= h(tr('الضريبة', 'VAT')) ?></th>
|
|
<th class="text-end"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php
|
|
$subtotalSum = 0;
|
|
$vatSum = 0;
|
|
$totalSum = 0;
|
|
foreach($salesReport as $sale):
|
|
$vat = (float) ($sale['vat_amount'] ?? 0);
|
|
$total = (float) $sale['total_amount'];
|
|
$calcSubtotal = $total - $vat;
|
|
|
|
$subtotalSum += $calcSubtotal;
|
|
$vatSum += $vat;
|
|
$totalSum += $total;
|
|
?>
|
|
<tr>
|
|
<td><?= h(date('Y-m-d H:i', strtotime((string)$sale['sale_date']))) ?></td>
|
|
<td><?= h((string)$sale['receipt_no']) ?></td>
|
|
<td><?= h((string)$sale['cashier_name']) ?></td>
|
|
<td><?= h(branch_label((string)$sale['branch_code'])) ?></td>
|
|
<td><?= h(ucfirst((string)$sale['payment_method'])) ?></td>
|
|
<td>
|
|
<?php if (($sale['status'] ?? 'completed') === 'order'): ?>
|
|
<span class="badge bg-warning text-dark"><i class="bi bi-clock"></i> <?= h(tr('طلب حجز', 'Order')) ?></span>
|
|
<?php else: ?>
|
|
<span class="badge bg-success"><i class="bi bi-check-circle"></i> <?= h(tr('مدفوع', 'Paid')) ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td class="text-end"><?= h(currency($calcSubtotal)) ?></td>
|
|
<td class="text-end"><?= h(currency($vat)) ?></td>
|
|
<td class="text-end fw-bold"><?= h(currency($total)) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
<tfoot class="table-dark">
|
|
<tr>
|
|
<td colspan="6" class="text-end"><?= h(tr('الإجمالي الكلي', 'Grand Total')) ?></td>
|
|
<td class="text-end fw-bold fs-6"><?= h(currency($subtotalSum)) ?></td>
|
|
<td class="text-end fw-bold fs-6"><?= h(currency($vatSum)) ?></td>
|
|
<td class="text-end fw-bold fs-5"><?= h(currency($totalSum)) ?></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php elseif ($tab === 'expenses'): ?>
|
|
|
|
<style>
|
|
@media print {
|
|
body { font-size: 13pt !important; background: #fff; }
|
|
.table-bordered th, .table-bordered td { border: 1px solid #000 !important; padding: 6px; }
|
|
.table th { background-color: #f0f0f0 !important; -webkit-print-color-adjust: exact; color: #000; }
|
|
.d-print-none { display: none !important; }
|
|
.surface-card { box-shadow: none !important; border: none !important; padding: 0 !important; }
|
|
.print-header { display: block !important; margin-bottom: 30px; }
|
|
.page-break-inside-avoid { page-break-inside: avoid; }
|
|
}
|
|
.print-header { display: none; }
|
|
</style>
|
|
|
|
<!-- Filters (Hidden on Print) -->
|
|
<div class="card mb-4 d-print-none">
|
|
<div class="card-body">
|
|
<form method="GET" action="reports.php" class="row g-3 align-items-end">
|
|
<input type="hidden" name="tab" value="expenses">
|
|
<div class="col-md-3">
|
|
<label class="form-label"><?= h(tr('من تاريخ', 'From Date')) ?></label>
|
|
<input type="date" name="date_from" class="form-control" value="<?= h($dateFrom) ?>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label"><?= h(tr('إلى تاريخ', 'To Date')) ?></label>
|
|
<input type="date" name="date_to" class="form-control" value="<?= h($dateTo) ?>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label"><?= h(tr('التصنيف', 'Category')) ?></label>
|
|
<select name="category_id" class="form-select">
|
|
<option value=""><?= h(tr('الكل', 'All')) ?></option>
|
|
<?php foreach($expenseCategories as $cat):
|
|
if ($cat['id'] == $categoryFilter) {
|
|
$catName = $cat['name_ar'] . ' / ' . $cat['name_en'];
|
|
break;
|
|
}
|
|
?>
|
|
<option value="<?= $cat['id'] ?>" <?= $categoryFilter == $cat['id'] ? 'selected' : '' ?> >
|
|
<?= h($cat['name_ar'] . ' / ' . $cat['name_en']) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
|
|
<select name="branch" class="form-select">
|
|
<option value=""><?= h(tr('جميع الفروع', 'All Branches')) ?></option>
|
|
<option value="general" <?= $branchFilter === 'general' ? 'selected' : '' ?>><?= h(tr('مصروفات عامة', 'General Expenses')) ?></option>
|
|
<?php
|
|
$availableBranches = get_user_branches_assoc($user);
|
|
foreach ($availableBranches as $code => $b):
|
|
?>
|
|
<option value="<?= h($code) ?>" <?= $branchFilter === $code ? 'selected' : '' ?>><?= h(branch_label($code)) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="submit" class="btn btn-primary w-100"><?= h(tr('بحث', 'Search')) ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Formal Print Header -->
|
|
<div class="print-header">
|
|
<div class="text-center mb-4" style="border-bottom: 2px solid #000; padding-bottom: 15px;">
|
|
<h2 style="font-weight: bold; margin:0;"><?= h(tr('تقرير المصروفات التفصيلي', 'Detailed Expenses Report')) ?></h2>
|
|
<p style="margin: 5px 0 0; color: #555;"><?= h(tr('تاريخ الإصدار:', 'Generated on:')) ?> <?= date('Y-m-d H:i') ?></p>
|
|
</div>
|
|
|
|
<div class="row mb-4" style="border: 1px solid #000; padding: 15px; margin: 0;">
|
|
<div class="col-8">
|
|
<div style="margin-bottom: 5px;"><strong><?= h(tr('الفترة:', 'Period:')) ?></strong> <?= h($dateFrom) ?> <?= h(tr('إلى', 'to')) ?> <?= h($dateTo) ?></div>
|
|
<?php if ($categoryFilter):
|
|
$catName = '';
|
|
foreach ($expenseCategories as $c) {
|
|
if ($c['id'] == $categoryFilter) {
|
|
$catName = $c['name_ar'] . ' / ' . $c['name_en'];
|
|
break;
|
|
}
|
|
}
|
|
?>
|
|
<div style="margin-bottom: 5px;"><strong><?= h(tr('التصنيف:', 'Category:')) ?></strong> <?= h($catName) ?></div>
|
|
<?php endif; ?>
|
|
<div><strong><?= h(tr('الفرع:', 'Branch:')) ?></strong> <?= h($branchFilter ? ($branchFilter === 'general' ? tr('مصروفات عامة', 'General Expenses') : branch_label($branchFilter)) : tr('جميع الفروع', 'All Branches')) ?></div>
|
|
</div>
|
|
<div class="col-4 text-end">
|
|
<div><strong><?= h(tr('بواسطة:', 'Requested By:')) ?></strong> <?= h($user['username']) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Results Table -->
|
|
<div class="surface-card">
|
|
<div class="d-flex justify-content-between align-items-center mb-3 d-print-none">
|
|
<h3 class="h5 mb-0"><?= h(tr('نتائج التقرير', 'Report Results')) ?></h3>
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="window.print()"><i class="bi bi-printer"></i> <?= h(tr('طباعة رسمية', 'Formal Print')) ?></button>
|
|
</div>
|
|
|
|
<?php if(empty($expensesReport)): ?>
|
|
<p class="text-muted d-print-none"><?= h(tr('لا توجد مصروفات في هذه الفترة.', 'No expenses found in this period.')) ?></p>
|
|
<div class="print-header text-center py-5">
|
|
<h4><?= h(tr('لا توجد بيانات لهذه الفترة', 'No data for this period')) ?></h4>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-striped align-middle" style="width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th><?= h(tr('التاريخ', 'Date')) ?></th>
|
|
<th><?= h(tr('الفرع', 'Branch')) ?></th>
|
|
<th><?= h(tr('التصنيف', 'Category')) ?></th>
|
|
<th><?= h(tr('الوصف', 'Description')) ?></th>
|
|
<th><?= h(tr('المسجل', 'Logged By')) ?></th>
|
|
<th class="text-end"><?= h(tr('المبلغ', 'Amount')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php
|
|
$totalSum = 0;
|
|
foreach($expensesReport as $exp):
|
|
$totalSum += (float) $exp['amount'];
|
|
?>
|
|
<tr>
|
|
<td><?= h(date('Y-m-d', strtotime((string)$exp['expense_date']))) ?></td>
|
|
<td>
|
|
<?php if (empty($exp['branch_code'])): ?>
|
|
<?= h(tr('عام', 'General')) ?>
|
|
<?php else:
|
|
echo h(branch_label((string)$exp['branch_code']));
|
|
endif; ?>
|
|
</td>
|
|
<td><?= h($exp['category_name_ar'] . ' / ' . $exp['category_name_en']) ?></td>
|
|
<td><?= h((string)$exp['description']) ?></td>
|
|
<td><?= h((string)$exp['created_by_name']) ?></td>
|
|
<td class="text-end fw-bold"><?= h(currency((float)$exp['amount'])) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
<tfoot class="table-dark">
|
|
<tr>
|
|
<td colspan="5" class="text-end"><strong><?= h(tr('الإجمالي الكلي', 'Grand Total')) ?></strong></td>
|
|
<td class="text-end fw-bold fs-5"><strong><?= h(currency($totalSum)) ?></strong></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Signatures (Print Only) -->
|
|
<div class="print-header mt-5 pt-5 page-break-inside-avoid">
|
|
<div class="row text-center mt-5">
|
|
<div class="col-4">
|
|
<p><strong><?= h(tr('المحاسب / المُعد', 'Prepared by / Accountant')) ?></strong></p>
|
|
<hr style="width: 60%; margin: 40px auto 0; border-top: 1px solid #000;">
|
|
</div>
|
|
<div class="col-4">
|
|
<p><strong><?= h(tr('المراجع', 'Checked by')) ?></strong></p>
|
|
<hr style="width: 60%; margin: 40px auto 0; border-top: 1px solid #000;">
|
|
</div>
|
|
<div class="col-4">
|
|
<p><strong><?= h(tr('المدير العام', 'General Manager')) ?></strong></p>
|
|
<hr style="width: 60%; margin: 40px auto 0; border-top: 1px solid #000;">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php elseif ($tab === 'orders'): ?>
|
|
|
|
<div class="card mb-4 d-print-none">
|
|
<div class="card-body">
|
|
<form method="GET" action="reports.php" class="row g-3 align-items-end">
|
|
<input type="hidden" name="tab" value="orders">
|
|
<div class="col-md-4">
|
|
<label class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
|
|
<select name="branch" class="form-select" onchange="this.form.submit()">
|
|
<option value=""><?= h(tr('جميع الفروع', 'All Branches')) ?></option>
|
|
<?php
|
|
$availableBranches = get_user_branches_assoc($user);
|
|
foreach ($availableBranches as $code => $b):
|
|
?>
|
|
<option value="<?= h($code) ?>" <?= $branchFilter === $code ? 'selected' : '' ?>><?= h(branch_label($code)) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-none d-print-block mb-4 text-center">
|
|
<h2><?= h(tr('طلبات للمتابعة', 'Follow-up Orders')) ?></h2>
|
|
<p><?= h(date('Y-m-d H:i')) ?></p>
|
|
<?php if ($branchFilter): ?>
|
|
<p><?= h(tr('الفرع:', 'Branch:')) ?> <?= h(branch_label($branchFilter)) ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="surface-card">
|
|
<div class="d-flex justify-content-between align-items-center mb-3 d-print-none">
|
|
<h3 class="h5 mb-0"><?= h(tr('طلبات حجز بانتظار الدفع', 'Reservation orders pending payment')) ?></h3>
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="window.print()"><i class="bi bi-printer"></i> <?= h(tr('طباعة', 'Print')) ?></button>
|
|
</div>
|
|
|
|
<?php if(empty($followUpOrders)): ?>
|
|
<p class="text-muted"><?= h(tr('لا توجد طلبات للمتابعة.', 'No follow-up orders.')) ?></p>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th><?= h(tr('التاريخ', 'Date')) ?></th>
|
|
<th><?= h(tr('رقم الإيصال', 'Receipt No')) ?></th>
|
|
<th><?= h(tr('العميل', 'Customer')) ?></th>
|
|
<th><?= h(tr('هاتف العميل', 'Customer Phone')) ?></th>
|
|
<th><?= h(tr('الفرع', 'Branch')) ?></th>
|
|
<th class="text-end"><?= h(tr('المبلغ المستحق', 'Due Amount')) ?></th>
|
|
<th class="d-print-none text-end"><?= h(tr('إجراءات', 'Actions')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach($followUpOrders as $sale): ?>
|
|
<tr>
|
|
<td><?= h(date('Y-m-d H:i', strtotime((string)$sale['sale_date']))) ?></td>
|
|
<td><?= h((string)$sale['receipt_no']) ?></td>
|
|
<td><?= h((string)($sale['customer_name'] ?: '-')) ?></td>
|
|
<td dir="ltr"><?= h((string)($sale['customer_phone'] ? phone_display($sale['customer_phone']) : '-')) ?></td>
|
|
<td><?= h(branch_label((string)$sale['branch_code'])) ?></td>
|
|
<td class="text-end fw-bold text-danger"><?= h(currency((float)$sale['total_amount'])) ?></td>
|
|
<td class="d-print-none text-end">
|
|
<a class="btn btn-sm btn-light border" href="<?= h(url_for('sale.php', ['id' => $sale['id']])) ?>"><?= h(tr('عرض', 'View')) ?></a>
|
|
<button class="btn btn-sm btn-success" onclick="markPaidFromReports(<?= $sale['id'] ?>)"><i class="bi bi-check-lg"></i> <?= h(tr('دفع', 'Pay')) ?></button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<script>
|
|
function markPaidFromReports(id) {
|
|
Swal.fire({ title: "<?= h(tr('تأكيد الدفع', 'Confirm payment')) ?>", text: "<?= h(tr('هل أنت متأكد من تأكيد دفع هذا الطلب؟', 'Are you sure you want to confirm payment for this order?')) ?>", icon: "warning", showCancelButton: true, confirmButtonText: "<?= h(tr('نعم', 'Yes')) ?>", cancelButtonText: "<?= h(tr('إلغاء', 'Cancel')) ?>" }).then((result) => { if(result.isConfirmed) { window.location.href = 'sales.php?mark_paid=' + id + '&redirect=reports.php?tab=orders'; } });
|
|
}
|
|
</script>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- Online Orders Section -->
|
|
<div class="surface-card mt-4 mb-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-3 d-print-none">
|
|
<h3 class="h5 mb-0"><i class="bi bi-globe me-2 text-primary"></i><?= h(tr('طلبات المتجر الإلكتروني (نشطة)', 'Active Online Store Orders')) ?></h3>
|
|
<a class="btn btn-outline-primary btn-sm" href="online_orders.php"><i class="bi bi-box-arrow-up-right"></i> <?= h(tr('إدارة الطلبات', 'Manage Orders')) ?></a>
|
|
</div>
|
|
|
|
<?php if(empty($onlineOrders)): ?>
|
|
<p class="text-muted"><?= h(tr('لا توجد طلبات متجر نشطة حالياً.', 'No active online orders currently.')) ?></p>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th><?= h(tr('التاريخ', 'Date')) ?></th>
|
|
<th><?= h(tr('رقم الطلب', 'Order ID')) ?></th>
|
|
<th><?= h(tr('العميل', 'Customer')) ?></th>
|
|
<th><?= h(tr('هاتف العميل', 'Customer Phone')) ?></th>
|
|
<th><?= h(tr('العنوان', 'Address')) ?></th>
|
|
<th><?= h(tr('الحالة', 'Status')) ?></th>
|
|
<th class="text-end"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach($onlineOrders as $order): ?>
|
|
<tr>
|
|
<td><?= h(date('Y-m-d H:i', strtotime((string)$order['created_at']))) ?></td>
|
|
<td>#<?= h((string)$order['id']) ?></td>
|
|
<td><?= h((string)($order['customer_name'] ?: '-')) ?></td>
|
|
<td dir="ltr"><?= h((string)($order['customer_phone'] ? phone_display($order['customer_phone']) : '-')) ?></td>
|
|
<td><?= h((string)($order['customer_address'] ?: '-')) ?></td>
|
|
<td>
|
|
<?php if ($order['status'] === 'pending'): ?>
|
|
<span class="badge bg-warning text-dark"><?= h(tr('قيد الانتظار', 'Pending')) ?></span>
|
|
<?php elseif ($order['status'] === 'accepted'): ?>
|
|
<span class="badge bg-info text-dark"><?= h(tr('مقبول', 'Accepted')) ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td class="text-end fw-bold text-primary"><?= h(currency((float)$order['total_amount'])) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php elseif ($tab === 'daily'): ?>
|
|
|
|
<div class="card mb-4 d-print-none">
|
|
<div class="card-body">
|
|
<form method="GET" action="reports.php" class="row g-3 align-items-end">
|
|
<input type="hidden" name="tab" value="daily">
|
|
<div class="col-md-4">
|
|
<label class="form-label"><?= h(tr('التاريخ', 'Date')) ?></label>
|
|
<input type="date" name="date" class="form-control" value="<?= h($reportDate) ?>" onchange="this.form.submit()">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
|
|
<select name="branch" class="form-select" onchange="this.form.submit()">
|
|
<option value=""><?= h(tr('جميع الفروع', 'All Branches')) ?></option>
|
|
<?php
|
|
$availableBranches = get_user_branches_assoc($user);
|
|
foreach ($availableBranches as $code => $b):
|
|
?>
|
|
<option value="<?= h($code) ?>" <?= $branchFilter === $code ? 'selected' : '' ?>><?= h(branch_label($code)) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="submit" class="btn btn-primary w-100"><?= h(tr('بحث', 'Search')) ?></button>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="button" class="btn btn-outline-secondary w-100" onclick="window.print()"><i class="bi bi-printer"></i> <?= h(tr('طباعة', 'Print')) ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-none d-print-block mb-4 text-center">
|
|
<h2><?= h(tr('التقرير اليومي', 'Daily Report')) ?></h2>
|
|
<p><?= h(tr('التاريخ:', 'Date:')) ?> <?= h($reportDate) ?></p>
|
|
<?php if ($branchFilter): ?>
|
|
<p><?= h(tr('الفرع:', 'Branch:')) ?> <?= h(branch_label($branchFilter)) ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if ($dailyTotals['total'] == 0): ?>
|
|
<div class="alert alert-info"><?= h(tr('لا توجد مبيعات في هذا اليوم.', 'No sales on this day.')) ?></div>
|
|
<?php else: ?>
|
|
<div class="row g-4 mb-4">
|
|
<!-- Total Card -->
|
|
<div class="col-12">
|
|
<div class="surface-card text-center py-4 bg-primary text-white rounded shadow-sm">
|
|
<h3 class="h5 mb-2"><?= h(tr('إجمالي المبيعات', 'Total Sales')) ?></h3>
|
|
<div class="fs-1 fw-bold"><?= h(currency($dailyTotals['total'])) ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- By Seller -->
|
|
<div class="col-md-4">
|
|
<div class="surface-card h-100 shadow-sm">
|
|
<h3 class="h5 mb-3 border-bottom pb-2"><i class="bi bi-person me-2 text-primary"></i><?= h(tr('حسب الموظف', 'By Seller')) ?></h3>
|
|
<ul class="list-group list-group-flush">
|
|
<?php foreach($dailyTotals['seller'] as $seller => $amount): ?>
|
|
<li class="list-group-item d-flex justify-content-between align-items-center px-0 bg-transparent">
|
|
<span><?= h($seller ?: tr('غير محدد', 'Unknown')) ?></span>
|
|
<span class="fw-bold"><?= h(currency((float)$amount)) ?></span>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- By Outlet -->
|
|
<div class="col-md-4">
|
|
<div class="surface-card h-100 shadow-sm">
|
|
<h3 class="h5 mb-3 border-bottom pb-2"><i class="bi bi-shop me-2 text-primary"></i><?= h(tr('حسب الفرع', 'By Outlet')) ?></h3>
|
|
<ul class="list-group list-group-flush">
|
|
<?php foreach($dailyTotals['outlet'] as $outlet => $amount): ?>
|
|
<li class="list-group-item d-flex justify-content-between align-items-center px-0 bg-transparent">
|
|
<span><?= h(branch_label((string)$outlet)) ?></span>
|
|
<span class="fw-bold"><?= h(currency((float)$amount)) ?></span>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- By Payment -->
|
|
<div class="col-md-4">
|
|
<div class="surface-card h-100 shadow-sm">
|
|
<h3 class="h5 mb-3 border-bottom pb-2"><i class="bi bi-credit-card me-2 text-primary"></i><?= h(tr('حسب طريقة الدفع', 'By Payment')) ?></h3>
|
|
<ul class="list-group list-group-flush">
|
|
<?php
|
|
$payLabels = ['cash' => tr('كاش', 'Cash'), 'card' => tr('بطاقة', 'Card'), 'bank' => tr('تحويل بنكي', 'Bank'), 'mixed' => tr('متعدد', 'Mixed')];
|
|
foreach($dailyTotals['payment'] as $payment => $amount):
|
|
?>
|
|
<li class="list-group-item d-flex justify-content-between align-items-center px-0 bg-transparent">
|
|
<span><?= h($payLabels[$payment] ?? ucfirst($payment)) ?></span>
|
|
<span class="fw-bold"><?= h(currency((float)$amount)) ?></span>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php else: ?>
|
|
|
|
<!-- Top KPIs -->
|
|
<section class="row g-3 mb-4 d-print-none">
|
|
<div class="col-md-3">
|
|
<article class="metric-card d-flex align-items-center h-100">
|
|
<div class="me-3 fs-1 text-primary"><i class="bi bi-wallet2"></i></div>
|
|
<div>
|
|
<div class="eyebrow text-muted mb-1"><?= h(tr('إجمالي المبيعات', 'Gross sales')) ?></div>
|
|
<div class="metric-value fs-4 fw-bold"><?= h(currency((float) $report['gross'])) ?></div>
|
|
<div class="small text-muted" style="font-size:0.8rem;"><?= h(tr('شامل جميع العمليات', 'Includes all transactions')) ?></div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<article class="metric-card d-flex align-items-center h-100">
|
|
<div class="me-3 fs-1 text-info"><i class="bi bi-receipt"></i></div>
|
|
<div>
|
|
<div class="eyebrow text-muted mb-1"><?= h(tr('إجمالي الضريبة', 'Total VAT')) ?></div>
|
|
<div class="metric-value fs-4 fw-bold"><?= h(currency((float) ($report['total_vat'] ?? 0))) ?></div>
|
|
<div class="small text-muted" style="font-size:0.8rem;"><?= h(tr('مجموع ضريبة القيمة المضافة', 'Total Value Added Tax')) ?></div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<article class="metric-card d-flex align-items-center h-100">
|
|
<div class="me-3 fs-1 text-success"><i class="bi bi-basket3"></i></div>
|
|
<div>
|
|
<div class="eyebrow text-muted mb-1"><?= h(tr('عدد الفواتير', 'Invoices')) ?></div>
|
|
<div class="metric-value fs-4 fw-bold"><?= h((string) $report['sales_count']) ?></div>
|
|
<div class="small text-muted" style="font-size:0.8rem;"><?= h(tr('إجمالي الفواتير المسجلة', 'Total logged invoices')) ?></div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<article class="metric-card d-flex align-items-center h-100">
|
|
<div class="me-3 fs-1 text-warning"><i class="bi bi-star"></i></div>
|
|
<div class="overflow-hidden">
|
|
<div class="eyebrow text-muted mb-1"><?= h(tr('أفضل صنف', 'Top product')) ?></div>
|
|
<div class="metric-value fs-5 fw-bold text-truncate" style="max-width: 150px;"><?= h($report['product_totals'] ? product_label((string) array_key_first($report['product_totals'])) : tr('لا يوجد', 'None yet')) ?></div>
|
|
<div class="small text-muted" style="font-size:0.8rem;"><?= h(tr('الأكثر مبيعاً حتى الآن', 'Most sold item so far')) ?></div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Charts and Breakdowns -->
|
|
<section class="row g-4 d-print-none">
|
|
|
|
<!-- Trend Chart -->
|
|
<div class="col-lg-12">
|
|
<div class="surface-card h-100">
|
|
<h3 class="h5 mb-3"><i class="bi bi-graph-up-arrow me-2 text-primary"></i><?= h(tr('أداء المبيعات الشهري', 'Monthly Sales Performance')) ?></h3>
|
|
<?php if (!$report['monthly_totals']): ?>
|
|
<div class="empty-state compact"><h4><?= h(tr('لا توجد بيانات', 'No data')) ?></h4></div>
|
|
<?php else: ?>
|
|
<canvas id="monthlySalesChart" height="80"></canvas>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const ctx = document.getElementById('monthlySalesChart').getContext('2d');
|
|
const rawData = <?= json_encode($report['monthly_totals']) ?>;
|
|
const labels = Object.keys(rawData);
|
|
const data = Object.values(rawData);
|
|
|
|
new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: '<?= h(tr('المبيعات', 'Sales')) ?>',
|
|
data: data,
|
|
borderColor: '#0d6efd',
|
|
backgroundColor: 'rgba(13, 110, 253, 0.1)',
|
|
borderWidth: 3,
|
|
fill: true,
|
|
tension: 0.4,
|
|
pointBackgroundColor: '#0d6efd',
|
|
pointRadius: 4,
|
|
pointHoverRadius: 6
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
plugins: { legend: { display: false } },
|
|
scales: { y: { beginAtZero: true } },
|
|
interaction: {
|
|
intersect: false,
|
|
mode: 'index',
|
|
},
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-6">
|
|
<div class="surface-card h-100">
|
|
<h3 class="h5 mb-4"><i class="bi bi-shop me-2 text-primary"></i><?= h(tr('المبيعات حسب الفرع', 'Sales by branch')) ?></h3>
|
|
<?php if (!$report['branch_totals']): ?>
|
|
<div class="empty-state compact"><h4><?= h(tr('لا توجد بيانات', 'No data')) ?></h4><p><?= h(tr('أضف عملية بيع أولاً لبدء التقارير.', 'Add a first sale to activate reports.')) ?></p></div>
|
|
<?php else: ?>
|
|
<div class="d-grid gap-3">
|
|
<?php
|
|
foreach ($report['branch_totals'] as $branchCode => $amount):
|
|
$percent = $report['gross'] > 0 ? ($amount / $report['gross']) * 100 : 0;
|
|
?>
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<span class="fw-bold fs-6"><?= h(branch_label((string) $branchCode)) ?></span>
|
|
<div class="text-end">
|
|
<strong class="d-block"><?= h(currency((float) $amount)) ?></strong>
|
|
<small class="text-muted"><?= round($percent, 1) ?>%</small>
|
|
</div>
|
|
</div>
|
|
<div class="progress" style="height: 8px;">
|
|
<div class="progress-bar bg-primary rounded-pill" role="progressbar" style="width: <?= $percent ?>%"></div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-6">
|
|
<div class="surface-card h-100">
|
|
<h3 class="h5 mb-4"><i class="bi bi-credit-card me-2 text-primary"></i><?= h(tr('المبيعات حسب الدفع', 'Sales by payment')) ?></h3>
|
|
<?php if (!$report['payment_totals']): ?>
|
|
<div class="empty-state compact"><h4><?= h(tr('بانتظار البيانات', 'Waiting for data')) ?></h4><p><?= h(tr('عند تسجيل عمليات بيع ستظهر هنا طرق الدفع.', 'Payment mix will appear here once sales are logged.')) ?></p></div>
|
|
<?php else: ?>
|
|
<div class="d-grid gap-3">
|
|
<?php
|
|
$colors = ['cash' => 'success', 'card' => 'info', 'bank' => 'warning', 'mixed' => 'secondary'];
|
|
foreach ($report['payment_totals'] as $payment => $amount):
|
|
$percent = $report['gross'] > 0 ? ($amount / $report['gross']) * 100 : 0;
|
|
$bg = $colors[$payment] ?? 'primary';
|
|
?>
|
|
<div>
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<span class="fw-bold fs-6"><?= h(ucfirst((string) $payment)) ?></span>
|
|
<div class="text-end">
|
|
<strong class="d-block"><?= h(currency((float) $amount)) ?></strong>
|
|
<small class="text-muted"><?= round($percent, 1) ?>%</small>
|
|
</div>
|
|
</div>
|
|
<div class="progress" style="height: 8px;">
|
|
<div class="progress-bar bg-<?= $bg ?> rounded-pill" role="progressbar" style="width: <?= $percent ?>%"></div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
<?php endif; ?>
|
|
|
|
<?php require __DIR__ . '/includes/footer.php'; ?>
|