38808-vm/expenses_dashboard.php
2026-03-27 03:44:56 +00:00

228 lines
8.3 KiB
PHP

<?php
require_once __DIR__ . '/includes/header.php';
if (!canView('expenses')) {
redirect('index.php');
}
// Helper to get totals
function getExpenseStats($month, $year) {
$db = db();
$start_date = "$year-$month-01";
$end_date = date("Y-m-t", strtotime($start_date));
// Total for month
$stmt = $db->prepare("SELECT SUM(amount) FROM expenses WHERE date BETWEEN ? AND ?");
$stmt->execute([$start_date, $end_date]);
$total = $stmt->fetchColumn() ?: 0;
// By Category
$stmt = $db->prepare("SELECT c.name, SUM(e.amount) as total
FROM expenses e
JOIN expense_categories c ON e.category_id = c.id
WHERE e.date BETWEEN ? AND ?
GROUP BY c.name
ORDER BY total DESC");
$stmt->execute([$start_date, $end_date]);
$by_category = $stmt->fetchAll(PDO::FETCH_ASSOC);
return ['total' => $total, 'by_category' => $by_category];
}
// Current month stats
$current_month = date('m');
$current_year = date('Y');
$current_stats = getExpenseStats($current_month, $current_year);
// Last 6 months trend
$trend_data = [];
for ($i = 5; $i >= 0; $i--) {
$d = strtotime("-$i months");
$m = date('m', $d);
$y = date('Y', $d);
$s = getExpenseStats($m, $y);
$trend_data[] = [
'label' => date('M Y', $d), // English month names for Chart.js
'display_label' => date('m/Y', $d),
'total' => $s['total']
];
}
// Recent Expenses
$stmt = db()->query("SELECT e.*, c.name as category_name
FROM expenses e
JOIN expense_categories c ON e.category_id = c.id
ORDER BY e.date DESC, e.id DESC LIMIT 5");
$recent_expenses = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">لوحة تحكم المصروفات</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<a href="expenses.php" class="btn btn-sm btn-primary">
<i class="fas fa-list"></i> عرض السجل
</a>
</div>
</div>
<!-- Stats Cards -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-body">
<h6 class="text-muted mb-2">إجمالي مصروفات هذا الشهر</h6>
<h3 class="fw-bold text-danger mb-0"><?= number_format($current_stats['total'], 2) ?> ر.س</h3>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-body">
<h6 class="text-muted mb-2">أعلى تصنيف للصرف</h6>
<?php if (!empty($current_stats['by_category'])): ?>
<h3 class="fw-bold text-primary mb-0"><?= htmlspecialchars($current_stats['by_category'][0]['name']) ?></h3>
<small class="text-muted"><?= number_format($current_stats['by_category'][0]['total'], 2) ?> ر.س</small>
<?php else: ?>
<h3 class="fw-bold text-muted mb-0">-</h3>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-body">
<h6 class="text-muted mb-2">متوسط الصرف (آخر 6 أشهر)</h6>
<?php
$avg = array_sum(array_column($trend_data, 'total')) / count($trend_data);
?>
<h3 class="fw-bold text-info mb-0"><?= number_format($avg, 2) ?> ر.س</h3>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<!-- Trend Chart -->
<div class="col-md-8 mb-4 mb-md-0">
<div class="card shadow-sm border-0 h-100">
<div class="card-header bg-transparent border-0 d-flex justify-content-between align-items-center">
<h5 class="mb-0">اتجاه المصروفات (آخر 6 أشهر)</h5>
</div>
<div class="card-body">
<canvas id="trendChart" height="120"></canvas>
</div>
</div>
</div>
<!-- Category Pie Chart -->
<div class="col-md-4">
<div class="card shadow-sm border-0 h-100">
<div class="card-header bg-transparent border-0">
<h5 class="mb-0">توزيع المصروفات (<?= date('m/Y') ?>)</h5>
</div>
<div class="card-body position-relative">
<canvas id="categoryChart"></canvas>
</div>
</div>
</div>
</div>
<!-- Recent Expenses -->
<div class="card shadow-sm border-0">
<div class="card-header bg-transparent border-0">
<h5 class="mb-0">آخر المصروفات المسجلة</h5>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4">التاريخ</th>
<th>التصنيف</th>
<th>الوصف/المورد</th>
<th>المبلغ</th>
<th>طريقة الدفع</th>
</tr>
</thead>
<tbody>
<?php if (empty($recent_expenses)): ?>
<tr>
<td colspan="5" class="text-center py-4 text-muted">لا توجد مصروفات مسجلة</td>
</tr>
<?php else: ?>
<?php foreach ($recent_expenses as $exp): ?>
<tr>
<td class="ps-4"><?= $exp['date'] ?></td>
<td><span class="badge bg-secondary bg-opacity-10 text-secondary"><?= htmlspecialchars($exp['category_name']) ?></span></td>
<td>
<div class="fw-bold"><?= htmlspecialchars($exp['description'] ?: '-') ?></div>
<div class="small text-muted"><?= htmlspecialchars($exp['vendor'] ?: '') ?></div>
</td>
<td class="fw-bold text-danger"><?= number_format($exp['amount'], 2) ?></td>
<td><?= htmlspecialchars($exp['payment_method']) ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Trend Chart
const trendCtx = document.getElementById('trendChart').getContext('2d');
new Chart(trendCtx, {
type: 'bar',
data: {
labels: <?= json_encode(array_column($trend_data, 'display_label')) ?>,
datasets: [{
label: 'المصروفات',
data: <?= json_encode(array_column($trend_data, 'total')) ?>,
backgroundColor: 'rgba(232, 62, 140, 0.6)', // Pinkish
borderColor: 'rgba(232, 62, 140, 1)',
borderWidth: 1,
borderRadius: 4
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false }
},
scales: {
y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.05)' } },
x: { grid: { display: false } }
}
}
});
// Category Chart
const catCtx = document.getElementById('categoryChart').getContext('2d');
const catData = <?= json_encode($current_stats['by_category']) ?>;
new Chart(catCtx, {
type: 'doughnut',
data: {
labels: catData.map(d => d.name),
datasets: [{
data: catData.map(d => d.total),
backgroundColor: [
'#4e73df', '#1cc88a', '#36b9cc', '#f6c23e', '#e74a3b',
'#858796', '#5a5c69', '#fd7e14', '#20c997', '#6f42c1'
],
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '70%',
plugins: {
legend: { position: 'bottom', labels: { boxWidth: 12 } }
}
}
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>