apply the school year concept
Tests / PHPUnit (push) Failing after 1m19s

This commit is contained in:
root
2026-07-14 00:59:00 -04:00
parent 504c3bc9f9
commit feb1b29a32
73 changed files with 4288 additions and 620 deletions
@@ -6,16 +6,6 @@
<div class="content"></div>
<h2 class="text-center mt-4 mb-3">Invoice Management</h2>
<!-- Controls -->
<div class="row g-2 align-items-center justify-content-center mb-3">
<div class="col-auto">
<label for="schoolYearSelect" class="col-form-label">School year</label>
</div>
<div class="col-auto">
<select id="schoolYearSelect" class="form-select form-select-sm" style="min-width: 180px;"></select>
</div>
</div>
<!-- Table for displaying invoices (client-hydrated) -->
<table id="invoicesTable" class="table table-striped table-hover table-bordered w-100">
<thead class="thead-dark">
@@ -155,16 +145,20 @@
const $tbl = $('#invoicesTable');
let dt = null;
const $year = document.getElementById('schoolYearSelect');
const selectedSchoolYear = () => $year ? $year.value : '';
const syncSchoolYearSelect = (resp) => {
if (!$year) return;
const years = Array.isArray(resp.schoolYears) ? resp.schoolYears : [];
const selected = resp.schoolYear || (years[0] || '');
$year.innerHTML = years.map(y => `<option value="${esc(y)}" ${y===selected?'selected':''}>${esc(y)}</option>`).join('');
};
// Global fallback handler for inline onclick
window.__genInvoice = async function(btn){
try {
btn.disabled = true;
await generateInvoice(btn.getAttribute('data-parent-id'));
const resp = await loadInvoices();
// Populate year select
const years = Array.isArray(resp.schoolYears) ? resp.schoolYears : [];
const selected = resp.schoolYear || (years[0] || '');
$year.innerHTML = years.map(y => `<option value="${esc(y)}" ${y===selected?'selected':''}>${esc(y)}</option>`).join('');
const resp = await loadInvoices(selectedSchoolYear());
syncSchoolYearSelect(resp);
const data = (resp.invoices || []).map(r => {
const ts = Date.parse(r.invoice_date || new Date().toISOString());
@@ -194,10 +188,7 @@
}
try {
const resp = await loadInvoices();
// Populate year select on first load
const years = Array.isArray(resp.schoolYears) ? resp.schoolYears : [];
const selected = resp.schoolYear || (years[0] || '');
$year.innerHTML = years.map(y => `<option value="${esc(y)}" ${y===selected?'selected':''}>${esc(y)}</option>`).join('');
syncSchoolYearSelect(resp);
const data = (resp.invoices || []).map(r => {
const ts = Date.parse(r.invoice_date || new Date().toISOString());
@@ -240,7 +231,7 @@
try {
await generateInvoice(btn.getAttribute('data-parent-id'));
// Refresh table minimally: reload data
const resp = await loadInvoices($year.value);
const resp = await loadInvoices(selectedSchoolYear());
const data = (resp.invoices || []).map(r => {
const ts = Date.parse(r.invoice_date || new Date().toISOString());
const genBtn = `<button type=\"button\" class=\"btn btn-primary btn-sm gen-invoice\" data-parent-id=\"${esc(r.parent_id)}\" onclick=\"return window.__genInvoice && window.__genInvoice(this)\">Generate Invoice</button>`;
@@ -273,7 +264,7 @@
try {
this.disabled = true;
await generateInvoice(this.getAttribute('data-parent-id'));
const resp = await loadInvoices($year.value);
const resp = await loadInvoices(selectedSchoolYear());
const data = (resp.invoices || []).map(r => {
const ts = Date.parse(r.invoice_date || new Date().toISOString());
const genBtn = `<button type=\"button\" class=\"btn btn-primary btn-sm gen-invoice\" data-parent-id=\"${esc(r.parent_id)}\" onclick=\"return window.__genInvoice && window.__genInvoice(this)\">Generate Invoice</button>`;
@@ -312,34 +303,36 @@
});
// Year change
$year.addEventListener('change', async () => {
try {
const resp = await loadInvoices($year.value);
const data = (resp.invoices || []).map(r => {
const ts = Date.parse(r.invoice_date || new Date().toISOString());
const genBtn = `<button type=\"button\" class=\"btn btn-primary btn-sm gen-invoice\" data-parent-id=\"${esc(r.parent_id)}\" onclick=\"return window.__genInvoice && window.__genInvoice(this)\">Generate Invoice</button>`;
const pdf = r.invoice_id ? `<a href=\"<?= base_url('invoice/pdf') ?>/${esc(r.invoice_id)}\" target=\"_blank\" class=\"btn btn-info btn-sm external-link\">View/Print PDF</a>` : '<span class=\"text-muted\">No invoice yet</span>';
return [
esc(r.parent_name || ''),
renderStudents(r.enrolledKids || []),
genBtn,
fmtMoney(r.invoice_amount),
fmtMoney(r.refund_amount),
`<span data-order=\"${ts}\">${formatDateTime(ts)}</span>`,
pdf,
];
});
if ($.fn.DataTable.isDataTable($tbl)) {
const dti = $tbl.DataTable();
dti.clear();
dti.rows.add(data).draw(false);
if ($year) {
$year.addEventListener('change', async () => {
try {
const resp = await loadInvoices($year.value);
const data = (resp.invoices || []).map(r => {
const ts = Date.parse(r.invoice_date || new Date().toISOString());
const genBtn = `<button type=\"button\" class=\"btn btn-primary btn-sm gen-invoice\" data-parent-id=\"${esc(r.parent_id)}\" onclick=\"return window.__genInvoice && window.__genInvoice(this)\">Generate Invoice</button>`;
const pdf = r.invoice_id ? `<a href=\"<?= base_url('invoice/pdf') ?>/${esc(r.invoice_id)}\" target=\"_blank\" class=\"btn btn-info btn-sm external-link\">View/Print PDF</a>` : '<span class=\"text-muted\">No invoice yet</span>';
return [
esc(r.parent_name || ''),
renderStudents(r.enrolledKids || []),
genBtn,
fmtMoney(r.invoice_amount),
fmtMoney(r.refund_amount),
`<span data-order=\"${ts}\">${formatDateTime(ts)}</span>`,
pdf,
];
});
if ($.fn.DataTable.isDataTable($tbl)) {
const dti = $tbl.DataTable();
dti.clear();
dti.rows.add(data).draw(false);
}
showToast('Loaded ' + $year.value);
} catch (e) {
console.error(e);
showToast('Failed to load ' + $year.value, false);
}
showToast('Loaded ' + $year.value);
} catch (e) {
console.error(e);
showToast('Failed to load ' + $year.value, false);
}
});
});
}
});
</script>
<?= $this->endSection() ?>