fix enrollment for the new school-year
Tests / PHPUnit (push) Successful in 1m26s

This commit is contained in:
root
2026-08-07 23:43:31 -04:00
parent b6f3b14e7b
commit 1ef2800f12
66 changed files with 8997 additions and 498 deletions
@@ -38,6 +38,7 @@
<th>Registration Date</th>
<th>Parent/Guardian</th>
<th>Student Name</th>
<th>Age</th>
<th>New Student</th>
<th>Removed (Prior Years)</th>
<th>Current Class</th>
@@ -78,6 +79,11 @@
<?php endif; ?>
</td>
<!-- Age -->
<td class="text-center">
<?= isset($student['age']) && $student['age'] !== '' && $student['age'] !== null ? esc((string)(int)$student['age']) : '-' ?>
</td>
<!-- New Student -->
<td>
<?php if (($student['new_student'] ?? 'Unknown') === 'Yes'): ?>
@@ -109,6 +115,9 @@
case 'admission under review':
echo '<span class="badge bg-primary">admission under review</span>';
break;
case 'review & decision':
echo '<span class="badge bg-secondary">review & decision</span>';
break;
case 'payment pending':
echo '<span class="badge bg-warning text-dark">payment pending</span>';
break;
@@ -145,6 +154,7 @@
data-parent-id="<?= $pid ?>"
data-prev="<?= esc($student['enrollment_status'] ?? '') ?>">
<option value="admission under review" <?= ($student['enrollment_status'] ?? '') === 'admission under review' ? 'selected' : '' ?>>admission under review</option>
<option value="review & decision" <?= ($student['enrollment_status'] ?? '') === 'review & decision' ? 'selected' : '' ?>>review &amp; decision</option>
<option value="payment pending" <?= ($student['enrollment_status'] ?? '') === 'payment pending' ? 'selected' : '' ?>>payment pending</option>
<option value="enrolled" <?= ($student['enrollment_status'] ?? '') === 'enrolled' ? 'selected' : '' ?>>enrolled</option>
<option value="withdraw under review" <?= ($student['enrollment_status'] ?? '') === 'withdraw under review' ? 'selected' : '' ?>>withdraw under review</option>
@@ -155,9 +165,9 @@
</select>
</td>
<!-- Assign Class (only for 'admission under review') -->
<!-- Assign Class (only for review statuses) -->
<td>
<?php $isUnderReview = (strtolower($student['enrollment_status'] ?? '') === 'admission under review'); ?>
<?php $isUnderReview = in_array(strtolower($student['enrollment_status'] ?? ''), ['admission under review', 'review & decision'], true); ?>
<?php if ($isUnderReview): ?>
<select class="form-select form-select-sm assign-class-select" <?= empty($isCurrentYear) ? 'disabled' : '' ?>
data-student-id="<?= $sid ?>"
@@ -181,7 +191,7 @@
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="9">No students available.</td>
<td colspan="10">No students available.</td>
</tr>
<?php endif; ?>
</tbody>
@@ -379,8 +389,8 @@
// Disable sort/search on interactive columns (status select, assign select)
columnDefs: [
{ targets: [7, 8], orderable: false, searchable: false },
{ targets: [0, 1, 2, 3, 4, 5, 6], render: function(data, type) {
{ targets: [8, 9], orderable: false, searchable: false },
{ targets: [0, 1, 2, 3, 4, 5, 6, 7], render: function(data, type) {
if (type === 'filter' || type === 'sort' || type === 'type') {
return stripHtml(data);
}
@@ -460,6 +470,7 @@
const s = norm(status);
switch (s) {
case 'admission under review': return '<span class="badge bg-primary">admission under review</span>';
case 'review & decision': return '<span class="badge bg-secondary">review &amp; decision</span>';
case 'payment pending': return '<span class="badge bg-warning text-dark">payment pending</span>';
case 'enrolled': return '<span class="badge bg-success">enrolled</span>';
case 'withdraw under review': return '<span class="badge bg-warning text-dark">withdraw under review</span>';
@@ -559,7 +570,7 @@
btn.disabled = true;
if (prog) prog.classList.remove('d-none');
// Collect tasks: rows where status is 'admission under review' and a class is selected
// Collect tasks: rows where status is a review state and a class is selected
const assigns = Array.from(document.querySelectorAll('.assign-class-select'));
const tasks = [];
const parentSet = new Set();
@@ -570,7 +581,7 @@
const desired = norm(statusSelect?.getAttribute('data-desired') || statusSelect?.value || '');
const classId = sel.value;
// Only include rows explicitly set to move to payment pending with a class selected
if (prev === 'admission under review' && desired === 'payment pending' && classId) {
if (['admission under review', 'review & decision'].includes(prev) && desired === 'payment pending' && classId) {
const studentId = sel.getAttribute('data-student-id');
const parentId = sel.getAttribute('data-parent-id');
const className = sel.options[sel.selectedIndex]?.text || '';
@@ -579,7 +590,7 @@
}
});
// Collect standalone status updates (not AUR->payment pending)
// Collect standalone status updates (not review-state -> payment pending)
const statusTasks = [];
const shouldInvoice = new Set(['payment pending','enrolled','withdrawn','refund pending','withdraw under review']);
document.querySelectorAll('select.enrollment-status').forEach(se => {
@@ -587,7 +598,7 @@
const prev = norm(se.getAttribute('data-prev') || '');
const desired = norm(se.getAttribute('data-desired') || '');
if (!desired || desired === prev) return;
if (prev === 'admission under review' && desired === 'payment pending') return; // handled by tasks
if (['admission under review', 'review & decision'].includes(prev) && desired === 'payment pending') return; // handled by tasks
const parentId = se.getAttribute('data-parent-id') || tr?.getAttribute('data-parent-id') || '';
statusTasks.push({ tr, studentId: se.getAttribute('data-student-id'), desired, parentId });
if (parentId && shouldInvoice.has(desired)) parentSet.add(parentId);
@@ -672,8 +683,8 @@
const rowData = r.data();
rowData[STATUS_COL_INDEX] = badgeForStatusJs(s.desired);
rowData[STATUS_SELECT_COL_INDEX] = buildStatusSelectHtml(s.studentId, s.tr.getAttribute('data-parent-id') || '', s.desired);
if (norm(s.desired) === 'admission under review') {
// leave current assign select as-is
if (['admission under review', 'review & decision'].includes(norm(s.desired))) {
rowData[ASSIGN_COL_INDEX] = buildAssignSelectHtml(s.studentId, s.tr.getAttribute('data-parent-id') || '', <?= json_encode($classes ?? [], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?>);
} else {
rowData[ASSIGN_COL_INDEX] = '<span class="text-muted">—</span>';
}
@@ -688,10 +699,12 @@
statusSelect.removeAttribute('data-desired');
statusSelect.classList.remove('pending-status-select');
}
if (norm(s.desired) !== 'admission under review') {
const cells = s.tr.querySelectorAll('td');
const assignCell = cells[ASSIGN_COL_INDEX];
if (assignCell) assignCell.innerHTML = '<span class="text-muted">—</span>';
const cells = s.tr.querySelectorAll('td');
const assignCell = cells[ASSIGN_COL_INDEX];
if (assignCell) {
assignCell.innerHTML = ['admission under review', 'review & decision'].includes(norm(s.desired))
? buildAssignSelectHtml(s.studentId, s.tr.getAttribute('data-parent-id') || '', <?= json_encode($classes ?? [], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?>)
: '<span class="text-muted">—</span>';
}
}
showRowToast(s.tr, 'Status updated.');
@@ -749,7 +762,7 @@
function buildStatusSelectHtml(studentId, parentId, current) {
const opts = [
'admission under review','payment pending','enrolled','withdraw under review','refund pending','withdrawn','waitlist','denied'
'admission under review','review & decision','payment pending','enrolled','withdraw under review','refund pending','withdrawn','waitlist','denied'
];
let html = `<select name="enrollment_status[${studentId}]" class="form-control enrollment-status" ${IS_CURRENT_YEAR ? '' : 'disabled'} data-student-id="${studentId}" data-parent-id="${parentId}" data-prev="${esc(current)}">`;
for (const o of opts) {
@@ -793,7 +806,7 @@
rowData[CLASS_COL_INDEX] = esc(st.class_section || 'Class not Assigned');
rowData[STATUS_COL_INDEX] = badgeForStatusJs(st.enrollment_status || '');
rowData[STATUS_SELECT_COL_INDEX] = buildStatusSelectHtml(id, tr.getAttribute('data-parent-id') || '', st.enrollment_status || '');
if (norm(st.enrollment_status || '') === 'admission under review') {
if (['admission under review', 'review & decision'].includes(norm(st.enrollment_status || ''))) {
rowData[ASSIGN_COL_INDEX] = buildAssignSelectHtml(id, tr.getAttribute('data-parent-id') || '', data.classes || []);
} else {
rowData[ASSIGN_COL_INDEX] = '<span class="text-muted">—</span>';