fix enrollment and email send to parent
This commit is contained in:
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -514,6 +514,8 @@ class DiscountController extends BaseController
|
||||
$affected = count($rowsToUpdate);
|
||||
$db->transCommit();
|
||||
|
||||
$this->triggerStudentEnrolledNotification($parentId, $rowsToUpdate);
|
||||
|
||||
log_message(
|
||||
'info',
|
||||
'Enrollment status -> enrolled for {n} row(s). parent={p}, year={y}, sem={s}, ids=[{ids}]',
|
||||
@@ -534,10 +536,60 @@ class DiscountController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
private function triggerStudentEnrolledNotification(int $parentId, array $enrollmentRows): void
|
||||
{
|
||||
if ($parentId <= 0 || $enrollmentRows === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$parent = $this->userModel->find($parentId) ?? [];
|
||||
$studentIds = array_values(array_unique(array_filter(
|
||||
array_map(static fn (array $row): int => (int) ($row['student_id'] ?? 0), $enrollmentRows),
|
||||
static fn (int $studentId): bool => $studentId > 0
|
||||
)));
|
||||
|
||||
if ($studentIds === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$students = [];
|
||||
foreach ($this->db->table('students')->whereIn('id', $studentIds)->get()->getResultArray() as $student) {
|
||||
$studentId = (int) ($student['id'] ?? 0);
|
||||
if ($studentId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$students[] = [
|
||||
'student_id' => $studentId,
|
||||
'name' => trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')) ?: 'Student #' . $studentId,
|
||||
];
|
||||
}
|
||||
|
||||
if ($students === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
Events::trigger('studentEnrolled', [
|
||||
'user_id' => $parent['id'] ?? $parentId,
|
||||
'email' => $parent['email'] ?? null,
|
||||
'firstname' => $parent['firstname'] ?? '',
|
||||
'lastname' => $parent['lastname'] ?? '',
|
||||
'school_year' => (string) $this->schoolYear,
|
||||
'portalLink' => base_url('/login'),
|
||||
], $students);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Enrollment confirmation notification after discount failed for parent {parent_id}: {error}', [
|
||||
'parent_id' => $parentId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function listVouchers()
|
||||
{
|
||||
$this->schoolYear = $this->activeSchoolYearName();
|
||||
$this->schoolYear = $this->currentSchoolYearName();
|
||||
|
||||
$vouchers = $this->voucherModel
|
||||
->where('school_year', $this->schoolYear)
|
||||
@@ -684,6 +736,19 @@ class DiscountController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
protected function currentSchoolYearName(?string $fallback = null): string
|
||||
{
|
||||
try {
|
||||
return service('schoolYearContext')->resolve($this->request)->yearName();
|
||||
} catch (\Throwable) {
|
||||
if ($fallback !== null && $fallback !== '') {
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
return (string) ($this->configModel->getConfig('school_year') ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔄 Helper: Current invoice balance (school-year scoped) = total - payments - discounts - refundsPaid
|
||||
*/
|
||||
|
||||
@@ -2010,8 +2010,14 @@ class ParentController extends BaseController
|
||||
private function enrollmentTuitionDue(array $students): float
|
||||
{
|
||||
$tuitionStudents = [];
|
||||
$existingTuitionStudentCount = 0;
|
||||
|
||||
foreach ($students as $student) {
|
||||
if ($this->countsTowardCurrentYearTuition($student)) {
|
||||
$existingTuitionStudentCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
|
||||
if (
|
||||
($evaluation['can_enroll'] ?? false) !== true
|
||||
@@ -2034,9 +2040,29 @@ class ParentController extends BaseController
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if ($existingTuitionStudentCount > 0) {
|
||||
$additionalStudentFee = (float) ($this->configModel->getConfig('second_student_fee') ?? 280);
|
||||
|
||||
return round(count($tuitionStudents) * $additionalStudentFee, 2);
|
||||
}
|
||||
|
||||
return (new FeeCalculationService())->calculateEnrollmentTuition($tuitionStudents);
|
||||
}
|
||||
|
||||
private function countsTowardCurrentYearTuition(array $student): bool
|
||||
{
|
||||
$status = strtolower(trim((string) ($student['enrollment_status'] ?? '')));
|
||||
if (in_array($status, ['withdrawn', 'withdraw under review', 'refund pending', 'denied', 'not enrolled'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array($status, ['admission under review', 'review & decision', 'payment pending', 'enrolled', 'waitlist'], true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return strtolower(trim((string) ($student['admission_status'] ?? ''))) === 'accepted';
|
||||
}
|
||||
|
||||
private function enrollmentFeeSchedule(string $selectedYear): array
|
||||
{
|
||||
$schoolYearConfig = $this->schoolYearConfig($selectedYear);
|
||||
@@ -2673,7 +2699,6 @@ class ParentController extends BaseController
|
||||
$this->db->transStart();
|
||||
|
||||
$studentAdded = false;
|
||||
$registeredStudentIds = [];
|
||||
|
||||
// Gather all POST data for last year flags
|
||||
$rawPost = $this->request->getPost();
|
||||
@@ -2700,9 +2725,6 @@ class ParentController extends BaseController
|
||||
|
||||
if ($result) {
|
||||
$studentAdded = true;
|
||||
if (is_numeric($result) && (int) $result > 0) {
|
||||
$registeredStudentIds[] = (int) $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2724,13 +2746,8 @@ class ParentController extends BaseController
|
||||
}
|
||||
|
||||
if ($studentAdded) {
|
||||
$enrollmentQuery = ['start' => 2];
|
||||
if ($registeredStudentIds !== []) {
|
||||
$enrollmentQuery['students'] = implode(',', array_values(array_unique($registeredStudentIds)));
|
||||
}
|
||||
|
||||
return redirect()->to(base_url('/parent/enroll_classes?' . http_build_query($enrollmentQuery)))
|
||||
->with('success', 'Registration successful! Continue enrollment by confirming your home address and phone number.');
|
||||
return redirect()->to(base_url('/parent/enroll_classes?' . http_build_query(['start' => 1])))
|
||||
->with('success', 'Registration successful! Continue enrollment by selecting all students you want to enroll.');
|
||||
}
|
||||
|
||||
return redirect()->to(base_url('/parent/child_register'))->with('success', 'Registration successful!');
|
||||
|
||||
@@ -332,7 +332,7 @@ class PaymentController extends ResourceController
|
||||
|
||||
// Read the search term (email or phone)
|
||||
$searchTerm = trim((string) $this->request->getGet('search_term'));
|
||||
$manualPaySchoolYear = $this->activeSchoolYearName();
|
||||
$manualPaySchoolYear = $this->currentSchoolYearName();
|
||||
|
||||
// --- Installment end date comes ONLY from config ---
|
||||
$installmentDateRaw = (string) ($this->installmentDate ?? '');
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Models\StudentSectionDistributionDraftModel;
|
||||
use App\Models\StudentAllergyModel;
|
||||
use App\Models\StudentMedicalConditionModel;
|
||||
use App\Support\Enrollment\DeliberationDecision;
|
||||
use CodeIgniter\Events\Events;
|
||||
use CodeIgniter\Database\Exceptions\DataException;
|
||||
use Throwable;
|
||||
|
||||
@@ -129,11 +130,21 @@ class StudentController extends BaseController
|
||||
$existing = $this->studentClassModel
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', (string)$this->schoolYear)
|
||||
->orderBy('id', 'DESC')
|
||||
->findAll();
|
||||
|
||||
$existingBySection = [];
|
||||
$scPk = 'id';
|
||||
foreach ($existing as $row) {
|
||||
$existingBySection[(int)($row['class_section_id'] ?? 0)] = $row;
|
||||
$rowEventFlag = (int)($row['is_event_only'] ?? 0);
|
||||
if ($rowEventFlag !== $isEventOnly) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sectionKey = (int)($row['class_section_id'] ?? 0);
|
||||
if ($sectionKey > 0 && !isset($existingBySection[$sectionKey])) {
|
||||
$existingBySection[$sectionKey] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$displayNames = [];
|
||||
@@ -184,8 +195,6 @@ class StudentController extends BaseController
|
||||
|
||||
try {
|
||||
// Upsert student_class entries per selection
|
||||
$scPk = $this->studentClassModel->primaryKey ?? 'id';
|
||||
|
||||
foreach ($classSectionIds as $cid) {
|
||||
$eventFlag = $isEventOnly ? 1 : 0;
|
||||
if (isset($existingBySection[$cid])) {
|
||||
@@ -215,6 +224,7 @@ class StudentController extends BaseController
|
||||
$attnStats = [];
|
||||
$scoreStats = [];
|
||||
$invoiceParentId = 0;
|
||||
$paymentPendingNotification = null;
|
||||
if (!$isEventOnly) {
|
||||
// Update enrollment for current term (if exists)
|
||||
$enroll = \Config\Services::enrollmentStatus(false)
|
||||
@@ -258,6 +268,16 @@ class StudentController extends BaseController
|
||||
if ((int) ($result['id'] ?? 0) <= 0) {
|
||||
throw new \RuntimeException('Failed to update enrollment.');
|
||||
}
|
||||
|
||||
$oldEnrollmentStatus = strtolower(trim(str_replace("\xc2\xa0", ' ', (string) ($result['old_status'] ?? $enroll['enrollment_status'] ?? ''))));
|
||||
$oldEnrollmentStatus = preg_replace('/\s+/', ' ', $oldEnrollmentStatus) ?? $oldEnrollmentStatus;
|
||||
if (in_array($oldEnrollmentStatus, ['admission under review', 'review & decision'], true)) {
|
||||
$paymentPendingNotification = [
|
||||
'parent_id' => $invoiceParentId,
|
||||
'student_id' => $studentId,
|
||||
'student_name' => trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')) ?: 'Student #' . $studentId,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 🔄 Re-tag attendance rows for this student in the current term (section + class)
|
||||
@@ -301,6 +321,10 @@ class StudentController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
if ($paymentPendingNotification !== null) {
|
||||
$this->triggerPaymentPendingNotification($paymentPendingNotification);
|
||||
}
|
||||
|
||||
$resp = [
|
||||
'ok' => true,
|
||||
'student_id' => $studentId,
|
||||
@@ -323,6 +347,51 @@ class StudentController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
private function triggerPaymentPendingNotification(array $notification): void
|
||||
{
|
||||
$parentId = (int) ($notification['parent_id'] ?? 0);
|
||||
$studentId = (int) ($notification['student_id'] ?? 0);
|
||||
if ($parentId <= 0 || $studentId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$parent = $this->userModel->find($parentId) ?? [];
|
||||
$invoice = $this->db->table('invoices')
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', (string) $this->schoolYear)
|
||||
->orderBy('created_at', 'DESC')
|
||||
->orderBy('id', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
$parentData = [
|
||||
'user_id' => $parent['id'] ?? $parentId,
|
||||
'email' => $parent['email'] ?? null,
|
||||
'firstname' => $parent['firstname'] ?? '',
|
||||
'lastname' => $parent['lastname'] ?? '',
|
||||
'school_year' => (string) $this->schoolYear,
|
||||
'portalLink' => base_url('/login'),
|
||||
];
|
||||
|
||||
if ($invoice) {
|
||||
$parentData['amount'] = (float) ($invoice['balance'] ?? $invoice['amount_due'] ?? $invoice['total_amount'] ?? 0);
|
||||
$parentData['due_date'] = $invoice['due_date'] ?? null;
|
||||
}
|
||||
|
||||
Events::trigger('paymentPending', $parentData, [[
|
||||
'student_id' => $studentId,
|
||||
'name' => (string) ($notification['student_name'] ?? ('Student #' . $studentId)),
|
||||
]]);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Payment pending notification after class assignment failed for parent {parent_id}: {error}', [
|
||||
'parent_id' => $parentId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a student-class assignment for the current term.
|
||||
*/
|
||||
@@ -1116,6 +1185,7 @@ class StudentController extends BaseController
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$activeEnrollmentStatuses = $this->distributionActiveEnrollmentStatuses();
|
||||
|
||||
if ($this->db->tableExists('student_class')) {
|
||||
$builder = $this->db->table('student_class sc')
|
||||
@@ -1141,16 +1211,16 @@ class StudentController extends BaseController
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('enrollments')) {
|
||||
$classIdExpression = $this->distributionEnrollmentClassIdExpression();
|
||||
$sectionIdExpression = $this->distributionEnrollmentSectionIdExpression();
|
||||
$builder = $this->db->table('enrollments e')
|
||||
->select('0 AS promotion_queue_id, e.student_id, e.school_year AS school_year_from, cs.class_id AS source_class_id, cs.class_section_name AS source_class_name, students.firstname, students.lastname, students.gender, students.age, students.dob, students.registration_grade', false)
|
||||
->select('0 AS promotion_queue_id, e.student_id, e.school_year AS school_year_from, ' . $classIdExpression . ' AS source_class_id, cs.class_section_name AS source_class_name, students.firstname, students.lastname, students.gender, students.age, students.dob, students.registration_grade', false)
|
||||
->join('students', 'students.id = e.student_id', 'inner')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = ' . $this->db->escape($year), 'left', false)
|
||||
->join('classSection cs', 'cs.class_section_id = ' . $sectionIdExpression . ' AND cs.school_year = ' . $this->db->escape($year), 'left', false)
|
||||
->where('e.school_year', $year)
|
||||
->whereIn('e.enrollment_status', ['admission under review', 'payment pending', 'enrolled'])
|
||||
->groupStart()
|
||||
->where('e.is_withdrawn', 0)
|
||||
->orWhere('e.is_withdrawn', null)
|
||||
->groupEnd();
|
||||
->whereIn('e.enrollment_status', $activeEnrollmentStatuses);
|
||||
|
||||
$this->applyEnrollmentNotWithdrawnFilter($builder);
|
||||
|
||||
if ($this->db->fieldExists('is_active', 'students')) {
|
||||
$builder->where('students.is_active', 1);
|
||||
@@ -1272,21 +1342,22 @@ class StudentController extends BaseController
|
||||
return [];
|
||||
}
|
||||
|
||||
$classIdExpression = $this->distributionEnrollmentClassIdExpression();
|
||||
$sectionIdExpression = $this->distributionEnrollmentSectionIdExpression();
|
||||
|
||||
$builder = $this->db->table('enrollments e')
|
||||
->select('0 AS promotion_queue_id, e.student_id, e.school_year AS school_year_from, cs.class_id AS source_class_id, cs.class_section_name AS source_class_name, students.firstname, students.lastname, students.gender, students.age, students.dob, students.registration_grade', false)
|
||||
->select('0 AS promotion_queue_id, e.student_id, e.school_year AS school_year_from, ' . $classIdExpression . ' AS source_class_id, cs.class_section_name AS source_class_name, students.firstname, students.lastname, students.gender, students.age, students.dob, students.registration_grade', false)
|
||||
->join('students', 'students.id = e.student_id', 'inner')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = ' . $this->db->escape($year), 'left', false)
|
||||
->join('classSection cs', 'cs.class_section_id = ' . $sectionIdExpression . ' AND cs.school_year = ' . $this->db->escape($year), 'left', false)
|
||||
->where('e.school_year', $year)
|
||||
->whereIn('e.enrollment_status', ['admission under review', 'payment pending', 'enrolled'])
|
||||
->groupStart()
|
||||
->where('e.is_withdrawn', 0)
|
||||
->orWhere('e.is_withdrawn', null)
|
||||
->groupEnd()
|
||||
->whereIn('e.enrollment_status', $this->distributionActiveEnrollmentStatuses())
|
||||
->groupStart()
|
||||
->where('cs.class_id', $classId)
|
||||
->orWhereIn('UPPER(students.registration_grade)', ['KG', 'K', 'KINDERGARTEN'])
|
||||
->groupEnd();
|
||||
|
||||
$this->applyEnrollmentNotWithdrawnFilter($builder);
|
||||
|
||||
if ($this->db->fieldExists('is_active', 'students')) {
|
||||
$builder->where('students.is_active', 1);
|
||||
}
|
||||
@@ -1459,19 +1530,17 @@ class StudentController extends BaseController
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('enrollments')) {
|
||||
$rows = $this->db->table('enrollments e')
|
||||
$sectionIdExpression = $this->distributionEnrollmentSectionIdExpression();
|
||||
$builder = $this->db->table('enrollments e')
|
||||
->select('cs.class_section_name')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = ' . $this->db->escape($previousYear), 'left', false)
|
||||
->join('classSection cs', 'cs.class_section_id = ' . $sectionIdExpression . ' AND cs.school_year = ' . $this->db->escape($previousYear), 'left', false)
|
||||
->where('e.student_id', $studentId)
|
||||
->where('e.school_year', $previousYear)
|
||||
->where('e.class_section_id IS NOT NULL', null, false)
|
||||
->whereIn('e.enrollment_status', ['admission under review', 'payment pending', 'enrolled'])
|
||||
->groupStart()
|
||||
->where('e.is_withdrawn', 0)
|
||||
->orWhere('e.is_withdrawn', null)
|
||||
->groupEnd()
|
||||
->get()
|
||||
->getResultArray();
|
||||
->where($sectionIdExpression . ' IS NOT NULL', null, false)
|
||||
->whereIn('e.enrollment_status', $this->distributionActiveEnrollmentStatuses());
|
||||
|
||||
$this->applyEnrollmentNotWithdrawnFilter($builder);
|
||||
$rows = $builder->get()->getResultArray();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$baseName = $this->baseClassNameForDistribution((string)($row['class_section_name'] ?? ''));
|
||||
@@ -1901,20 +1970,18 @@ class StudentController extends BaseController
|
||||
}
|
||||
|
||||
if (empty($names) && $this->db->tableExists('enrollments')) {
|
||||
$rows = $this->db->table('enrollments e')
|
||||
$sectionIdExpression = $this->distributionEnrollmentSectionIdExpression();
|
||||
$builder = $this->db->table('enrollments e')
|
||||
->select('cs.class_section_name')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = ' . $this->db->escape($previousYear), 'left', false)
|
||||
->join('classSection cs', 'cs.class_section_id = ' . $sectionIdExpression . ' AND cs.school_year = ' . $this->db->escape($previousYear), 'left', false)
|
||||
->where('e.student_id', $studentId)
|
||||
->where('e.school_year', $previousYear)
|
||||
->where('e.class_section_id IS NOT NULL', null, false)
|
||||
->whereIn('e.enrollment_status', ['payment pending', 'enrolled'])
|
||||
->groupStart()
|
||||
->where('e.is_withdrawn', 0)
|
||||
->orWhere('e.is_withdrawn', null)
|
||||
->groupEnd()
|
||||
->orderBy('cs.class_section_name', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
->where($sectionIdExpression . ' IS NOT NULL', null, false)
|
||||
->whereIn('e.enrollment_status', $this->distributionActiveEnrollmentStatuses())
|
||||
->orderBy('cs.class_section_name', 'ASC');
|
||||
|
||||
$this->applyEnrollmentNotWithdrawnFilter($builder);
|
||||
$rows = $builder->get()->getResultArray();
|
||||
foreach ($rows as $row) {
|
||||
$name = trim((string)($row['class_section_name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
@@ -2399,6 +2466,56 @@ class StudentController extends BaseController
|
||||
return $this->distributionBaseClassIdCache[$cacheKey];
|
||||
}
|
||||
|
||||
private function distributionActiveEnrollmentStatuses(): array
|
||||
{
|
||||
return [
|
||||
'admission under review',
|
||||
'review & decision',
|
||||
'payment pending',
|
||||
'enrolled',
|
||||
];
|
||||
}
|
||||
|
||||
private function applyEnrollmentNotWithdrawnFilter($builder): void
|
||||
{
|
||||
if (! $this->db->fieldExists('is_withdrawn', 'enrollments')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$builder->groupStart()
|
||||
->where('e.is_withdrawn', 0)
|
||||
->orWhere('e.is_withdrawn', null)
|
||||
->groupEnd();
|
||||
}
|
||||
|
||||
private function distributionEnrollmentClassIdExpression(): string
|
||||
{
|
||||
$parts = [];
|
||||
foreach (['assigned_grade_id', 'source_grade_id'] as $field) {
|
||||
if ($this->db->fieldExists($field, 'enrollments')) {
|
||||
$parts[] = 'e.' . $field;
|
||||
}
|
||||
}
|
||||
$parts[] = 'cs.class_id';
|
||||
|
||||
return count($parts) === 1 ? $parts[0] : 'COALESCE(' . implode(', ', $parts) . ')';
|
||||
}
|
||||
|
||||
private function distributionEnrollmentSectionIdExpression(): string
|
||||
{
|
||||
$parts = [];
|
||||
foreach (['assigned_class_section_id', 'class_section_id', 'source_class_section_id'] as $field) {
|
||||
if ($this->db->fieldExists($field, 'enrollments')) {
|
||||
$parts[] = 'e.' . $field;
|
||||
}
|
||||
}
|
||||
if (empty($parts)) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
return count($parts) === 1 ? $parts[0] : 'COALESCE(' . implode(', ', $parts) . ')';
|
||||
}
|
||||
|
||||
private function pendingDistributionDraftClassIds(string $year): array
|
||||
{
|
||||
if ($year === '' || ! $this->db->tableExists('student_section_distribution_drafts')) {
|
||||
|
||||
@@ -901,6 +901,19 @@ private function applyDistributionDraftToStudentClass(int $studentId, string $ye
|
||||
return;
|
||||
}
|
||||
|
||||
$enrollment = $this->db->table('enrollments')
|
||||
->select('class_section_id')
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', $year)
|
||||
->orderBy('updated_at', 'DESC')
|
||||
->orderBy('id', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if ((int)($enrollment['class_section_id'] ?? 0) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$targetSectionId = (int)($draft['class_section_id'] ?? 0);
|
||||
if ($targetSectionId <= 0) {
|
||||
return;
|
||||
|
||||
@@ -9,7 +9,7 @@ final class EnrollmentEligibility
|
||||
public const DEFERRED_MESSAGE = 'Re-enrollment cannot currently be completed because the final deliberation decision is deferred. Please contact the school administration for the next required step.';
|
||||
public const KG_MISSING_DECISION_ELIGIBLE_MESSAGE = 'KG students may complete registration now. Their new-year grade placement will be based on the school age-placement rule.';
|
||||
public const MISSING_DECISION_MESSAGE = 'Re-enrollment cannot currently be completed because no final deliberation decision is recorded for the student. Registration will become available after the school records a final decision.';
|
||||
public const ADULT_STUDENT_MESSAGE = 'This student will be 18 years old or older on September 1 of the selected school year. A parent or guardian cannot complete registration for this student. Please contact school administration.';
|
||||
public const ADULT_STUDENT_MESSAGE = 'This student will be 18 years old or older on September 1 of the selected school year. This student can no longer enroll with the school';
|
||||
public const ADULT_STUDENT_PARENT_PORTAL_MESSAGE = self::ADULT_STUDENT_MESSAGE;
|
||||
public const WITHDRAWN_PORTAL_MESSAGE = 'This student is currently marked as Withdrawn and cannot be enrolled at this time. Please contact school administration.';
|
||||
public const SIBLING_PORTAL_MESSAGE = 'Enrollment cannot continue because the family record requires administrative review. Please contact school administration.';
|
||||
|
||||
@@ -279,7 +279,20 @@ $hasSettledEnrollmentStatus = static function (?string $status, ?string $admissi
|
||||
};
|
||||
$enrollableCount = 0;
|
||||
$withdrawableCount = 0;
|
||||
$currentYearTuitionStudentCount = 0;
|
||||
foreach (($students ?? []) as $student) {
|
||||
$studentStatus = strtolower(trim((string) ($student['enrollment_status'] ?? '')));
|
||||
$studentAdmissionStatus = strtolower(trim((string) ($student['admission_status'] ?? '')));
|
||||
if (
|
||||
! in_array($studentStatus, ['withdrawn', 'withdraw under review', 'refund pending', 'denied', 'not enrolled'], true)
|
||||
&& (
|
||||
in_array($studentStatus, ['admission under review', 'review & decision', 'payment pending', 'enrolled', 'waitlist'], true)
|
||||
|| $studentAdmissionStatus === 'accepted'
|
||||
)
|
||||
) {
|
||||
$currentYearTuitionStudentCount++;
|
||||
}
|
||||
|
||||
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
|
||||
if (
|
||||
(($evaluation['can_enroll'] ?? false) === true || ($evaluation['parent_enrollment_allowed'] ?? false) === true)
|
||||
@@ -942,6 +955,7 @@ $studentCount = count($students ?? []);
|
||||
'carryOverBalance' => (float) ($familyFinancialSummary['carry_forward_balance'] ?? $familyFinancialSummary['carry_over_balance'] ?? 0),
|
||||
'currentBalance' => (float) ($familyFinancialSummary['current_year_balance'] ?? $familyFinancialSummary['current_balance'] ?? 0),
|
||||
'amountDue' => (float) ($familyFinancialSummary['total_enrollment_due'] ?? $familyFinancialSummary['amount_due'] ?? 0),
|
||||
'currentYearTuitionStudentCount' => (int) ($currentYearTuitionStudentCount ?? 0),
|
||||
], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?>;
|
||||
let currentStep = 0;
|
||||
let hasAcceptedSchoolPolicy = <?= $hasAcceptedSchoolPolicy ? 'true' : 'false' ?>;
|
||||
@@ -975,7 +989,8 @@ $studentCount = count($students ?? []);
|
||||
|
||||
function calculateSelectedTuition() {
|
||||
return selectedEnrollInputs().reduce((total, input, index) => {
|
||||
return total + (index === 0 ? Number(feeSchedule.firstStudentFee || 0) : Number(feeSchedule.secondStudentFee || 0));
|
||||
const familyPosition = Number(familyFinancial.currentYearTuitionStudentCount || 0) + index;
|
||||
return total + (familyPosition === 0 ? Number(feeSchedule.firstStudentFee || 0) : Number(feeSchedule.secondStudentFee || 0));
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -1037,8 +1052,8 @@ $studentCount = count($students ?? []);
|
||||
}
|
||||
|
||||
feeReviewList.innerHTML = cards.map((student, index) => {
|
||||
const tuitionFee = index === 0 ? feeSchedule.firstStudentFee : feeSchedule.secondStudentFee;
|
||||
const tier = index === 0 ? 'First student tuition tier' : 'Additional student tuition tier';
|
||||
const familyPosition = Number(familyFinancial.currentYearTuitionStudentCount || 0) + index;
|
||||
const tuitionFee = familyPosition === 0 ? feeSchedule.firstStudentFee : feeSchedule.secondStudentFee;
|
||||
const lines = [
|
||||
['Student', student.name],
|
||||
['School ID', student.schoolId || 'N/A'],
|
||||
@@ -1717,11 +1732,11 @@ $studentCount = count($students ?? []);
|
||||
});
|
||||
|
||||
refreshEligibility().then(() => {
|
||||
if (enrollmentStartStep >= 2 && startButton && !startButton.disabled && !<?= $flashError ? 'true' : 'false' ?>) {
|
||||
if (enrollmentStartStep >= 1 && startButton && !startButton.disabled && !<?= $flashError ? 'true' : 'false' ?>) {
|
||||
openEnrollmentAtStep(enrollmentStartStep - 1);
|
||||
}
|
||||
}).catch(() => {
|
||||
if (enrollmentStartStep >= 2 && startButton && !startButton.disabled && !<?= $flashError ? 'true' : 'false' ?>) {
|
||||
if (enrollmentStartStep >= 1 && startButton && !startButton.disabled && !<?= $flashError ? 'true' : 'false' ?>) {
|
||||
openEnrollmentAtStep(enrollmentStartStep - 1);
|
||||
}
|
||||
// Keep server-rendered eligibility if refresh fails.
|
||||
|
||||
@@ -101,20 +101,13 @@ $disableEmergencyBtn = count($emergencies) >= $maxEmergency;
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
$hasUnenrolled = false;
|
||||
$unenrolledStudentIds = [];
|
||||
foreach ($existingKids as $kid) {
|
||||
if ($kid['enrollment'] == 0) {
|
||||
$hasUnenrolled = true;
|
||||
$studentId = (int) ($kid['id'] ?? 0);
|
||||
if ($studentId > 0) {
|
||||
$unenrolledStudentIds[] = $studentId;
|
||||
}
|
||||
}
|
||||
}
|
||||
usort($unenrolledStudentIds, static fn (int $a, int $b): int => $b <=> $a);
|
||||
$enrollmentUrl = base_url('/parent/enroll_classes?' . http_build_query([
|
||||
'start' => 2,
|
||||
'students' => implode(',', $unenrolledStudentIds),
|
||||
'start' => 1,
|
||||
]));
|
||||
?>
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
'administrator/sections/auto-distribute',
|
||||
'admin/enrollment/new-students',
|
||||
'payment/unpaid-parents',
|
||||
'discounts/list',
|
||||
'expenses/index',
|
||||
'reimbursements/index',
|
||||
];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user