38960-vm/includes/pages/reports.php
2026-03-22 10:04:41 +00:00

531 lines
20 KiB
PHP

<?php
// Default Date Range: Current Month
$start_date = $_GET['start_date'] ?? date('Y-m-01');
$end_date = $_GET['end_date'] ?? date('Y-m-t');
// Ensure dates are valid
if (!$start_date) $start_date = date('Y-m-01');
if (!$end_date) $end_date = date('Y-m-t');
// --- QUERIES ---
// 1. New Patients
$stmt = $db->prepare("SELECT COUNT(*) FROM patients WHERE DATE(created_at) BETWEEN ? AND ?");
$stmt->execute([$start_date, $end_date]);
$new_patients = $stmt->fetchColumn();
// 2. Total Visits
$stmt = $db->prepare("SELECT COUNT(*) FROM visits WHERE DATE(visit_date) BETWEEN ? AND ?");
$stmt->execute([$start_date, $end_date]);
$total_visits = $stmt->fetchColumn();
// 3. Total Revenue
$stmt = $db->prepare("SELECT SUM(total_amount) FROM bills WHERE DATE(created_at) BETWEEN ? AND ?");
$stmt->execute([$start_date, $end_date]);
$total_revenue = $stmt->fetchColumn() ?: 0;
// 4. Pending Bills (Outstanding amount for bills created in this period)
$stmt = $db->prepare("SELECT SUM(patient_payable) FROM bills WHERE status != 'Paid' AND DATE(created_at) BETWEEN ? AND ?");
$stmt->execute([$start_date, $end_date]);
$pending_bills = $stmt->fetchColumn() ?: 0;
// 5. Revenue Trend (Daily)
$stmt = $db->prepare("SELECT DATE(created_at) as date, SUM(total_amount) as total FROM bills WHERE DATE(created_at) BETWEEN ? AND ? GROUP BY DATE(created_at) ORDER BY date ASC");
$stmt->execute([$start_date, $end_date]);
$revenue_trend = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 6. Visits by Doctor (Top 5)
// Updated to join employees
$stmt = $db->prepare("
SELECT d.name_$lang as doctor_name, COUNT(v.id) as count
FROM visits v
JOIN employees d ON v.doctor_id = d.id
WHERE DATE(v.visit_date) BETWEEN ? AND ?
GROUP BY v.doctor_id
ORDER BY count DESC
LIMIT 5
");
$stmt->execute([$start_date, $end_date]);
$visits_by_doctor = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 7. Patient Gender Distribution
$stmt = $db->prepare("SELECT gender, COUNT(*) as count FROM patients WHERE DATE(created_at) BETWEEN ? AND ? GROUP BY gender");
$stmt->execute([$start_date, $end_date]);
$patients_by_gender = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
// 8. Department Revenue Report
// Updated to join employees
$stmt = $db->prepare("
SELECT d.name_$lang as name,
COUNT(DISTINCT b.visit_id) as visit_count,
SUM(b.total_amount) as revenue
FROM bills b
JOIN visits v ON b.visit_id = v.id
JOIN employees doc ON v.doctor_id = doc.id
JOIN departments d ON doc.department_id = d.id
WHERE b.status = 'Paid' AND DATE(b.created_at) BETWEEN ? AND ?
GROUP BY d.id
ORDER BY revenue DESC
");
$stmt->execute([$start_date, $end_date]);
$dept_revenue = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 9. Doctor Revenue Report
// Updated to join employees
$stmt = $db->prepare("
SELECT doc.name_$lang as name,
d.name_$lang as department_name,
COUNT(DISTINCT b.visit_id) as visit_count,
SUM(b.total_amount) as revenue
FROM bills b
JOIN visits v ON b.visit_id = v.id
JOIN employees doc ON v.doctor_id = doc.id
LEFT JOIN departments d ON doc.department_id = d.id
WHERE b.status = 'Paid' AND DATE(b.created_at) BETWEEN ? AND ?
GROUP BY doc.id
ORDER BY revenue DESC
");
$stmt->execute([$start_date, $end_date]);
$doctor_revenue = $stmt->fetchAll(PDO::FETCH_ASSOC);
// --- PREPARE DATA FOR JS ---
$chart_dept_revenue_labels = array_column($dept_revenue, 'name');
$chart_dept_revenue_data = array_column($dept_revenue, 'revenue');
$chart_revenue_labels = array_column($revenue_trend, 'date');
$chart_revenue_data = array_column($revenue_trend, 'total');
$chart_doctor_labels = array_column($visits_by_doctor, 'doctor_name');
$chart_doctor_data = array_column($visits_by_doctor, 'count');
$chart_gender_labels = array_keys($patients_by_gender);
$chart_gender_data = array_values($patients_by_gender);
?>
<!-- Include Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
@media print {
/* Hide Non-Printable Elements */
#sidebar, .navbar, .btn, form, footer, .no-print {
display: none !important;
}
/* Layout Adjustments for Print */
.main-content {
margin-left: 0 !important;
padding: 0 !important;
width: 100% !important;
}
body {
background: white !important;
font-size: 12pt;
}
.card {
border: none !important;
box-shadow: none !important;
}
.card-header {
background: none !important;
border-bottom: 2px solid #000 !important;
padding-left: 0 !important;
}
/* Ensure Charts are Visible */
canvas {
max-width: 100% !important;
height: auto !important;
}
/* Table Styling for Print */
.table {
width: 100% !important;
border-collapse: collapse !important;
}
.table th, .table td {
border: 1px solid #ddd !important;
padding: 8px !important;
}
/* Page Break */
.page-break {
page-break-before: always;
}
/* Header for Print */
.print-header {
display: block !important;
text-align: center;
margin-bottom: 20px;
}
}
.print-header {
display: none;
}
</style>
<!-- Print Header -->
<div class="print-header">
<h2><?php echo __('admin_reports'); ?></h2>
<p class="text-muted">
<?php echo __('report_period'); ?>: <?php echo $start_date; ?> <?php echo __('to'); ?> <?php echo $end_date; ?><br>
<?php echo __('generated_on'); ?>: <?php echo date('Y-m-d H:i'); ?>
</p>
</div>
<div class="d-flex justify-content-between align-items-center mb-4 no-print">
<h3 class="fw-bold text-secondary"><?php echo __('admin_reports'); ?></h3>
<div class="d-flex gap-2">
<!-- Date Filter Form -->
<form method="GET" class="d-flex gap-2 align-items-center">
<div class="input-group">
<span class="input-group-text bg-white border-end-0"><i class="bi bi-calendar"></i></span>
<input type="date" name="start_date" class="form-control border-start-0" value="<?php echo $start_date; ?>" required>
</div>
<span class="text-muted">-</span>
<div class="input-group">
<span class="input-group-text bg-white border-end-0"><i class="bi bi-calendar"></i></span>
<input type="date" name="end_date" class="form-control border-start-0" value="<?php echo $end_date; ?>" required>
</div>
<button type="submit" class="btn btn-primary"><?php echo __('generate'); ?></button>
</form>
<button onclick="window.print()" class="btn btn-outline-dark">
<i class="bi bi-printer"></i> <?php echo __('print_report'); ?>
</button>
</div>
</div>
<!-- Summary Cards -->
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="rounded-circle p-3 bg-primary bg-opacity-10 text-primary me-3">
<i class="bi bi-people fs-3"></i>
</div>
<div>
<h6 class="text-muted mb-1"><?php echo __('new_patients'); ?></h6>
<h4 class="fw-bold mb-0"><?php echo number_format($new_patients); ?></h4>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="rounded-circle p-3 bg-success bg-opacity-10 text-success me-3">
<i class="bi bi-clipboard2-pulse fs-3"></i>
</div>
<div>
<h6 class="text-muted mb-1"><?php echo __('total_visits'); ?></h6>
<h4 class="fw-bold mb-0"><?php echo number_format($total_visits); ?></h4>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="rounded-circle p-3 bg-info bg-opacity-10 text-info me-3">
<i class="bi bi-currency-dollar fs-3"></i>
</div>
<div>
<h6 class="text-muted mb-1"><?php echo __('total_revenue'); ?></h6>
<h4 class="fw-bold mb-0"><?php echo format_currency($total_revenue); ?></h4>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="rounded-circle p-3 bg-warning bg-opacity-10 text-warning me-3">
<i class="bi bi-exclamation-circle fs-3"></i>
</div>
<div>
<h6 class="text-muted mb-1"><?php echo __('pending_bills'); ?></h6>
<h4 class="fw-bold mb-0"><?php echo format_currency($pending_bills); ?></h4>
</div>
</div>
</div>
</div>
</div>
<!-- Charts Row 1 -->
<div class="row g-4 mb-4 page-break">
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-bottom-0 pt-4 px-4">
<h5 class="fw-bold text-dark mb-0"><?php echo __('department_revenue'); ?></h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center" style="min-height: 350px;">
<div style="position: relative; width: 300px; height: 300px;">
<canvas id="deptRevenueChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-bottom-0 pt-4 px-4">
<h5 class="fw-bold text-dark mb-0"><?php echo __('revenue_trend'); ?></h5>
</div>
<div class="card-body" style="min-height: 350px;">
<div style="position: relative; height: 300px; width: 100%;">
<canvas id="revenueChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Charts Row 2 -->
<div class="row g-4 mb-5 page-break">
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-bottom-0 pt-4 px-4">
<h5 class="fw-bold text-dark mb-0"><?php echo __('top_doctors'); ?></h5>
</div>
<div class="card-body" style="min-height: 350px;">
<div style="position: relative; height: 300px; width: 100%;">
<canvas id="doctorsChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-bottom-0 pt-4 px-4">
<h5 class="fw-bold text-dark mb-0"><?php echo __('new_patients'); ?> (<?php echo __('gender'); ?>)</h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center" style="min-height: 350px;">
<div style="position: relative; width: 300px; height: 300px;">
<canvas id="genderChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Detailed Tables -->
<div class="row g-4 mb-4 page-break">
<!-- Department Revenue -->
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-bottom-0 pt-4 px-4 d-flex justify-content-between align-items-center">
<h5 class="fw-bold text-dark mb-0"><?php echo __('department_revenue'); ?></h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light text-secondary">
<tr>
<th class="px-4 py-3"><?php echo __('department'); ?></th>
<th class="py-3 text-center"><?php echo __('visits'); ?> (Paid)</th>
<th class="py-3 text-end px-4"><?php echo __('revenue'); ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($dept_revenue)): ?>
<tr><td colspan="3" class="text-center py-4 text-muted"><?php echo __('no_data_found'); ?></td></tr>
<?php else: ?>
<?php foreach ($dept_revenue as $d): ?>
<tr>
<td class="px-4 fw-semibold"><?php echo htmlspecialchars($d['name']); ?></td>
<td class="text-center"><?php echo number_format($d['visit_count']); ?></td>
<td class="text-end px-4 text-success fw-bold"><?php echo format_currency($d['revenue']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Doctor Revenue -->
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-bottom-0 pt-4 px-4 d-flex justify-content-between align-items-center">
<h5 class="fw-bold text-dark mb-0"><?php echo __('doctor_revenue'); ?></h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light text-secondary">
<tr>
<th class="px-4 py-3"><?php echo __('doctor'); ?></th>
<th class="py-3 text-center"><?php echo __('visits'); ?> (Paid)</th>
<th class="py-3 text-end px-4"><?php echo __('revenue'); ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($doctor_revenue)): ?>
<tr><td colspan="3" class="text-center py-4 text-muted"><?php echo __('no_data_found'); ?></td></tr>
<?php else: ?>
<?php foreach ($doctor_revenue as $d): ?>
<tr>
<td class="px-4">
<div class="fw-semibold text-dark"><?php echo htmlspecialchars($d['name']); ?></div>
<small class="text-muted"><?php echo htmlspecialchars($d['department_name'] ?? '-'); ?></small>
</td>
<td class="text-center"><?php echo number_format($d['visit_count']); ?></td>
<td class="text-end px-4 text-success fw-bold"><?php echo format_currency($d['revenue']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Colors
const colors = {
primary: '#0d6efd',
success: '#198754',
info: '#0dcaf0',
warning: '#ffc107',
danger: '#dc3545',
secondary: '#6c757d',
light: '#f8f9fa',
dark: '#212529',
purple: '#6f42c1',
indigo: '#6610f2',
pink: '#d63384',
teal: '#20c997'
};
// Palette for charts
const chartColors = [
colors.primary,
colors.success,
colors.info,
colors.warning,
colors.danger,
colors.purple,
colors.teal,
colors.pink,
colors.indigo,
colors.secondary
];
// 1. Department Revenue (Pie) - Replaces Visits by Status
const ctxDeptRevenue = document.getElementById('deptRevenueChart').getContext('2d');
new Chart(ctxDeptRevenue, {
type: 'pie',
data: {
labels: <?php echo json_encode($chart_dept_revenue_labels); ?>,
datasets: [{
data: <?php echo json_encode($chart_dept_revenue_data); ?>,
backgroundColor: chartColors,
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'bottom' },
tooltip: {
callbacks: {
label: function(context) {
let label = context.label || '';
if (label) {
label += ': ';
}
if (context.parsed !== null) {
label += new Intl.NumberFormat().format(context.parsed);
}
return label;
}
}
}
}
}
});
// 2. Revenue Trend (Line)
const ctxRevenue = document.getElementById('revenueChart').getContext('2d');
new Chart(ctxRevenue, {
type: 'line',
data: {
labels: <?php echo json_encode($chart_revenue_labels); ?>,
datasets: [{
label: '<?php echo __('revenue'); ?>',
data: <?php echo json_encode($chart_revenue_data); ?>,
borderColor: colors.info,
backgroundColor: 'rgba(13, 202, 240, 0.1)',
tension: 0.3,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: { beginAtZero: true }
}
}
});
// 3. Top Doctors (Bar)
const ctxDoctors = document.getElementById('doctorsChart').getContext('2d');
new Chart(ctxDoctors, {
type: 'bar',
data: {
labels: <?php echo json_encode($chart_doctor_labels); ?>,
datasets: [{
label: '<?php echo __('visits'); ?>',
data: <?php echo json_encode($chart_doctor_data); ?>,
backgroundColor: colors.primary,
borderRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y', // Horizontal Bar
scales: {
x: { beginAtZero: true }
}
}
});
// 4. Gender (Pie)
const ctxGender = document.getElementById('genderChart').getContext('2d');
new Chart(ctxGender, {
type: 'pie',
data: {
labels: <?php echo json_encode($chart_gender_labels); ?>,
datasets: [{
data: <?php echo json_encode($chart_gender_data); ?>,
backgroundColor: ['#36a2eb', '#ff6384', '#ffcd56'],
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'bottom' }
}
}
});
});
</script>