fix enrollment and email send to parent
This commit is contained in:
@@ -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')) {
|
||||
|
||||
Reference in New Issue
Block a user