configService->context(); $semester = (string) ($context['semester'] ?? ''); $schoolYear = (string) ($context['school_year'] ?? ''); $classSectionIds = $this->normalizeIds($classSectionIds); if ($studentId <= 0 || empty($classSectionIds) || $schoolYear === '') { return ['ok' => false, 'message' => 'Missing required data (student/class section).']; } $student = Student::query()->find($studentId); if (! $student) { return ['ok' => false, 'message' => 'Student not found.']; } $sections = ClassSection::query() ->whereIn('class_section_id', $classSectionIds) ->orWhereIn('id', $classSectionIds) ->get() ->toArray(); if (empty($sections)) { return ['ok' => false, 'message' => 'Class/Section not found.']; } $sectionMap = []; foreach ($sections as $section) { $sectionMap[(int) ($section['class_section_id'] ?? 0)] = $section; } $missing = array_diff($classSectionIds, array_keys($sectionMap)); if (! empty($missing)) { return ['ok' => false, 'message' => 'One or more selected classes do not exist.']; } $existing = StudentClass::query() ->where('student_id', $studentId) ->where('school_year', $schoolYear) ->get() ->keyBy(fn ($row) => (int) ($row->class_section_id ?? 0)); $primarySectionId = $classSectionIds[0]; $primarySection = $sectionMap[$primarySectionId] ?? reset($sectionMap); $parentClassId = (int) ($primarySection['class_id'] ?? 0); if ($parentClassId <= 0) { $parentClassId = (int) (ClassSection::getClassId($primarySectionId) ?? 0); } $attendanceStats = []; $scoreStats = []; DB::transaction(function () use ( $studentId, $schoolYear, $classSectionIds, $isEventOnly, $userId, $existing, $semester, $primarySectionId, $parentClassId, &$attendanceStats, &$scoreStats ): void { foreach ($classSectionIds as $classSectionId) { $eventFlag = $isEventOnly ? 1 : 0; if ($existing->has($classSectionId)) { $eventFlag = (int) ($existing[$classSectionId]->is_event_only ?? 0); } $payload = [ 'student_id' => $studentId, 'class_section_id' => $classSectionId, 'school_year' => $schoolYear, 'semester' => $semester, 'is_event_only' => $eventFlag, 'updated_by' => $userId, 'updated_at' => now(), ]; if ($existing->has($classSectionId)) { $existing[$classSectionId]->fill($payload)->save(); } else { StudentClass::query()->create($payload + [ 'created_at' => now(), ]); } } if (! $isEventOnly) { $enrollment = Enrollment::query() ->where('student_id', $studentId) ->where('school_year', $schoolYear) ->when($semester !== '', fn ($q) => $q->where('semester', $semester)) ->first(); if ($enrollment) { $enrollment->update([ 'class_section_id' => $primarySectionId, 'enrollment_status' => 'payment pending', 'admission_status' => 'accepted', 'updated_at' => now(), ]); } $attendanceStats = $this->updateAttendance( $studentId, $primarySectionId, $parentClassId, $semester, $schoolYear, $userId ); $scoreStats = $this->updateScores( $studentId, $primarySectionId, $semester, $schoolYear, $userId ); } }); $displayNames = []; foreach ($classSectionIds as $classSectionId) { $section = $sectionMap[$classSectionId] ?? null; if (! $section) { continue; } $name = (string) ($section['class_section_name'] ?? ''); if ($name === '') { $name = 'Section #'.$classSectionId; } $displayNames[] = $name; } return [ 'ok' => true, 'student_id' => $studentId, 'class_section_id' => $primarySectionId, 'class_section_ids' => $classSectionIds, 'class_section_names' => $displayNames, 'attendance_updates' => $attendanceStats, 'score_updates' => $scoreStats, ]; } public function removeClass(int $studentId, int $classSectionId, ?int $userId = null): array { $context = $this->configService->context(); $semester = (string) ($context['semester'] ?? ''); $schoolYear = (string) ($context['school_year'] ?? ''); if ($studentId <= 0 || $classSectionId <= 0 || $schoolYear === '') { return ['ok' => false, 'message' => 'Missing required data (student/class section).']; } $row = StudentClass::query() ->where('student_id', $studentId) ->where('class_section_id', $classSectionId) ->where('school_year', $schoolYear) ->first(); if (! $row) { return ['ok' => false, 'message' => 'Assignment not found for this student/class.']; } $remainingIds = []; $remainingNames = []; DB::transaction(function () use ( $studentId, $classSectionId, $schoolYear, $semester, $userId, &$remainingIds, &$remainingNames ): void { StudentClass::query() ->where('student_id', $studentId) ->where('class_section_id', $classSectionId) ->where('school_year', $schoolYear) ->delete(); $remainingIds = StudentClass::getClassSectionIdsByStudentId($studentId, $schoolYear); $remainingNames = StudentClass::getClassSectionsByStudentId($studentId, $schoolYear, true); $enrollment = Enrollment::query() ->where('student_id', $studentId) ->where('school_year', $schoolYear) ->when($semester !== '', fn ($q) => $q->where('semester', $semester)) ->first(); if ($enrollment) { $newClassId = ! empty($remainingIds) ? $remainingIds[0] : null; if ((int) $enrollment->class_section_id === $classSectionId || $newClassId !== null) { $updates = [ 'class_section_id' => $newClassId, 'updated_at' => now(), ]; if (Schema::hasColumn('enrollments', 'updated_by')) { $updates['updated_by'] = $userId; } $enrollment->update($updates); } } }); return [ 'ok' => true, 'student_id' => $studentId, 'removed_class_id' => $classSectionId, 'remaining_ids' => $remainingIds, 'remaining_names' => $remainingNames, ]; } private function updateAttendance( int $studentId, int $classSectionId, int $classId, string $semester, string $schoolYear, ?int $modifiedBy ): array { $now = now()->toDateTimeString(); $attendanceUpdated = DB::table('attendance_data') ->where('student_id', $studentId) ->where('semester', $semester) ->where('school_year', $schoolYear) ->update([ 'class_section_id' => $classSectionId, 'class_id' => $classId, 'updated_at' => $now, 'modified_by' => $modifiedBy, ]); $recordUpdated = DB::table('attendance_record') ->where('student_id', $studentId) ->where('semester', $semester) ->where('school_year', $schoolYear) ->update([ 'class_section_id' => $classSectionId, 'updated_at' => $now, 'modified_by' => $modifiedBy, ]); return [ 'attendance_data_updated' => $attendanceUpdated, 'attendance_record_updated' => $recordUpdated, ]; } private function updateScores( int $studentId, int $classSectionId, string $semester, string $schoolYear, ?int $modifiedBy ): array { $now = now()->toDateTimeString(); $tables = [ 'homework', 'quiz', 'project', 'participation', 'midterm_exam', 'final_exam', 'final_score', 'semester_scores', ]; $results = []; foreach ($tables as $table) { $payload = [ 'class_section_id' => $classSectionId, 'updated_at' => $now, ]; if ($modifiedBy !== null && Schema::hasColumn($table, 'updated_by')) { $payload['updated_by'] = $modifiedBy; } $results[$table.'_updated'] = DB::table($table) ->where('student_id', $studentId) ->where('semester', $semester) ->where('school_year', $schoolYear) ->update($payload); } return $results; } private function normalizeIds(array $ids): array { $values = array_map('intval', $ids); $values = array_values(array_unique(array_filter($values, static fn ($v) => $v > 0))); return $values; } }