fix is_active student flag logic and make moving with enrollment status
This commit is contained in:
@@ -15,7 +15,6 @@ use App\Models\StudentAllergyModel;
|
||||
use App\Models\StudentMedicalConditionModel;
|
||||
use App\Support\Enrollment\DeliberationDecision;
|
||||
use CodeIgniter\Database\Exceptions\DataException;
|
||||
use Config\Services;
|
||||
use Throwable;
|
||||
|
||||
class StudentController extends BaseController
|
||||
@@ -225,14 +224,20 @@ class StudentController extends BaseController
|
||||
|
||||
if ($enroll) {
|
||||
$enPk = $this->enrollmentModel->primaryKey ?? 'id';
|
||||
if (!$this->enrollmentModel->update($enroll[$enPk], [
|
||||
$result = \Config\Services::enrollmentStatus(false)->upsertStatus([
|
||||
'id' => (int) $enroll[$enPk],
|
||||
'student_id' => $studentId,
|
||||
'parent_id' => (int) ($enroll['parent_id'] ?? 0),
|
||||
'school_year' => (string) $this->schoolYear,
|
||||
'semester' => (string) $this->semester,
|
||||
'class_section_id' => $primarySectionId,
|
||||
'enrollment_status' => 'payment pending',
|
||||
// Ensure admission is marked accepted once moved out of review
|
||||
'admission_status' => 'accepted',
|
||||
'updated_at' => $now,
|
||||
])) {
|
||||
throw new \RuntimeException('Failed to update enrollment: ' . json_encode($this->enrollmentModel->errors()));
|
||||
], $userId ?: null, 'student_class_assignment');
|
||||
if ((int) ($result['id'] ?? 0) <= 0) {
|
||||
throw new \RuntimeException('Failed to update enrollment.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,242 +392,6 @@ class StudentController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
public function removedStudents()
|
||||
{
|
||||
$schoolYear = (string)($this->schoolYear ?? '');
|
||||
|
||||
$classMap = [];
|
||||
$activeYearStudentIds = [];
|
||||
$classQuery = $this->db->table('student_class sc')
|
||||
->select('sc.student_id, cs.class_section_name')
|
||||
->join('classSection cs', 'cs.class_section_id = sc.class_section_id', 'left');
|
||||
if ($schoolYear !== '') {
|
||||
$classQuery->where('sc.school_year', $schoolYear);
|
||||
}
|
||||
$classRows = $classQuery->get()->getResultArray();
|
||||
|
||||
foreach ($classRows as $row) {
|
||||
$sid = (int)($row['student_id'] ?? 0);
|
||||
if ($sid > 0) {
|
||||
$activeYearStudentIds[$sid] = true;
|
||||
}
|
||||
|
||||
$name = trim((string)($row['class_section_name'] ?? ''));
|
||||
if ($sid <= 0 || $name === '') continue;
|
||||
$classMap[$sid][] = $name;
|
||||
}
|
||||
|
||||
$students = $this->studentModel
|
||||
->select('id, school_id, firstname, lastname, gender, age, is_active')
|
||||
->orderBy('lastname', 'ASC')
|
||||
->orderBy('firstname', 'ASC')
|
||||
->findAll();
|
||||
|
||||
$activeStudents = [];
|
||||
$removedStudents = [];
|
||||
foreach ($students as $student) {
|
||||
$studentId = (int)($student['id'] ?? 0);
|
||||
$isGloballyActive = (int)($student['is_active'] ?? 0) === 1;
|
||||
$hasSelectedYearClass = $studentId > 0 && isset($activeYearStudentIds[$studentId]);
|
||||
|
||||
if ($isGloballyActive && $hasSelectedYearClass) {
|
||||
$activeStudents[] = $student;
|
||||
} else {
|
||||
$removedStudents[] = $student;
|
||||
}
|
||||
}
|
||||
|
||||
$attachClassNames = static function (array $students) use ($classMap): array {
|
||||
foreach ($students as &$student) {
|
||||
$sid = (int)($student['id'] ?? 0);
|
||||
$names = $classMap[$sid] ?? [];
|
||||
$names = array_values(array_unique(array_filter($names)));
|
||||
$student['class_sections'] = $names;
|
||||
$student['class_section_name'] = !empty($names) ? implode(', ', $names) : 'No class assigned';
|
||||
}
|
||||
unset($student);
|
||||
return $students;
|
||||
};
|
||||
|
||||
return view('administrator/removed_students', [
|
||||
'active_students' => $attachClassNames($activeStudents),
|
||||
'removed_students' => $attachClassNames($removedStudents),
|
||||
'school_year' => $schoolYear,
|
||||
'active_school_year' => (string)($this->schoolYear ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
public function setStudentActive()
|
||||
{
|
||||
$studentId = (int) $this->request->getPost('student_id');
|
||||
$isActiveRaw = (string) $this->request->getPost('is_active');
|
||||
$isActive = $isActiveRaw === '1' ? 1 : 0;
|
||||
$now = utc_now();
|
||||
$userId = (int) (session()->get('user_id') ?? 0);
|
||||
|
||||
if ($studentId <= 0) {
|
||||
return redirect()->back()->with('error', 'Invalid student ID.');
|
||||
}
|
||||
|
||||
$student = $this->studentModel->find($studentId);
|
||||
if (!$student) {
|
||||
return redirect()->back()->with('error', 'Student not found.');
|
||||
}
|
||||
|
||||
if (!$this->studentModel->update($studentId, ['is_active' => $isActive])) {
|
||||
return redirect()->back()->with('error', 'Unable to update student status.');
|
||||
}
|
||||
|
||||
$message = $isActive ? 'Student restored successfully.' : 'Student removed successfully.';
|
||||
|
||||
if ($isActive === 1) {
|
||||
$hasCurrentClass = $this->studentClassModel
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', (string)$this->schoolYear)
|
||||
->where('semester', (string)$this->semester)
|
||||
->first();
|
||||
|
||||
if (!$hasCurrentClass) {
|
||||
$lastClass = $this->studentClassModel
|
||||
->where('student_id', $studentId)
|
||||
->orderBy('updated_at', 'DESC')
|
||||
->orderBy('created_at', 'DESC')
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
$restoreClassId = (int)($lastClass['class_section_id'] ?? 0);
|
||||
if ($restoreClassId > 0) {
|
||||
$inserted = $this->studentClassModel->insert([
|
||||
'student_id' => $studentId,
|
||||
'class_section_id' => $restoreClassId,
|
||||
'semester' => (string)$this->semester,
|
||||
'school_year' => (string)$this->schoolYear,
|
||||
'description' => $lastClass['description'] ?? null,
|
||||
'updated_by' => $userId ?: null,
|
||||
'updated_at' => $now,
|
||||
'created_at' => $now,
|
||||
]);
|
||||
|
||||
if ($inserted) {
|
||||
$classLabel = (string)($this->classSectionModel->getClassSectionNameBySectionId($restoreClassId) ?? '');
|
||||
if ($classLabel !== '') {
|
||||
$message .= ' Class assignment restored to ' . $classLabel . '.';
|
||||
}
|
||||
} else {
|
||||
$message .= ' No class assignment found for the current term.';
|
||||
return redirect()->to(base_url('administrator/removed_students'))->with('warning', $message);
|
||||
}
|
||||
} else {
|
||||
$message .= ' No class assignment found for the current term.';
|
||||
return redirect()->to(base_url('administrator/removed_students'))->with('warning', $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isActive === 0) {
|
||||
if (!$this->notifyParentOfStudentRemoval($studentId)) {
|
||||
$message .= ' Parent notification email could not be sent.';
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->to(base_url('administrator/removed_students'))->with('success', $message);
|
||||
}
|
||||
|
||||
private function notifyParentOfStudentRemoval(int $studentId): bool
|
||||
{
|
||||
try {
|
||||
$studentRow = $this->studentModel
|
||||
->select([
|
||||
'students.firstname AS student_firstname',
|
||||
'students.lastname AS student_lastname',
|
||||
'students.school_id',
|
||||
'users.id AS parent_id',
|
||||
'users.firstname AS parent_firstname',
|
||||
'users.lastname AS parent_lastname',
|
||||
'users.email AS parent_email',
|
||||
])
|
||||
->join('users', 'users.id = students.parent_id', 'left')
|
||||
->where('students.id', $studentId)
|
||||
->first();
|
||||
|
||||
if (empty($studentRow)) {
|
||||
log_message('warning', "Student removal email skipped: student {$studentId} not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$parentEmail = trim((string)($studentRow['parent_email'] ?? ''));
|
||||
if ($parentEmail === '') {
|
||||
log_message('warning', "Student removal email skipped: missing parent email for student {$studentId}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$studentName = trim(trim((string)($studentRow['student_firstname'] ?? '')) . ' ' . trim((string)($studentRow['student_lastname'] ?? '')));
|
||||
$parentName = trim(trim((string)($studentRow['parent_firstname'] ?? '')) . ' ' . trim((string)($studentRow['parent_lastname'] ?? '')));
|
||||
if ($parentName === '') {
|
||||
$parentName = 'Parent/Guardian';
|
||||
}
|
||||
|
||||
$subject = 'Withdrawal Notice for ' . ($studentName !== '' ? $studentName : 'Your Student');
|
||||
|
||||
$html = view('emails/student_removed', [
|
||||
'student_name' => $studentName,
|
||||
'school_id' => $studentRow['school_id'] ?? '',
|
||||
'parent_name' => $parentName,
|
||||
'signature' => 'AlRahma School Administration',
|
||||
], ['saveData' => true]);
|
||||
|
||||
$sent = $this->sendHtmlEmail($parentEmail, $subject, $html);
|
||||
$logLevel = $sent ? 'info' : 'warning';
|
||||
log_message($logLevel, "Student removal email " . ($sent ? '' : 'not ') . "sent (studentId: {$studentId}, parent: {$parentEmail}).");
|
||||
|
||||
return $sent;
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Student removal notification failed: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function sendHtmlEmail(string $to, string $subject, string $html): bool
|
||||
{
|
||||
try {
|
||||
$service = function_exists('service') ? service('emailService') : null;
|
||||
if (!$service && method_exists(Services::class, 'emailService')) {
|
||||
$service = Services::emailService();
|
||||
}
|
||||
|
||||
if ($service && method_exists($service, 'send')) {
|
||||
$result = $service->send($to, $subject, $html, 'student-removal');
|
||||
if ($result) {
|
||||
return true;
|
||||
}
|
||||
log_message('debug', 'Custom emailService failed to send student removal notice.');
|
||||
}
|
||||
|
||||
$email = Services::email();
|
||||
$cfg = config('Email');
|
||||
$fromEmail = $cfg->fromEmail ?? $cfg->SMTPUser ?? 'no-reply@example.com';
|
||||
$fromName = $cfg->fromName ?? 'Al Rahma Sunday School';
|
||||
|
||||
$email->setTo($to);
|
||||
$email->setFrom($fromEmail, $fromName);
|
||||
$email->setSubject($subject);
|
||||
$email->setMessage($html);
|
||||
$email->setMailType('html');
|
||||
|
||||
$ok = $email->send();
|
||||
if (!$ok) {
|
||||
$debug = method_exists($email, 'printDebugger') ? $email->printDebugger(['headers', 'subject']) : 'no debugger';
|
||||
log_message('debug', 'CI Email send failed for student removal: ' . print_r($debug, true));
|
||||
}
|
||||
|
||||
return $ok;
|
||||
} catch (\Throwable $e) {
|
||||
log_message('debug', 'sendHtmlEmail exception for student removal: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function updateStudentAttendanceSection(
|
||||
int $studentId,
|
||||
int $newClassSectionId,
|
||||
@@ -2742,7 +2511,6 @@ class StudentController extends BaseController
|
||||
'rfid_tag' => 'permit_empty|max_length[100]',
|
||||
'semester' => 'permit_empty|in_list[Fall,Spring,Summer]',
|
||||
'is_new' => 'required|in_list[0,1]',
|
||||
'is_active' => 'permit_empty|in_list[0,1]',
|
||||
|
||||
// Free-text lists; parsed later
|
||||
'medical_conditions' => 'permit_empty',
|
||||
@@ -2777,7 +2545,6 @@ class StudentController extends BaseController
|
||||
'rfid_tag' => trim((string) $request->getPost('rfid_tag')),
|
||||
'semester' => trim((string) $request->getPost('semester')),
|
||||
'is_new' => (string) $request->getPost('is_new', FILTER_SANITIZE_NUMBER_INT),
|
||||
'is_active' => (string) ($rawPost['is_active'] ?? ''),
|
||||
|
||||
// raw lists (may be missing from POST entirely)
|
||||
'medical_conditions' => (string) ($rawPost['medical_conditions'] ?? ''),
|
||||
@@ -2841,11 +2608,8 @@ class StudentController extends BaseController
|
||||
'school_year' => $in['school_year'] ?: null,
|
||||
'rfid_tag' => $in['rfid_tag'] ?: null,
|
||||
'semester' => $in['semester'] ?: null,
|
||||
'is_new' => (int) ($in['is_new'] === '1'),
|
||||
];
|
||||
if (array_key_exists('is_active', $rawPost)) {
|
||||
$studentData['is_active'] = (int) ($in['is_active'] === '1');
|
||||
}
|
||||
'is_new' => (int) ($in['is_new'] === '1'),
|
||||
];
|
||||
|
||||
// Normalize health lists (server-side safety)
|
||||
$normConditions = $this->normalizeHealthList($in['medical_conditions'], 100); // -> condition_name
|
||||
|
||||
Reference in New Issue
Block a user