287 lines
12 KiB
PHP
287 lines
12 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/includes/layout.php'; require_role('admin');
|
|
|
|
// Helper to validate date
|
|
function validate_date($date, $format = 'Y-m-d') {
|
|
$d = DateTime::createFromFormat($format, $date);
|
|
return $d && $d->format($format) === $date;
|
|
}
|
|
|
|
// Params
|
|
$reportType = $_GET['type'] ?? 'countries_origin'; // countries_origin, countries_dest, cities_origin, cities_dest, shippers
|
|
$startDate = $_GET['start_date'] ?? date('Y-m-01'); // Default to first day of current month
|
|
$endDate = $_GET['end_date'] ?? date('Y-m-t'); // Default to last day of current month
|
|
$export = $_GET['export'] ?? null;
|
|
|
|
if (!validate_date($startDate)) $startDate = date('Y-m-01');
|
|
if (!validate_date($endDate)) $endDate = date('Y-m-t');
|
|
|
|
// Build Query
|
|
$where = "s.payment_status = 'paid' AND DATE(s.created_at) BETWEEN ? AND ?";
|
|
$params = [$startDate, $endDate];
|
|
|
|
$groupBy = '';
|
|
$selectName = '';
|
|
$join = '';
|
|
$orderBy = 'total_amount DESC';
|
|
|
|
$pageTitle = t('shipments_by_origin_country');
|
|
|
|
switch ($reportType) {
|
|
case 'countries_origin':
|
|
$selectName = "COALESCE(co.name_en, 'Unknown') as name";
|
|
$join = "LEFT JOIN cities c ON s.origin_city = c.name_en
|
|
LEFT JOIN countries co ON c.country_id = co.id";
|
|
$groupBy = "co.name_en";
|
|
$pageTitle = t('shipments_by_origin_country');
|
|
break;
|
|
|
|
case 'countries_dest':
|
|
$selectName = "COALESCE(co.name_en, 'Unknown') as name";
|
|
$join = "LEFT JOIN cities c ON s.destination_city = c.name_en
|
|
LEFT JOIN countries co ON c.country_id = co.id";
|
|
$groupBy = "co.name_en";
|
|
$pageTitle = t('shipments_by_dest_country');
|
|
break;
|
|
|
|
case 'cities_origin':
|
|
$selectName = "s.origin_city as name";
|
|
$groupBy = "s.origin_city";
|
|
$pageTitle = t('shipments_by_origin_city');
|
|
break;
|
|
|
|
case 'cities_dest':
|
|
$selectName = "s.destination_city as name";
|
|
$groupBy = "s.destination_city";
|
|
$pageTitle = t('shipments_by_dest_city');
|
|
break;
|
|
|
|
case 'shippers':
|
|
$selectName = "s.shipper_name as name"; // simplified, could join users table
|
|
$groupBy = "s.shipper_name";
|
|
$pageTitle = t('shipments_by_shipper');
|
|
break;
|
|
|
|
default:
|
|
$reportType = 'countries_origin';
|
|
$selectName = "COALESCE(co.name_en, 'Unknown') as name";
|
|
$join = "LEFT JOIN cities c ON s.origin_city = c.name_en
|
|
LEFT JOIN countries co ON c.country_id = co.id";
|
|
$groupBy = "co.name_en";
|
|
$pageTitle = t('shipments_by_origin_country');
|
|
break;
|
|
}
|
|
|
|
$sql = "
|
|
SELECT
|
|
$selectName,
|
|
COUNT(s.id) as shipment_count,
|
|
SUM(s.total_price) as total_amount,
|
|
SUM(s.platform_fee) as total_profit
|
|
FROM shipments s
|
|
$join
|
|
WHERE $where
|
|
GROUP BY $groupBy
|
|
ORDER BY $orderBy
|
|
";
|
|
|
|
try {
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute($params);
|
|
$results = $stmt->fetchAll();
|
|
} catch (PDOException $e) {
|
|
$results = [];
|
|
$error = "Database error: " . $e->getMessage();
|
|
}
|
|
|
|
// Calculate totals
|
|
$totalShipments = 0;
|
|
$grandTotalAmount = 0.0;
|
|
$grandTotalProfit = 0.0;
|
|
|
|
foreach ($results as $row) {
|
|
$totalShipments += $row['shipment_count'];
|
|
$grandTotalAmount += $row['total_amount'];
|
|
$grandTotalProfit += $row['total_profit'];
|
|
}
|
|
|
|
// Handle CSV Export
|
|
if ($export === 'csv') {
|
|
header('Content-Type: text/csv');
|
|
header('Content-Disposition: attachment; filename="report_' . $reportType . '_' . date('Ymd') . '.csv"');
|
|
|
|
$out = fopen('php://output', 'w');
|
|
|
|
// Header
|
|
fputcsv($out, ['Name', 'Shipments', 'Total Amount', 'Profit']);
|
|
|
|
// Rows
|
|
foreach ($results as $row) {
|
|
fputcsv($out, [
|
|
$row['name'],
|
|
$row['shipment_count'],
|
|
number_format((float)$row['total_amount'], 2, '.', ''),
|
|
number_format((float)$row['total_profit'], 2, '.', '')
|
|
]);
|
|
}
|
|
|
|
// Footer
|
|
fputcsv($out, ['Total', $totalShipments, number_format($grandTotalAmount, 2, '.', ''), number_format($grandTotalProfit, 2, '.', '')]);
|
|
|
|
fclose($out);
|
|
exit;
|
|
}
|
|
|
|
render_header(t('summary_report'), 'reports_summary', true);
|
|
?>
|
|
|
|
<div class="row g-0">
|
|
<div class="col-md-2 bg-white border-end min-vh-100 d-print-none">
|
|
<?php render_admin_sidebar('reports_summary'); ?>
|
|
</div>
|
|
<div class="col-md-10 p-4">
|
|
|
|
<!-- Print Header -->
|
|
<div class="d-none d-print-block mb-4">
|
|
<div class="row align-items-center mb-4">
|
|
<div class="col-6">
|
|
<?php
|
|
$logoPath = get_setting('logo_path');
|
|
if ($logoPath): ?>
|
|
<img src="<?= e($logoPath) ?>" alt="Company Logo" style="max-height: 80px; width: auto;">
|
|
<?php else: ?>
|
|
<h2 class="fw-bold mb-0 text-dark"><?= e(get_setting('company_name', 'CargoLink')) ?></h2>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="col-6 text-end">
|
|
<h3 class="mb-1 text-dark fw-bold"><?= e(t('summary_report')) ?></h3>
|
|
<p class="mb-0 text-muted small">
|
|
<?= e(get_setting('company_address')) ?><br>
|
|
<?= e(get_setting('company_email')) ?> | <?= e(get_setting('company_phone')) ?>
|
|
</p>
|
|
<p class="mb-0 text-muted small mt-2">
|
|
<?= e(t('generated')) ?>: <?= date('d M Y, H:i') ?>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="border p-3 rounded bg-light">
|
|
<div class="row">
|
|
<div class="col-6">
|
|
<strong class="text-muted d-block small text-uppercase"><?= e(t('report_type')) ?></strong>
|
|
<span class="fs-5 fw-bold text-dark"><?= e($pageTitle) ?></span>
|
|
</div>
|
|
<div class="col-6 text-end">
|
|
<strong class="text-muted d-block small text-uppercase"><?= e(t('period')) ?></strong>
|
|
<span class="fs-5 fw-bold text-dark"><?= e($startDate) ?> — <?= e($endDate) ?></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4 d-print-none">
|
|
<div>
|
|
<h1 class="section-title mb-1"><?= e(t('summary_report')) ?></h1>
|
|
<p class="muted mb-0"><?= e(t('analyze_performance')) ?></p>
|
|
</div>
|
|
<div>
|
|
<a href="?type=<?= e($reportType) ?>&start_date=<?= e($startDate) ?>&end_date=<?= e($endDate) ?>&export=csv" class="btn btn-outline-primary btn-sm me-2">
|
|
<i class="bi bi-download me-2"></i><?= e(t('export_csv')) ?>
|
|
</a>
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="window.print()">
|
|
<i class="bi bi-printer me-2"></i><?= e(t('print')) ?>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel p-4 mb-4 d-print-none">
|
|
<form method="get" class="row g-3 align-items-end">
|
|
<div class="col-md-3">
|
|
<label class="form-label small text-muted"><?= e(t('report_type')) ?></label>
|
|
<select name="type" class="form-select" onchange="this.form.submit()">
|
|
<option value="countries_origin" <?= $reportType === 'countries_origin' ? 'selected' : '' ?>><?= e(t('shipments_by_origin_country')) ?></option>
|
|
<option value="countries_dest" <?= $reportType === 'countries_dest' ? 'selected' : '' ?>><?= e(t('shipments_by_dest_country')) ?></option>
|
|
<option value="cities_origin" <?= $reportType === 'cities_origin' ? 'selected' : '' ?>><?= e(t('shipments_by_origin_city')) ?></option>
|
|
<option value="cities_dest" <?= $reportType === 'cities_dest' ? 'selected' : '' ?>><?= e(t('shipments_by_dest_city')) ?></option>
|
|
<option value="shippers" <?= $reportType === 'shippers' ? 'selected' : '' ?>><?= e(t('shipments_by_shipper')) ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small text-muted"><?= e(t('start_date')) ?></label>
|
|
<input type="date" name="start_date" class="form-control" value="<?= e($startDate) ?>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small text-muted"><?= e(t('end_date')) ?></label>
|
|
<input type="date" name="end_date" class="form-control" value="<?= e($endDate) ?>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="submit" class="btn btn-primary w-100"><?= e(t('apply_filter')) ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="panel p-0 print-no-shadow">
|
|
<div class="p-3 border-bottom bg-light d-flex justify-content-between align-items-center d-print-none">
|
|
<h5 class="mb-0 fw-bold"><?= e($pageTitle) ?></h5>
|
|
<span class="badge bg-secondary"><?= e($startDate) ?> to <?= e($endDate) ?></span>
|
|
</div>
|
|
|
|
<?php if (empty($results)): ?>
|
|
<div class="p-5 text-center text-muted">
|
|
<i class="bi bi-bar-chart fs-1 mb-3 d-block"></i>
|
|
<p class="mb-0"><?= e(t('no_paid_shipments')) ?></p>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0 align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th class="ps-4"><?= e(t('name')) ?></th>
|
|
<th class="text-center"><?= e(t('stats_shipments')) ?></th>
|
|
<th class="text-end"><?= e(t('total_amount')) ?></th>
|
|
<th class="text-end pe-4"><?= e(t('profit')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($results as $row): ?>
|
|
<tr>
|
|
<td class="ps-4 fw-medium"><?= e($row['name']) ?></td>
|
|
<td class="text-center"><?= number_format((int)$row['shipment_count']) ?></td>
|
|
<td class="text-end text-dark"><?= format_currency((float)$row['total_amount']) ?></td>
|
|
<td class="text-end pe-4 text-success fw-bold"><?= format_currency((float)$row['total_profit']) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
<tfoot class="table-light fw-bold border-top">
|
|
<tr>
|
|
<td class="ps-4"><?= e(t('total_label')) ?></td>
|
|
<td class="text-center"><?= number_format($totalShipments) ?></td>
|
|
<td class="text-end"><?= format_currency($grandTotalAmount) ?></td>
|
|
<td class="text-end pe-4 text-success"><?= format_currency($grandTotalProfit) ?></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Print Footer -->
|
|
<div class="d-none d-print-block print-footer border-top pt-2">
|
|
<div class="row text-muted small">
|
|
<div class="col-6">
|
|
<?= e(t('printed_by')) ?>: <?= e($_SESSION['user_name'] ?? 'Admin') ?>
|
|
</div>
|
|
<div class="col-6 text-end">
|
|
<?= e(t('generated')) ?>: <?= date('d M Y H:i') ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php render_footer(); ?>
|