266 lines
16 KiB
PHP
266 lines
16 KiB
PHP
<?= $this->extend('layout/management_layout') ?>
|
|
<?= $this->section('content') ?>
|
|
|
|
<?php
|
|
$filters = $filters ?? [];
|
|
$result = $result ?? null;
|
|
$summary = $result['summary'] ?? [];
|
|
$mode = $filters['calculator_mode'] ?? 'compare';
|
|
$fmtMoney = static fn($value): string => '$' . number_format((float) $value, 2);
|
|
$warningText = static function (array $warnings): string {
|
|
$warnings = array_values(array_filter(array_map('trim', $warnings), 'strlen'));
|
|
return $warnings === [] ? 'None' : implode(' | ', $warnings);
|
|
};
|
|
?>
|
|
|
|
<div class="container-fluid mt-4">
|
|
<div class="d-flex flex-wrap justify-content-between align-items-end gap-3 mb-4">
|
|
<div>
|
|
<h2 class="mt-4 mb-1">Tuition Collection Forecast</h2>
|
|
<p class="text-muted mb-0">Uses actual enrollment families from the selected school year to compare old and new projected tuition income.</p>
|
|
</div>
|
|
<?php if ($result): ?>
|
|
<?php
|
|
$exportQuery = http_build_query([
|
|
'school_year' => $result['school_year'] ?? '',
|
|
'semester' => $result['semester'] ?? '',
|
|
'calculator_mode' => $result['calculator_mode'] ?? 'compare',
|
|
'include_withdrawn_mode' => $filters['include_withdrawn_mode'] ?? 'refund_deadline',
|
|
'include_payment_pending' => !empty($filters['include_payment_pending']) ? '1' : '0',
|
|
'include_event_only' => !empty($filters['include_event_only']) ? '1' : '0',
|
|
'include_paid_invoices' => !empty($filters['include_paid_invoices']) ? '1' : '0',
|
|
'unit_price' => $filters['unit_price'] ?? '',
|
|
'youth_unit_price' => $filters['youth_unit_price'] ?? '',
|
|
]);
|
|
?>
|
|
<a href="<?= site_url('administrator/tuition-forecast/export?' . $exportQuery) ?>" class="btn btn-success">
|
|
Export CSV
|
|
</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="card shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<form method="get" action="<?= site_url('administrator/tuition-forecast') ?>" class="row g-3 align-items-end">
|
|
<div class="col-md-3">
|
|
<label for="school_year" class="form-label">School Year</label>
|
|
<select id="school_year" name="school_year" class="form-select">
|
|
<?php foreach (($schoolYears ?? []) as $schoolYear): ?>
|
|
<option value="<?= esc($schoolYear) ?>" <?= ($filters['school_year'] ?? '') === $schoolYear ? 'selected' : '' ?>>
|
|
<?= esc($schoolYear) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="semester" class="form-label">Semester Filter</label>
|
|
<select id="semester" name="semester" class="form-select">
|
|
<option value="" <?= ($filters['semester'] ?? '') === '' ? 'selected' : '' ?>>All Year</option>
|
|
<?php foreach (($semesters ?? []) as $semester): ?>
|
|
<option value="<?= esc($semester) ?>" <?= ($filters['semester'] ?? '') === $semester ? 'selected' : '' ?>>
|
|
<?= esc($semester) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="calculator_mode" class="form-label">Calculator Mode</label>
|
|
<select id="calculator_mode" name="calculator_mode" class="form-select">
|
|
<option value="compare" <?= $mode === 'compare' ? 'selected' : '' ?>>Compare Both</option>
|
|
<option value="old" <?= $mode === 'old' ? 'selected' : '' ?>>Old Only</option>
|
|
<option value="new" <?= $mode === 'new' ? 'selected' : '' ?>>New Only</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="include_withdrawn_mode" class="form-label">Withdrawn Students</label>
|
|
<select id="include_withdrawn_mode" name="include_withdrawn_mode" class="form-select">
|
|
<option value="refund_deadline" <?= ($filters['include_withdrawn_mode'] ?? 'refund_deadline') === 'refund_deadline' ? 'selected' : '' ?>>Use Refund Deadline Rule</option>
|
|
<option value="include" <?= ($filters['include_withdrawn_mode'] ?? '') === 'include' ? 'selected' : '' ?>>Always Include</option>
|
|
<option value="exclude" <?= ($filters['include_withdrawn_mode'] ?? '') === 'exclude' ? 'selected' : '' ?>>Always Exclude</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="unit_price" class="form-label">Grades Unit Price</label>
|
|
<input
|
|
type="number"
|
|
min="0"
|
|
step="0.01"
|
|
id="unit_price"
|
|
name="unit_price"
|
|
class="form-control"
|
|
value="<?= esc((string) ($filters['unit_price'] ?? ($summary['unit_price'] ?? ''))) ?>"
|
|
placeholder="New tuition unit price for grades">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="youth_unit_price" class="form-label">Youth Unit Price</label>
|
|
<input
|
|
type="number"
|
|
min="0"
|
|
step="0.01"
|
|
id="youth_unit_price"
|
|
name="youth_unit_price"
|
|
class="form-control"
|
|
value="<?= esc((string) ($filters['youth_unit_price'] ?? ($summary['youth_unit_price'] ?? ''))) ?>"
|
|
placeholder="New tuition unit price for youth">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="include_payment_pending" name="include_payment_pending" value="1" <?= !empty($filters['include_payment_pending']) ? 'checked' : '' ?>>
|
|
<label class="form-check-label" for="include_payment_pending">Include payment-pending students</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="include_event_only" name="include_event_only" value="1" <?= !empty($filters['include_event_only']) ? 'checked' : '' ?>>
|
|
<label class="form-check-label" for="include_event_only">Show event-only students in warnings</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<button type="submit" class="btn btn-primary">Run Forecast</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($result): ?>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-xl col-lg-3 col-md-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="text-muted small">Total Parents</div>
|
|
<div class="fs-4 fw-semibold"><?= esc((string) ($summary['family_count'] ?? 0)) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl col-lg-3 col-md-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="text-muted small">Total Students</div>
|
|
<div class="fs-4 fw-semibold"><?= esc((string) ($summary['student_count'] ?? 0)) ?></div>
|
|
<div class="small text-muted">Billable: <?= esc((string) ($summary['billable_student_count'] ?? 0)) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl col-lg-3 col-md-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="text-muted small">Old Projected Income</div>
|
|
<div class="fs-5 fw-semibold"><?= esc($fmtMoney($summary['old_projected_income'] ?? ($summary['old_projected_tuition'] ?? 0))) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl col-lg-3 col-md-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="text-muted small">New Projected Income</div>
|
|
<div class="fs-5 fw-semibold"><?= esc($fmtMoney($summary['new_projected_income'] ?? ($summary['new_projected_tuition'] ?? 0))) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl col-lg-3 col-md-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="text-muted small">Grades Unit Price</div>
|
|
<div class="fs-5 fw-semibold"><?= esc($fmtMoney($summary['unit_price'] ?? 0)) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl col-lg-3 col-md-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="text-muted small">Youth Unit Price</div>
|
|
<div class="fs-5 fw-semibold"><?= esc($fmtMoney($summary['youth_unit_price'] ?? 0)) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl col-lg-3 col-md-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="text-muted small">Difference</div>
|
|
<div class="fs-5 fw-semibold <?= ((float) ($summary['difference'] ?? 0)) >= 0 ? 'text-success' : 'text-danger' ?>">
|
|
<?= esc($fmtMoney($summary['difference'] ?? 0)) ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card shadow-sm">
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Parent / Family</th>
|
|
<th>Student Count</th>
|
|
<th>Billable Student Count</th>
|
|
<th>Old Tuition Total</th>
|
|
<th>New Tuition Total</th>
|
|
<th>Difference</th>
|
|
<th>Warnings</th>
|
|
<th>Details</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (!empty($result['families'])): ?>
|
|
<?php foreach ($result['families'] as $family): ?>
|
|
<tr>
|
|
<td><?= esc($family['parent_name'] ?? '') ?></td>
|
|
<td><?= esc((string) ($family['student_count'] ?? 0)) ?></td>
|
|
<td><?= esc((string) ($family['billable_student_count'] ?? 0)) ?></td>
|
|
<td><?= esc($fmtMoney($family['old_total'] ?? 0)) ?></td>
|
|
<td><?= esc($fmtMoney($family['new_total'] ?? 0)) ?></td>
|
|
<td class="<?= ((float) ($family['difference'] ?? 0)) >= 0 ? 'text-success' : 'text-danger' ?>">
|
|
<?= esc($fmtMoney($family['difference'] ?? 0)) ?>
|
|
</td>
|
|
<td class="small"><?= esc($warningText($family['warnings'] ?? [])) ?></td>
|
|
<td>
|
|
<details>
|
|
<summary>Students</summary>
|
|
<div class="table-responsive mt-2">
|
|
<table class="table table-sm table-striped mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Student</th>
|
|
<th>Grade</th>
|
|
<th>Billable</th>
|
|
<th>Excluded Reason</th>
|
|
<th>Old Rule</th>
|
|
<th>Old Amount</th>
|
|
<th>New Rule</th>
|
|
<th>New Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach (($family['student_details'] ?? []) as $detail): ?>
|
|
<tr>
|
|
<td><?= esc($detail['student_name'] ?? '') ?></td>
|
|
<td><?= esc($detail['grade_level'] ?? '') ?></td>
|
|
<td><?= !empty($detail['billable']) ? 'Yes' : 'No' ?></td>
|
|
<td><?= esc((string) ($detail['excluded_reason'] ?? '')) ?></td>
|
|
<td><?= esc((string) ($detail['old_rule'] ?? '')) ?></td>
|
|
<td><?= esc($fmtMoney($detail['old_amount'] ?? 0)) ?></td>
|
|
<td><?= esc((string) ($detail['new_rule'] ?? '')) ?></td>
|
|
<td><?= esc($fmtMoney($detail['new_amount'] ?? 0)) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</details>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php else: ?>
|
|
<tr>
|
|
<td colspan="8" class="text-center text-muted">No forecastable families matched the selected filters.</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?= $this->endSection() ?>
|