add features to family card, scores, invoices and students details
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 49s
Tests / PHPUnit (push) Successful in 1m51s

This commit is contained in:
root
2026-09-09 23:02:16 -04:00
parent 36e8ffe56d
commit ac19226bf0
11 changed files with 822 additions and 38 deletions
+132 -12
View File
@@ -239,6 +239,7 @@ class FamilyAdminController extends BaseController
public function card(): ResponseInterface
{
$db = \Config\Database::connect();
$canViewInvoices = $this->canViewFamilyInvoices();
$studentId = (int) ($this->request->getGet('student_id') ?? 0);
$guardianId = (int) ($this->request->getGet('guardian_id') ?? 0);
$familyId = (int) ($this->request->getGet('family_id') ?? 0);
@@ -338,9 +339,8 @@ class FamilyAdminController extends BaseController
// Hydrate with guardians, students (+grades), invoices, payments
$invoiceModel = new \App\Models\InvoiceModel();
$paymentModel = new \App\Models\PaymentModel();
$studentClassModel = new \App\Models\StudentClassModel();
$configModel = new \App\Models\ConfigurationModel();
$schoolYear = (string) ($configModel->getConfig('school_year') ?? '');
$schoolYear = $this->currentSchoolYearName((string) ($configModel->getConfig('school_year') ?? ''));
// Guardians
$guardians = $db->query(
@@ -354,10 +354,11 @@ class FamilyAdminController extends BaseController
[$familyId]
)->getResultArray();
$family['guardians'] = $guardians;
$family['can_view_invoices'] = $canViewInvoices;
// Students
$studentsRows = $db->query(
"SELECT s.id, s.firstname, s.lastname
"SELECT s.*
FROM family_students fs
JOIN students s ON s.id = fs.student_id
WHERE fs.family_id = ?
@@ -369,7 +370,7 @@ class FamilyAdminController extends BaseController
$studentIds = array_map(static fn(array $row): int => (int) ($row['id'] ?? 0), $studentsRows);
if (!in_array($studentId, $studentIds, true)) {
$selectedStudent = $db->query(
"SELECT id, firstname, lastname
"SELECT *
FROM students
WHERE id = ?
LIMIT 1",
@@ -383,13 +384,101 @@ class FamilyAdminController extends BaseController
}
if (!empty($studentsRows)) {
$studentIds = array_values(array_filter(array_map(
static fn(array $row): int => (int) ($row['id'] ?? 0),
$studentsRows
)));
$allergiesByStudent = [];
$conditionsByStudent = [];
$classAssignmentsByStudent = [];
$enrollmentByStudent = [];
$scoreHistoryByStudent = [];
if (!empty($studentIds)) {
if ($schoolYear !== '') {
$classSectionJoin = 'cs.class_section_id = sc.class_section_id';
if ($db->fieldExists('school_year', 'classSection')) {
$classSectionJoin .= ' AND (cs.school_year = sc.school_year OR cs.school_year IS NULL)';
}
$classAssignmentRows = $db->table('student_class sc')
->select('sc.student_id, cs.class_section_name, c.class_name')
->join('classSection cs', $classSectionJoin, 'left')
->join('classes c', 'c.id = cs.class_id', 'left')
->whereIn('sc.student_id', $studentIds)
->where('sc.school_year', $schoolYear)
->where('sc.class_section_id IS NOT NULL', null, false)
->orderBy('cs.class_section_name', 'ASC')
->get()
->getResultArray();
foreach ($classAssignmentRows as $classAssignmentRow) {
$assignmentStudentId = (int) ($classAssignmentRow['student_id'] ?? 0);
$className = trim((string) ($classAssignmentRow['class_name'] ?? ''));
$sectionName = trim((string) ($classAssignmentRow['class_section_name'] ?? ''));
$label = $className !== '' && $sectionName !== '' && strcasecmp($className, $sectionName) !== 0
? $className . ' / ' . $sectionName
: ($sectionName !== '' ? $sectionName : $className);
if ($assignmentStudentId > 0 && $label !== '') {
$classAssignmentsByStudent[$assignmentStudentId][$label] = true;
}
}
$enrollmentRows = $db->table('enrollments e')
->select('e.student_id, e.enrollment_status')
->whereIn('e.student_id', $studentIds)
->where('e.school_year', $schoolYear)
->orderBy('e.updated_at', 'DESC')
->orderBy('e.enrollment_date', 'DESC')
->orderBy('e.id', 'DESC')
->get()
->getResultArray();
foreach ($enrollmentRows as $enrollmentRow) {
$enrollmentStudentId = (int) ($enrollmentRow['student_id'] ?? 0);
if ($enrollmentStudentId > 0 && !isset($enrollmentByStudent[$enrollmentStudentId])) {
$enrollmentByStudent[$enrollmentStudentId] = $enrollmentRow;
}
}
}
$allergyRows = $db->table('student_allergies')
->select('student_id, allergy')
->whereIn('student_id', $studentIds)
->orderBy('allergy', 'ASC')
->get()
->getResultArray();
foreach ($allergyRows as $allergyRow) {
$allergiesByStudent[(int) ($allergyRow['student_id'] ?? 0)][] = (string) ($allergyRow['allergy'] ?? '');
}
$conditionRows = $db->table('student_medical_conditions')
->select('student_id, condition_name')
->whereIn('student_id', $studentIds)
->orderBy('condition_name', 'ASC')
->get()
->getResultArray();
foreach ($conditionRows as $conditionRow) {
$conditionsByStudent[(int) ($conditionRow['student_id'] ?? 0)][] = (string) ($conditionRow['condition_name'] ?? '');
}
$scoreHistoryByStudent = (new \App\Services\StudentScoreHistoryService($db))
->forStudents($studentIds);
}
foreach ($studentsRows as &$sr) {
$sid = (int) ($sr['id'] ?? 0);
$sr['grade'] = $sid ? (string) ($studentClassModel->getClassSectionsByStudentId($sid, $schoolYear) ?? '') : '';
$enrollment = $enrollmentByStudent[$sid] ?? [];
$sr['grade'] = implode(', ', array_keys($classAssignmentsByStudent[$sid] ?? []));
$sr['enrollment_status'] = (string) ($enrollment['enrollment_status'] ?? '');
$sr['allergies'] = $allergiesByStudent[$sid] ?? [];
$sr['medical_conditions'] = $conditionsByStudent[$sid] ?? [];
$sr['score_history'] = $scoreHistoryByStudent[$sid] ?? [];
}
unset($sr);
}
$family['students'] = $studentsRows;
$family['selected_student_id'] = $studentId;
// Financials
$parentIds = array_map(static fn($g) => (int)($g['user_id'] ?? 0), $guardians);
@@ -427,13 +516,21 @@ class FamilyAdminController extends BaseController
}
}
if (!empty($parentIds)) {
if ($canViewInvoices && !empty($parentIds)) {
// Invoices
$invRows = $db->table('invoices')
$invoiceBuilder = $db->table('invoices')
->select('id, parent_id, invoice_number, status, total_amount, paid_amount, balance, issue_date, due_date')
->whereIn('parent_id', $parentIds)
->orderBy('issue_date', 'DESC')
->get()->getResultArray();
->orderBy('issue_date', 'DESC');
if ($schoolYear !== '') {
$invoiceBuilder->where('school_year', $schoolYear);
}
$invRows = $invoiceBuilder->get()->getResultArray();
foreach ($invRows as &$invoiceRow) {
$invoiceParentId = (int) ($invoiceRow['parent_id'] ?? 0);
$invoiceRow['parent_name'] = $gmap[$invoiceParentId] ?? ('Parent #' . $invoiceParentId);
}
unset($invoiceRow);
$family['invoices'] = $invRows;
$invoiceMap = [];
foreach ($invRows as $ir) {
@@ -449,20 +546,43 @@ class FamilyAdminController extends BaseController
}
// Payments
$payRows = $db->table('payments p')
$paymentBuilder = $db->table('payments p')
->select('p.id, p.parent_id, p.invoice_id, p.paid_amount, p.payment_method, p.payment_date, p.status AS payment_status, p.installment_seq, p.number_of_installments, i.invoice_number, i.balance AS invoice_current_balance, i.status AS invoice_status, i.school_year')
->join('invoices i', 'i.id = p.invoice_id', 'inner')
->whereIn('p.parent_id', $parentIds)
->orderBy('p.payment_date', 'DESC')
->orderBy('p.id', 'DESC')
->limit(10)
->get()->getResultArray();
->limit(10);
if ($schoolYear !== '') {
$paymentBuilder->where('i.school_year', $schoolYear);
}
$payRows = $paymentBuilder->get()->getResultArray();
foreach ($payRows as &$paymentRow) {
$paymentParentId = (int) ($paymentRow['parent_id'] ?? 0);
$paymentRow['parent_name'] = $gmap[$paymentParentId] ?? ('Parent #' . $paymentParentId);
}
unset($paymentRow);
$family['payments'] = $payRows;
}
return service('response')->setBody(view('family/card', ['f' => $family]));
}
private function canViewFamilyInvoices(): bool
{
$roles = array_map(
static fn($role): string => strtolower(trim((string) $role)),
array_filter(array_merge((array) session()->get('roles'), [session()->get('role')]))
);
return (bool) array_intersect(array_unique($roles), [
'admin',
'administrator',
'administrative staff',
'principal',
]);
}
public function composeEmail()
{
$to = trim((string)$this->request->getGet('to'));