@@ -305,6 +305,7 @@ class ParentController extends BaseController
|
||||
// Attach enrollment and class section data to each student
|
||||
foreach ($students as &$student) {
|
||||
$studentId = $student['id'];
|
||||
$student['age'] = $this->calculateAgeAsOfSchoolYearStartYear($student['dob'] ?? null, $selectedYear);
|
||||
|
||||
// Get class section info (can be multiple sections like Grade + Arabic)
|
||||
$classSections = $this->studentClassModel->getClassSectionsByStudentId($studentId, $selectedYear, true);
|
||||
@@ -428,11 +429,14 @@ class ParentController extends BaseController
|
||||
}
|
||||
if ($existingEnrollment) {
|
||||
if ($existingEnrollment['is_withdrawn'] == 1) {
|
||||
$passedPreviousYear = $this->studentPassedPreviousYear((int)$studentId, (string)$this->schoolYear);
|
||||
|
||||
// Reactivate the enrollment if the student was previously withdrawn
|
||||
$this->enrollmentModel->where('id', $existingEnrollment['id'])->update([
|
||||
'is_withdrawn' => 0,
|
||||
'withdrawal_date' => null,
|
||||
'enrollment_status' => 'payment pending',
|
||||
'enrollment_status' => $passedPreviousYear ? 'payment pending' : 'admission under review',
|
||||
'admission_status' => $passedPreviousYear ? 'accepted' : 'pending',
|
||||
'updated_at' => utc_now()
|
||||
]);
|
||||
log_message('info', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']} (ID $studentId) has been re-enrolled in enrollment ID {$existingEnrollment['id']}.");
|
||||
@@ -442,6 +446,8 @@ class ParentController extends BaseController
|
||||
log_message('info', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']} (ID $studentId) is already actively enrolled.");
|
||||
}
|
||||
} else {
|
||||
$passedPreviousYear = $this->studentPassedPreviousYear((int)$studentId, (string)$this->schoolYear);
|
||||
|
||||
// If no enrollment record exists, insert a new enrollment record
|
||||
$result = $this->enrollmentModel->insert([
|
||||
'student_id' => $studentId,
|
||||
@@ -450,8 +456,8 @@ class ParentController extends BaseController
|
||||
'semester' => $this->semester,
|
||||
'enrollment_date' => local_date(utc_now(), 'Y-m-d'),
|
||||
'is_withdrawn' => 0,
|
||||
'enrollment_status' => 'admission under review',
|
||||
'admission_status' => 'pending',
|
||||
'enrollment_status' => $passedPreviousYear ? 'payment pending' : 'admission under review',
|
||||
'admission_status' => $passedPreviousYear ? 'accepted' : 'pending',
|
||||
'created_at' => utc_now()
|
||||
]);
|
||||
|
||||
@@ -625,6 +631,61 @@ 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')) {
|
||||
$passedDecision = $this->db->table('student_decisions')
|
||||
->select('id')
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', $previousSchoolYear)
|
||||
->where('LOWER(TRIM(decision)) = ' . $this->db->escape('pass'), null, false)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
return $passedDecision !== null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'studentPassedPreviousYear failed: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function previousSchoolYearName(string $schoolYear): ?string
|
||||
{
|
||||
$schoolYear = trim($schoolYear);
|
||||
|
||||
if (! preg_match('/^(\d{4})-(\d{4})$/', $schoolYear, $matches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ((int) $matches[1] - 1) . '-' . ((int) $matches[2] - 1);
|
||||
}
|
||||
|
||||
public function enrollFailure()
|
||||
{
|
||||
echo view('/parent/enroll_failure');
|
||||
@@ -1754,6 +1815,77 @@ $existing = $this->studentModel
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateAgeAsOfToday(?string $dob): ?int
|
||||
{
|
||||
$dob = trim((string) $dob);
|
||||
|
||||
if ($dob === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$timezone = new \DateTimeZone((string) (config('School')->attendance['timezone'] ?? user_timezone()));
|
||||
$birthDate = \DateTimeImmutable::createFromFormat('!Y-m-d', $dob, $timezone);
|
||||
$errors = \DateTimeImmutable::getLastErrors();
|
||||
$hasParseErrors = is_array($errors)
|
||||
&& (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0);
|
||||
|
||||
if ($birthDate === false || $hasParseErrors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$today = new \DateTimeImmutable('today', $timezone);
|
||||
|
||||
if ($birthDate > $today) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $birthDate->diff($today)->y;
|
||||
} catch (\Throwable $e) {
|
||||
log_message('warning', 'Unable to calculate current age from DOB: {message}', [
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateAgeAsOfSchoolYearStartYear(?string $dob, string $schoolYear): ?int
|
||||
{
|
||||
$dob = trim((string) $dob);
|
||||
$schoolYear = trim($schoolYear);
|
||||
|
||||
if ($dob === '' || ! preg_match('/^(\d{4})/', $schoolYear, $matches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$timezone = new \DateTimeZone((string) (config('School')->attendance['timezone'] ?? user_timezone()));
|
||||
$birthDate = \DateTimeImmutable::createFromFormat('!Y-m-d', $dob, $timezone);
|
||||
$errors = \DateTimeImmutable::getLastErrors();
|
||||
$hasParseErrors = is_array($errors)
|
||||
&& (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0);
|
||||
|
||||
if ($birthDate === false || $hasParseErrors) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$schoolYearStartYearCutoff = new \DateTimeImmutable($matches[1] . '-12-31', $timezone);
|
||||
|
||||
if ($birthDate > $schoolYearStartYearCutoff) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $birthDate->diff($schoolYearStartYearCutoff)->y;
|
||||
} catch (\Throwable $e) {
|
||||
log_message('warning', 'Unable to calculate school-year age from DOB: {message}', [
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function parentEventPage()
|
||||
{
|
||||
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
|
||||
Reference in New Issue
Block a user