This commit is contained in:
@@ -391,7 +391,8 @@ class ParentController extends BaseController
|
||||
'lastDayOfRegistration' => $this->lastDayOfRegistration,
|
||||
'schoolStartDate' => $this->schoolStartDate,
|
||||
'hasAcceptedSchoolPolicy' => $this->hasAcceptedPolicyForYear((int) $parentId, $selectedYear),
|
||||
'familyFinancialSummary' => $this->familyFinancialSummary((int) $parentId, $previousSchoolYear, $selectedYear),
|
||||
'familyFinancialSummary' => $this->familyFinancialSummary((int) $parentId, $previousSchoolYear, $selectedYear, $students),
|
||||
'enrollmentFeeSchedule' => $this->enrollmentFeeSchedule($selectedYear),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
log_message('error', 'An error occurred in enrollClasses: ' . $e->getMessage());
|
||||
@@ -462,6 +463,12 @@ class ParentController extends BaseController
|
||||
$submittedStudentIds = array_values(array_unique(array_filter(array_map('intval', (array) $enroll), static fn (int $id): bool => $id > 0)));
|
||||
$evaluations = [];
|
||||
$errors = [];
|
||||
$submittedEnrollmentContexts = [];
|
||||
|
||||
$studentInfoErrors = $this->updateEnrollmentStudentInfo($submittedStudentIds, (int) $parentId, $selectedYear);
|
||||
if ($studentInfoErrors !== []) {
|
||||
return redirect()->back()->withInput()->with('error', implode(' ', $studentInfoErrors));
|
||||
}
|
||||
|
||||
foreach ($submittedStudentIds as $studentId) {
|
||||
try {
|
||||
@@ -524,8 +531,10 @@ class ParentController extends BaseController
|
||||
->getRowArray();
|
||||
|
||||
$studentName = trim((string) ($studentData[$studentId]['firstname'] ?? '') . ' ' . (string) ($studentData[$studentId]['lastname'] ?? '')) ?: 'Student ID ' . $studentId;
|
||||
$isReturningReEnrollment = $this->isReturningReEnrollmentStudent((int)$studentId, $selectedYear);
|
||||
$submittedEnrollmentContexts[$studentId] = $isReturningReEnrollment ? 're_enrollment' : 'first_enrollment';
|
||||
|
||||
if ($existingEnrollment) {
|
||||
$isReturningReEnrollment = $this->isReturningReEnrollmentStudent((int)$studentId, $selectedYear);
|
||||
$targetEnrollmentStatus = $isReturningReEnrollment ? 'payment pending' : 'admission under review';
|
||||
$targetAdmissionStatus = $isReturningReEnrollment ? 'accepted' : 'pending';
|
||||
|
||||
@@ -568,7 +577,6 @@ class ParentController extends BaseController
|
||||
implode(' ', array_map('strval', $evaluation['rule_codes'] ?? []))
|
||||
);
|
||||
} else {
|
||||
$isReturningReEnrollment = $this->isReturningReEnrollmentStudent((int)$studentId, $selectedYear);
|
||||
$targetEnrollmentStatus = $isReturningReEnrollment ? 'payment pending' : 'admission under review';
|
||||
$targetAdmissionStatus = $isReturningReEnrollment ? 'accepted' : 'pending';
|
||||
|
||||
@@ -616,6 +624,7 @@ class ParentController extends BaseController
|
||||
if (! $this->db->transStatus()) {
|
||||
return redirect()->back()->withInput()->with('error', 'A database error occurred while submitting enrollment.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// $studentData now holds info for all students processed
|
||||
@@ -710,6 +719,8 @@ class ParentController extends BaseController
|
||||
return redirect()->to('/parent/enroll_classes');
|
||||
} else {
|
||||
// Redirect to enrollment success page if there are enrollments
|
||||
$contextValues = array_values(array_unique($submittedEnrollmentContexts ?? []));
|
||||
$parentData['enrollment_context'] = count($contextValues) === 1 ? $contextValues[0] : 'mixed';
|
||||
Events::trigger('admissionUnderReview', $parentData, $studentData); //send notification for enrolled student
|
||||
return redirect()->to('/parent/enroll_classes');
|
||||
}
|
||||
@@ -739,6 +750,115 @@ class ParentController extends BaseController
|
||||
return $this->filterEnrollmentPayloadByColumns($payload);
|
||||
}
|
||||
|
||||
private function updateEnrollmentStudentInfo(array $studentIds, int $parentId, string $schoolYear): array
|
||||
{
|
||||
$studentInfo = $this->request->getPost('student_info');
|
||||
if (! is_array($studentInfo)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$submittedIds = array_fill_keys(array_map('intval', $studentIds), true);
|
||||
$errors = [];
|
||||
$updates = [];
|
||||
|
||||
foreach ($studentInfo as $studentId => $fields) {
|
||||
$studentId = (int) $studentId;
|
||||
if ($studentId <= 0 || ! isset($submittedIds[$studentId]) || ! is_array($fields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = $this->studentModel
|
||||
->where('id', $studentId)
|
||||
->where('parent_id', $parentId)
|
||||
->first();
|
||||
|
||||
if (! is_array($existing)) {
|
||||
$errors[] = 'Student ID ' . $studentId . ': student record was not found.';
|
||||
continue;
|
||||
}
|
||||
|
||||
$firstName = $this->normalizeEnrollmentStudentName((string) ($fields['firstname'] ?? ''));
|
||||
$lastName = $this->normalizeEnrollmentStudentName((string) ($fields['lastname'] ?? ''));
|
||||
$dob = trim((string) ($fields['dob'] ?? ''));
|
||||
$gender = trim((string) ($fields['gender'] ?? ''));
|
||||
$registrationGrade = trim((string) ($fields['registration_grade'] ?? ''));
|
||||
$photoConsent = (string) ($fields['photo_consent'] ?? '');
|
||||
$studentLabel = trim($firstName . ' ' . $lastName) ?: 'Student ID ' . $studentId;
|
||||
|
||||
try {
|
||||
$this->validateNames($firstName);
|
||||
$this->validateNames($lastName);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$errors[] = $studentLabel . ': ' . $e->getMessage();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! in_array($gender, ['Male', 'Female'], true)) {
|
||||
$errors[] = $studentLabel . ': gender is required.';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($photoConsent !== '0' && $photoConsent !== '1') {
|
||||
$errors[] = $studentLabel . ': photo consent is required.';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($registrationGrade === '' || mb_strlen($registrationGrade) > 50) {
|
||||
$errors[] = $studentLabel . ': registration grade is required.';
|
||||
continue;
|
||||
}
|
||||
|
||||
$dobObj = \DateTimeImmutable::createFromFormat('!Y-m-d', $dob, new \DateTimeZone('UTC'));
|
||||
$dobErrors = \DateTimeImmutable::getLastErrors();
|
||||
$dobWarningCount = is_array($dobErrors) ? (int) ($dobErrors['warning_count'] ?? 0) : 0;
|
||||
$dobErrorCount = is_array($dobErrors) ? (int) ($dobErrors['error_count'] ?? 0) : 0;
|
||||
if ($dobObj === false || $dobWarningCount > 0 || $dobErrorCount > 0) {
|
||||
$errors[] = $studentLabel . ': date of birth must use YYYY-MM-DD format.';
|
||||
continue;
|
||||
}
|
||||
|
||||
$validation = $this->validateDobAge(
|
||||
$dob,
|
||||
$this->registrationMinimumAgeDeadline($schoolYear),
|
||||
5,
|
||||
18,
|
||||
$this->schoolYearAgeDeadline($schoolYear)
|
||||
);
|
||||
if (! $validation['isValid']) {
|
||||
$errors[] = $studentLabel . ': ' . $validation['message'] . '.';
|
||||
continue;
|
||||
}
|
||||
|
||||
$updates[$studentId] = [
|
||||
'firstname' => $firstName,
|
||||
'lastname' => $lastName,
|
||||
'dob' => $dobObj->format('Y-m-d'),
|
||||
'age' => $this->calculateAgeAsOfSchoolYearStartYear($dobObj->format('Y-m-d'), $schoolYear),
|
||||
'gender' => $gender,
|
||||
'registration_grade' => $registrationGrade,
|
||||
'photo_consent' => (int) $photoConsent,
|
||||
];
|
||||
}
|
||||
|
||||
if ($errors !== []) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
foreach ($updates as $studentId => $payload) {
|
||||
if (! $this->studentModel->update($studentId, $payload)) {
|
||||
$errors[] = 'Student ID ' . $studentId . ': student information could not be updated.';
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function normalizeEnrollmentStudentName(string $name): string
|
||||
{
|
||||
$name = trim(preg_replace('/\s+/', ' ', $name) ?? '');
|
||||
return mb_convert_case($name, MB_CASE_TITLE, 'UTF-8');
|
||||
}
|
||||
|
||||
private function exceptionReasonFromEvaluation(array $evaluation): ?string
|
||||
{
|
||||
if (! empty($evaluation['admin_exception'])) {
|
||||
@@ -1625,16 +1745,16 @@ class ParentController extends BaseController
|
||||
return 'Contact administration';
|
||||
}
|
||||
|
||||
private function familyFinancialSummary(int $parentId, ?string $previousSchoolYear, string $selectedYear): array
|
||||
private function familyFinancialSummary(int $parentId, ?string $previousSchoolYear, string $selectedYear, array $students = []): array
|
||||
{
|
||||
$schoolYearConfig = $this->schoolYearConfig($selectedYear);
|
||||
$carryOver = $previousSchoolYear !== null ? $this->invoiceBalanceForParent($parentId, $previousSchoolYear) : 0.0;
|
||||
$currentBalance = $this->invoiceBalanceForParent($parentId, $selectedYear);
|
||||
$registrationFee = round((float) ($schoolYearConfig['registration_fee'] ?? 0), 2);
|
||||
$tuitionDue = round((float) ($schoolYearConfig['tuition_due_at_registration'] ?? 0), 2);
|
||||
$tuitionDue = $this->enrollmentTuitionDue($students);
|
||||
$mandatoryFees = round((float) ($schoolYearConfig['mandatory_fees'] ?? 0), 2);
|
||||
$behavior = (string) ($schoolYearConfig['carry_over_balance_behavior'] ?? 'submission_blocked_until_payment');
|
||||
$amountDue = max(0.0, $carryOver) + $registrationFee + $tuitionDue + $mandatoryFees;
|
||||
$amountDue = max(0.0, $carryOver) + max(0.0, $currentBalance) + $registrationFee + $tuitionDue + $mandatoryFees;
|
||||
|
||||
return [
|
||||
'currency' => '$',
|
||||
@@ -1650,6 +1770,52 @@ class ParentController extends BaseController
|
||||
];
|
||||
}
|
||||
|
||||
private function enrollmentTuitionDue(array $students): float
|
||||
{
|
||||
$tuitionStudents = [];
|
||||
|
||||
foreach ($students as $student) {
|
||||
$status = strtolower(trim((string) ($student['enrollment_status'] ?? '')));
|
||||
$eligibilityMessage = is_array($student['enrollment_eligibility_message'] ?? null)
|
||||
? $student['enrollment_eligibility_message']
|
||||
: ['blocking' => false];
|
||||
|
||||
if ($status !== 'not enrolled' || ! empty($eligibilityMessage['blocking'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
|
||||
$tuitionStudents[] = [
|
||||
'student_id' => (int) ($student['id'] ?? $student['student_id'] ?? 0),
|
||||
'class_section_id' => (int) (
|
||||
$evaluation['assigned_class_section_id']
|
||||
?? $student['class_section_id']
|
||||
?? 0
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if ($tuitionStudents === []) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return (new FeeCalculationService())->calculateEnrollmentTuition($tuitionStudents);
|
||||
}
|
||||
|
||||
private function enrollmentFeeSchedule(string $selectedYear): array
|
||||
{
|
||||
$schoolYearConfig = $this->schoolYearConfig($selectedYear);
|
||||
|
||||
return [
|
||||
'currency' => '$',
|
||||
'first_student_fee' => round((float) ($this->configModel->getConfig('first_student_fee') ?? 380), 2),
|
||||
'second_student_fee' => round((float) ($this->configModel->getConfig('second_student_fee') ?? 280), 2),
|
||||
'registration_fee' => round((float) ($schoolYearConfig['registration_fee'] ?? 0), 2),
|
||||
'tuition_due_at_registration' => round((float) ($schoolYearConfig['tuition_due_at_registration'] ?? 0), 2),
|
||||
'mandatory_fees' => round((float) ($schoolYearConfig['mandatory_fees'] ?? 0), 2),
|
||||
];
|
||||
}
|
||||
|
||||
private function financialPolicyMessage(string $behavior, string $configured): string
|
||||
{
|
||||
$configured = trim($configured);
|
||||
|
||||
Reference in New Issue
Block a user