208 lines
8.4 KiB
PHP
208 lines
8.4 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/includes/layout.php';
|
|
|
|
// 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
|
|
|
|
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';
|
|
|
|
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 = "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 = "Shipments by Destination Country";
|
|
break;
|
|
|
|
case 'cities_origin':
|
|
$selectName = "s.origin_city as name";
|
|
$groupBy = "s.origin_city";
|
|
$pageTitle = "Shipments by Origin City";
|
|
break;
|
|
|
|
case 'cities_dest':
|
|
$selectName = "s.destination_city as name";
|
|
$groupBy = "s.destination_city";
|
|
$pageTitle = "Shipments by Destination City";
|
|
break;
|
|
|
|
case 'shippers':
|
|
$selectName = "s.shipper_name as name"; // simplified, could join users table
|
|
$groupBy = "s.shipper_name";
|
|
$pageTitle = "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 = "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 for footer
|
|
$totalShipments = 0;
|
|
$grandTotalAmount = 0.0;
|
|
$grandTotalProfit = 0.0;
|
|
|
|
foreach ($results as $row) {
|
|
$totalShipments += $row['shipment_count'];
|
|
$grandTotalAmount += $row['total_amount'];
|
|
$grandTotalProfit += $row['total_profit'];
|
|
}
|
|
|
|
render_header('Reports Summary', 'reports_summary', true);
|
|
?>
|
|
|
|
<div class="row g-0">
|
|
<div class="col-md-2 bg-white border-end min-vh-100">
|
|
<?php render_admin_sidebar('reports_summary'); ?>
|
|
</div>
|
|
<div class="col-md-10 p-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h1 class="section-title mb-1">Summary Reports</h1>
|
|
<p class="muted mb-0">Analyze shipment performance, revenue, and profits.</p>
|
|
</div>
|
|
<div>
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="window.print()">
|
|
<i class="bi bi-printer me-2"></i>Print
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel p-4 mb-4">
|
|
<form method="get" class="row g-3 align-items-end">
|
|
<div class="col-md-3">
|
|
<label class="form-label small text-muted">Report Type</label>
|
|
<select name="type" class="form-select" onchange="this.form.submit()">
|
|
<option value="countries_origin" <?= $reportType === 'countries_origin' ? 'selected' : '' ?>>By Origin Country</option>
|
|
<option value="countries_dest" <?= $reportType === 'countries_dest' ? 'selected' : '' ?>>By Destination Country</option>
|
|
<option value="cities_origin" <?= $reportType === 'cities_origin' ? 'selected' : '' ?>>By Origin City</option>
|
|
<option value="cities_dest" <?= $reportType === 'cities_dest' ? 'selected' : '' ?>>By Destination City</option>
|
|
<option value="shippers" <?= $reportType === 'shippers' ? 'selected' : '' ?>>By Shipper</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small text-muted">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">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">Apply Filter</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="panel p-0">
|
|
<div class="p-3 border-bottom bg-light d-flex justify-content-between align-items-center">
|
|
<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">No paid shipments found for this period.</p>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0 align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="ps-4">Name</th>
|
|
<th class="text-center">Shipments</th>
|
|
<th class="text-end">Total Amount</th>
|
|
<th class="text-end pe-4">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">$<?= number_format((float)$row['total_amount'], 2) ?></td>
|
|
<td class="text-end pe-4 text-success fw-bold">$<?= number_format((float)$row['total_profit'], 2) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
<tfoot class="table-light fw-bold border-top">
|
|
<tr>
|
|
<td class="ps-4">TOTAL</td>
|
|
<td class="text-center"><?= number_format($totalShipments) ?></td>
|
|
<td class="text-end">$<?= number_format($grandTotalAmount, 2) ?></td>
|
|
<td class="text-end pe-4 text-success">$<?= number_format($grandTotalProfit, 2) ?></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
@media print {
|
|
.admin-sidebar, .btn, form { display: none !important; }
|
|
.col-md-10 { width: 100% !important; padding: 0 !important; }
|
|
.panel { border: none !important; box-shadow: none !important; }
|
|
}
|
|
</style>
|
|
|
|
<?php render_footer(); ?>
|