fix enrollment, invoice, payment and financila aid
This commit is contained in:
@@ -317,13 +317,8 @@ class ParentController extends BaseController
|
||||
$student['class_section'] = !empty($classSections)
|
||||
? implode(', ', $classSections)
|
||||
: 'Class not Assigned';
|
||||
$isArabicClass = !empty($classSections) && array_reduce(
|
||||
$classSections,
|
||||
static fn($carry, $name) => $carry || (is_string($name) && stripos($name, 'arabic') === 0),
|
||||
false
|
||||
);
|
||||
|
||||
// ✅ Get enrollment status AND admission status
|
||||
// Get enrollment status AND admission status
|
||||
$enrollment = $this->db->table('enrollments')
|
||||
->select('enrollment_status, admission_status')
|
||||
->where('student_id', $studentId)
|
||||
@@ -350,10 +345,7 @@ class ParentController extends BaseController
|
||||
$student['enrollment_status'] = 'not enrolled';
|
||||
}
|
||||
|
||||
// If assigned to Arabic class without an enrollment record, display as enrolled.
|
||||
if ($student['enrollment_status'] === 'not enrolled' && $isArabicClass) {
|
||||
$student['enrollment_status'] = 'enrolled';
|
||||
}
|
||||
// No enrollment record fabrication from class assignment.
|
||||
|
||||
// ✅ Updated disable logic to include denied status
|
||||
$student['disable_enroll'] = in_array(
|
||||
@@ -411,6 +403,15 @@ class ParentController extends BaseController
|
||||
)));
|
||||
}
|
||||
|
||||
if ($previousSchoolYear !== null) {
|
||||
service('enrollmentTransition')->syncParentFinancialReviewFlags(
|
||||
(int) $parentId,
|
||||
$previousSchoolYear,
|
||||
$selectedYear,
|
||||
array_values(array_map(static fn (array $student): int => (int) ($student['id'] ?? 0), $students))
|
||||
);
|
||||
}
|
||||
|
||||
// Render view
|
||||
return view('/parent/enroll_classes', [
|
||||
'students' => $students,
|
||||
@@ -528,6 +529,7 @@ class ParentController extends BaseController
|
||||
|
||||
$studentName = trim((string) ($studentInfo['firstname'] ?? '') . ' ' . (string) ($studentInfo['lastname'] ?? '')) ?: 'Student ID ' . $studentId;
|
||||
if (empty($evaluation['can_enroll'])) {
|
||||
$transitionService->logEnrollmentBlock($evaluation, 'parent_enroll_submit', (int) $parentId, (int) $parentId);
|
||||
$messages = array_values(array_filter(array_map('trim', array_map('strval', $evaluation['blockers'] ?? []))));
|
||||
$codes = array_values(array_filter(array_map('strval', array_merge($evaluation['blocking_rule_codes'] ?? [], $evaluation['review_rule_codes'] ?? []))));
|
||||
$errors[] = $studentName . ': ' . ($messages !== [] ? implode(' ', $messages) : 'Enrollment is not currently allowed' . ($codes !== [] ? ' (' . implode(', ', $codes) . ')' : '') . '.');
|
||||
@@ -891,7 +893,12 @@ class ParentController extends BaseController
|
||||
private function updateEnrollmentParentContact(int $parentId): array
|
||||
{
|
||||
$fields = $this->request->getPost('parent_contact');
|
||||
$normalized = $this->normalizeEnrollmentParentContact(is_array($fields) ? $fields : []);
|
||||
$currentState = '';
|
||||
$user = $this->userModel->find($parentId);
|
||||
if (is_array($user)) {
|
||||
$currentState = strtoupper(trim((string) ($user['state'] ?? '')));
|
||||
}
|
||||
$normalized = $this->normalizeEnrollmentParentContact(is_array($fields) ? $fields : [], $currentState);
|
||||
if ($normalized['errors'] !== []) {
|
||||
return $normalized['errors'];
|
||||
}
|
||||
@@ -907,33 +914,38 @@ class ParentController extends BaseController
|
||||
* @param array<string, mixed> $fields
|
||||
* @return array{errors: list<string>, data: array<string, string>}
|
||||
*/
|
||||
private function normalizeEnrollmentParentContact(array $fields): array
|
||||
private function normalizeEnrollmentParentContact(array $fields, string $existingState = ''): array
|
||||
{
|
||||
$phoneDigits = preg_replace('/\D/', '', (string) ($fields['cellphone'] ?? '')) ?? '';
|
||||
$street = trim((string) ($fields['address_street'] ?? ''));
|
||||
$apt = trim((string) ($fields['apt'] ?? ''));
|
||||
$city = trim((string) ($fields['city'] ?? ''));
|
||||
$street = $this->collapseContactWhitespace((string) ($fields['address_street'] ?? ''));
|
||||
$apt = $this->collapseContactWhitespace((string) ($fields['apt'] ?? ''));
|
||||
$city = $this->collapseContactWhitespace((string) ($fields['city'] ?? ''));
|
||||
$state = strtoupper(trim((string) ($fields['state'] ?? '')));
|
||||
$zip = trim((string) ($fields['zip'] ?? ''));
|
||||
$zip = preg_replace('/\D/', '', (string) ($fields['zip'] ?? '')) ?? '';
|
||||
$errors = [];
|
||||
$allowedStates = ['CT', 'ME', 'MA', 'NH', 'NY', 'RI', 'VT'];
|
||||
$existingState = strtoupper(trim($existingState));
|
||||
if (preg_match('/^[A-Z]{2}$/', $existingState) === 1) {
|
||||
$allowedStates[] = $existingState;
|
||||
}
|
||||
|
||||
if (strlen($phoneDigits) !== 10) {
|
||||
$errors[] = 'A valid 10-digit home/cell phone number is required.';
|
||||
}
|
||||
|
||||
if (strlen($street) < 5 || strlen($street) > 255) {
|
||||
$errors[] = 'Home street address is required.';
|
||||
if ($street === '' || strlen($street) < 2 || strlen($street) > 50 || ! preg_match('/^[A-Za-z0-9.\s\-]+$/', $street)) {
|
||||
$errors[] = 'Home street address must be 2–50 characters and may contain only letters, numbers, spaces, periods, and hyphens.';
|
||||
}
|
||||
|
||||
if ($apt !== '' && strlen($apt) > 15) {
|
||||
$errors[] = 'Apartment or unit must be 15 characters or fewer.';
|
||||
if ($apt !== '' && (strlen($apt) > 15 || ! preg_match('/^[A-Za-z0-9.\s\-]+$/', $apt))) {
|
||||
$errors[] = 'Apartment or unit must be 15 characters or fewer and may contain only letters, numbers, spaces, periods, and hyphens.';
|
||||
}
|
||||
|
||||
if (strlen($city) < 2 || strlen($city) > 100) {
|
||||
$errors[] = 'City is required.';
|
||||
if (strlen($city) < 2 || strlen($city) > 30 || ! preg_match('/^[A-Za-z\s.\'\-]+$/', $city)) {
|
||||
$errors[] = 'City must be 2–30 characters and may contain only letters, spaces, periods, apostrophes, and hyphens.';
|
||||
}
|
||||
|
||||
if (! preg_match('/^[A-Z]{2}$/', $state)) {
|
||||
if (! preg_match('/^[A-Z]{2}$/', $state) || ! in_array($state, $allowedStates, true)) {
|
||||
$errors[] = 'State is required.';
|
||||
}
|
||||
|
||||
@@ -951,9 +963,9 @@ class ParentController extends BaseController
|
||||
'errors' => [],
|
||||
'data' => [
|
||||
'cellphone' => $formattedPhone ?: $phoneDigits,
|
||||
'address_street' => $street,
|
||||
'apt' => $apt,
|
||||
'city' => ucfirst(strtolower($city)),
|
||||
'address_street' => ucwords(strtolower($street)),
|
||||
'apt' => strtoupper($apt),
|
||||
'city' => ucwords(strtolower($city), " -'"),
|
||||
'state' => $state,
|
||||
'zip' => $zip,
|
||||
'updated_at' => utc_now(),
|
||||
@@ -961,6 +973,11 @@ class ParentController extends BaseController
|
||||
];
|
||||
}
|
||||
|
||||
private function collapseContactWhitespace(string $value): string
|
||||
{
|
||||
return trim(preg_replace('/\s+/', ' ', $value) ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $selected
|
||||
* @return list<string>
|
||||
@@ -1211,51 +1228,6 @@ class ParentController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
private function studentPassedPreviousYear(int $studentId, string $targetSchoolYear): bool
|
||||
{
|
||||
$previousSchoolYear = $this->previousSchoolYearName($targetSchoolYear);
|
||||
|
||||
if ($studentId <= 0 || $previousSchoolYear === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->db->tableExists('promotion_queue')) {
|
||||
$queuedPromotion = $this->db->table('promotion_queue')
|
||||
->select('id')
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year_from', $previousSchoolYear)
|
||||
->where('school_year_to', $targetSchoolYear)
|
||||
->whereIn('status', ['queued', 'assigned', 'applied'])
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
if ($queuedPromotion !== null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('student_decisions')) {
|
||||
$decisionRow = $this->db->table('student_decisions')
|
||||
->select('decision')
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', $previousSchoolYear)
|
||||
->orderBy('updated_at', 'DESC')
|
||||
->orderBy('id', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
return DeliberationDecision::normalize($decisionRow['decision'] ?? null) === DeliberationDecision::PASSED;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'studentPassedPreviousYear failed: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isReturningReEnrollmentStudent(int $studentId, string $targetSchoolYear): bool
|
||||
{
|
||||
$previousSchoolYear = $this->previousSchoolYearName($targetSchoolYear);
|
||||
@@ -1704,6 +1676,18 @@ class ParentController extends BaseController
|
||||
|
||||
private function eligibilityMessageFromTransition(array $student, ?array $evaluation, ?string $fallMakeupExamOn): array
|
||||
{
|
||||
if ($this->hasSettledParentEnrollmentStatus(
|
||||
(string) ($student['enrollment_status'] ?? ''),
|
||||
(string) ($student['admission_status'] ?? '')
|
||||
)) {
|
||||
return [
|
||||
'message' => EnrollmentEligibility::alreadyEnrolledMessage((string) ($student['enrollment_status'] ?? '')),
|
||||
'blocking' => true,
|
||||
'level' => 'info',
|
||||
'primary_block_reason' => 'ALREADY_ENROLLED',
|
||||
];
|
||||
}
|
||||
|
||||
if ($evaluation === null) {
|
||||
return $this->enrollmentEligibilityMessageForStudent(
|
||||
$student,
|
||||
@@ -1721,6 +1705,15 @@ class ParentController extends BaseController
|
||||
];
|
||||
}
|
||||
|
||||
if (($evaluation['can_enroll'] ?? false) === false && ! empty($evaluation['primary_parent_message'])) {
|
||||
return [
|
||||
'message' => (string) $evaluation['primary_parent_message'],
|
||||
'blocking' => true,
|
||||
'level' => 'danger',
|
||||
'primary_block_reason' => $evaluation['primary_block_reason'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
$blockers = array_values(array_filter(array_map('trim', array_map('strval', $evaluation['blockers'] ?? []))));
|
||||
if ($blockers !== []) {
|
||||
$name = $this->studentNameFromRow($student);
|
||||
@@ -1830,14 +1823,7 @@ class ParentController extends BaseController
|
||||
private function parentEnrollmentState(array $student): string
|
||||
{
|
||||
$status = strtolower(trim((string) ($student['enrollment_status'] ?? '')));
|
||||
if (in_array($status, [
|
||||
'admission under review',
|
||||
'review & decision',
|
||||
'payment pending',
|
||||
'enrolled',
|
||||
'waitlist',
|
||||
'withdraw under review',
|
||||
], true)) {
|
||||
if ($this->hasSettledParentEnrollmentStatus($status, (string) ($student['admission_status'] ?? ''))) {
|
||||
return 'Already submitted';
|
||||
}
|
||||
|
||||
@@ -1889,29 +1875,131 @@ class ParentController extends BaseController
|
||||
return 'Contact administration';
|
||||
}
|
||||
|
||||
private function hasSettledParentEnrollmentStatus(string $status, string $admissionStatus = ''): bool
|
||||
{
|
||||
if (strtolower(trim($admissionStatus)) === 'accepted') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array(strtolower(trim($status)), [
|
||||
'admission under review',
|
||||
'review & decision',
|
||||
'payment pending',
|
||||
'enrolled',
|
||||
'waitlist',
|
||||
'withdraw under review',
|
||||
'refund pending',
|
||||
], true);
|
||||
}
|
||||
|
||||
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 = $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) + max(0.0, $currentBalance) + $registrationFee + $tuitionDue + $mandatoryFees;
|
||||
$summary = service('enrollmentTransition')->getEnrollmentFinancialSummary(
|
||||
$parentId,
|
||||
$previousSchoolYear ?? '',
|
||||
$selectedYear,
|
||||
$tuitionDue
|
||||
);
|
||||
$behavior = (string) ($summary['balance_behavior'] ?? 'submission_blocked_until_payment');
|
||||
$schoolYearConfig = $this->schoolYearConfig($selectedYear);
|
||||
$summary['policy_message'] = $this->financialPolicyMessage(
|
||||
$behavior,
|
||||
(string) ($schoolYearConfig['financial_policy_message'] ?? '')
|
||||
);
|
||||
|
||||
return [
|
||||
'currency' => '$',
|
||||
'carry_over_balance' => round($carryOver, 2),
|
||||
'current_balance' => round($currentBalance, 2),
|
||||
'registration_fee' => $registrationFee,
|
||||
'tuition_due_at_registration' => $tuitionDue,
|
||||
'mandatory_fees' => $mandatoryFees,
|
||||
'amount_due' => round($amountDue, 2),
|
||||
'balance_behavior' => $behavior,
|
||||
'payment_plan_available' => (bool) ($schoolYearConfig['payment_plan_available'] ?? false),
|
||||
'policy_message' => $this->financialPolicyMessage($behavior, (string) ($schoolYearConfig['financial_policy_message'] ?? '')),
|
||||
];
|
||||
return $summary;
|
||||
}
|
||||
|
||||
public function enrollmentEligibilityRefresh()
|
||||
{
|
||||
$parentId = (int) session()->get('user_id');
|
||||
if ($parentId <= 0) {
|
||||
return $this->response->setStatusCode(401)->setJSON(['error' => 'Unauthorized']);
|
||||
}
|
||||
|
||||
$selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
$previousSchoolYear = $this->previousSchoolYearName($selectedYear);
|
||||
if ($previousSchoolYear === null) {
|
||||
return $this->response->setJSON(['students' => []]);
|
||||
}
|
||||
|
||||
$students = $this->db->table('students')
|
||||
->select('id, firstname, lastname, dob')
|
||||
->where('parent_id', $parentId)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$transitionService = service('enrollmentTransition');
|
||||
$payload = [];
|
||||
foreach ($students as $student) {
|
||||
$studentId = (int) ($student['id'] ?? 0);
|
||||
if ($studentId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existingEnrollment = $this->db->table('enrollments')
|
||||
->select('enrollment_status, admission_status')
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', $selectedYear)
|
||||
->orderBy('id', 'DESC')
|
||||
->get()
|
||||
->getRowArray();
|
||||
$existingStatus = is_array($existingEnrollment) ? (string) ($existingEnrollment['enrollment_status'] ?? '') : '';
|
||||
$existingAdmissionStatus = is_array($existingEnrollment) ? (string) ($existingEnrollment['admission_status'] ?? '') : '';
|
||||
|
||||
if ($this->hasSettledParentEnrollmentStatus($existingStatus, $existingAdmissionStatus)) {
|
||||
$payload[] = [
|
||||
'student_id' => $studentId,
|
||||
'can_enroll' => false,
|
||||
'primary_block_reason' => 'ALREADY_ENROLLED',
|
||||
'primary_parent_message' => EnrollmentEligibility::alreadyEnrolledMessage($existingStatus),
|
||||
'block_title' => EnrollmentEligibility::alreadyEnrolledTitle($existingStatus),
|
||||
'decision' => 'ALREADY_ENROLLED',
|
||||
'blocking_rule_codes' => ['ALREADY_ENROLLED'],
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$evaluation = $transitionService->evaluateForParent(
|
||||
$parentId,
|
||||
$studentId,
|
||||
$previousSchoolYear,
|
||||
$selectedYear
|
||||
);
|
||||
$payload[] = [
|
||||
'student_id' => $studentId,
|
||||
'can_enroll' => (bool) ($evaluation['can_enroll'] ?? false),
|
||||
'primary_block_reason' => $evaluation['primary_block_reason'] ?? null,
|
||||
'primary_parent_message' => $evaluation['primary_parent_message'] ?? null,
|
||||
'blocking_rule_codes' => array_values(array_map('strval', $evaluation['blocking_rule_codes'] ?? [])),
|
||||
'decision' => $evaluation['decision'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
$financialSummary = $transitionService->getEnrollmentFinancialSummary(
|
||||
$parentId,
|
||||
(string) $previousSchoolYear,
|
||||
$selectedYear
|
||||
);
|
||||
|
||||
if ($previousSchoolYear !== null) {
|
||||
$transitionService->syncParentFinancialReviewFlags(
|
||||
$parentId,
|
||||
$previousSchoolYear,
|
||||
$selectedYear,
|
||||
array_values(array_map(static fn (array $student): int => (int) ($student['id'] ?? 0), $students))
|
||||
);
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'students' => $payload,
|
||||
'financial_summary' => [
|
||||
'carry_forward_balance' => (float) ($financialSummary['carry_forward_balance'] ?? 0),
|
||||
'current_year_balance' => (float) ($financialSummary['current_year_balance'] ?? 0),
|
||||
'total_enrollment_due' => (float) ($financialSummary['total_enrollment_due'] ?? 0),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function enrollmentTuitionDue(array $students): float
|
||||
@@ -1919,16 +2007,11 @@ class ParentController extends BaseController
|
||||
$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'])) {
|
||||
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
|
||||
if (($evaluation['can_enroll'] ?? false) !== true) {
|
||||
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) (
|
||||
@@ -1954,9 +2037,7 @@ class ParentController extends BaseController
|
||||
'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),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1976,20 +2057,79 @@ class ParentController extends BaseController
|
||||
};
|
||||
}
|
||||
|
||||
private function invoiceBalanceForParent(int $parentId, string $schoolYear): float
|
||||
private function auditParentStudentFieldChanges(array $original, array $updated, int $parentId, string $source): void
|
||||
{
|
||||
if ($parentId <= 0 || $schoolYear === '' || ! $this->db->tableExists('invoices')) {
|
||||
return 0.0;
|
||||
if (! $this->db->tableExists('enrollment_transition_audits')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$row = $this->db->table('invoices')
|
||||
->select('COALESCE(SUM(balance), 0) AS balance', false)
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->getRowArray();
|
||||
$studentId = (int) ($original['id'] ?? 0);
|
||||
if ($studentId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return round((float) ($row['balance'] ?? 0), 2);
|
||||
$fields = ['firstname', 'lastname', 'dob'];
|
||||
$changes = [];
|
||||
foreach ($fields as $field) {
|
||||
$oldValue = trim((string) ($original[$field] ?? ''));
|
||||
$newValue = trim((string) ($updated[$field] ?? ''));
|
||||
if ($oldValue !== $newValue) {
|
||||
$changes[$field] = [
|
||||
'old_value' => $oldValue,
|
||||
'new_value' => $newValue,
|
||||
'changed' => true,
|
||||
'changed_by' => $parentId,
|
||||
'changed_at' => date('Y-m-d H:i:s'),
|
||||
'source' => $source,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($changes === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->table('enrollment_transition_audits')->insert([
|
||||
'student_id' => $studentId,
|
||||
'school_year' => (string) ($updated['school_year'] ?? $original['school_year'] ?? date('Y')),
|
||||
'source_school_year' => null,
|
||||
'action' => 'parent_student_field_edit',
|
||||
'performed_by' => $parentId,
|
||||
'original_values_json' => json_encode($original, JSON_UNESCAPED_SLASHES),
|
||||
'new_values_json' => json_encode(['changes' => $changes, 'updated' => $updated], JSON_UNESCAPED_SLASHES),
|
||||
'reason' => $source,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function parentEditAffectsEligibility(array $original, array $updated): bool
|
||||
{
|
||||
return trim((string) ($original['dob'] ?? '')) !== trim((string) ($updated['dob'] ?? ''))
|
||||
|| trim((string) ($original['lastname'] ?? '')) !== trim((string) ($updated['lastname'] ?? ''));
|
||||
}
|
||||
|
||||
private function recheckEligibilityAfterParentEdit(int $studentId, int $parentId, string $targetSchoolYear): void
|
||||
{
|
||||
$previousSchoolYear = $this->previousSchoolYearName($targetSchoolYear);
|
||||
if ($previousSchoolYear === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$evaluation = service('enrollmentTransition')->evaluateForParent(
|
||||
$parentId,
|
||||
$studentId,
|
||||
$previousSchoolYear,
|
||||
$targetSchoolYear,
|
||||
'parent'
|
||||
);
|
||||
|
||||
if (($evaluation['can_enroll'] ?? false) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = (string) ($evaluation['primary_parent_message'] ?? 'Updated student information affects enrollment eligibility.');
|
||||
service('enrollmentTransition')->logEnrollmentBlock($evaluation, 'parent_student_edit', $parentId, $parentId);
|
||||
session()->setFlashdata('warning', $message);
|
||||
}
|
||||
|
||||
private function schoolYearConfig(string $schoolYear): array
|
||||
@@ -2548,7 +2688,7 @@ class ParentController extends BaseController
|
||||
}
|
||||
|
||||
// ✅ 8. Pass $isNew to the student save function
|
||||
$result = $this->validateAndSaveOrUpdateStudent($idx, $parentId, $this->semester, $this->schoolYear, $schoolIdService, $isNew, null, null);
|
||||
$result = $this->validateAndSaveOrUpdateStudent($idx, $parentId, $this->semester, $this->schoolYear, $schoolIdService, $isNew, null);
|
||||
|
||||
if ($result) {
|
||||
$studentAdded = true;
|
||||
@@ -2775,7 +2915,19 @@ $existing = $this->studentModel
|
||||
|
||||
// ---------- UPDATE OR INSERT ----------
|
||||
if ($studentId) {
|
||||
$existing = $this->studentModel->find((int) $studentId);
|
||||
if (! is_array($existing) || (int) ($existing['parent_id'] ?? 0) !== (int) $parentId) {
|
||||
session()->setFlashdata('error', 'Student record was not found for this parent account.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->auditParentStudentFieldChanges($existing, $studentData, (int) $parentId, 'parent_student_edit');
|
||||
$this->studentModel->update($studentId, $studentData);
|
||||
|
||||
if ($this->parentEditAffectsEligibility($existing, $studentData)) {
|
||||
$this->recheckEligibilityAfterParentEdit((int) $studentId, (int) $parentId, (string) $schoolYear);
|
||||
}
|
||||
} else {
|
||||
$studentData['registration_date'] = utc_now();
|
||||
$studentData['tuition_paid'] = 0;
|
||||
@@ -3148,7 +3300,7 @@ $existing = $this->studentModel
|
||||
$this->request->setGlobal('post', $formData);
|
||||
|
||||
// Save/update
|
||||
$this->validateAndSaveOrUpdateStudent(0, $parentId, $this->semester, $this->schoolYear, $schoolIdService, $id, null, null);
|
||||
$this->validateAndSaveOrUpdateStudent(0, $parentId, $this->semester, $this->schoolYear, $schoolIdService, null, (int) $id);
|
||||
|
||||
return redirect()->to('/parent/child_register')->with('success', 'Student updated!');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user