diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index a0e6098..d637a00 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -206,7 +206,11 @@ abstract class BaseController extends Controller 'admin', 'administrator', 'administrative staff', + 'parent', 'principal', + 'teacher', + 'teacher assistant', + 'teacher_assistant', 'vice principal', ]); } diff --git a/app/Controllers/ClassProgressController.php b/app/Controllers/ClassProgressController.php index 859f308..64f4cb7 100644 --- a/app/Controllers/ClassProgressController.php +++ b/app/Controllers/ClassProgressController.php @@ -54,6 +54,7 @@ class ClassProgressController extends BaseController public function create() { + $context = $this->resolveSchoolYearContext(); $teacherId = (int) session()->get('user_id'); $assignments = $this->loadTeacherSections($teacherId); $first = $assignments[0] ?? null; @@ -76,12 +77,15 @@ class ClassProgressController extends BaseController 'classId' => $classId, 'sundayOptions' => $sundayOptions, 'defaultWeekStart' => $defaultWeekStart, + 'isSchoolYearReadonly' => $context->isReadonly(), ]; return view('teacher/class_progress_submit', $data); } public function store() { + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); + [, $schoolYear] = $this->resolveCurrentTerm(); $subjectSections = self::SUBJECT_SECTIONS; $rules = [ 'class_section_id' => 'required|integer', @@ -132,8 +136,9 @@ class ClassProgressController extends BaseController ->select('id') ->where('class_section_id', $classSectionId) ->where('week_start', $weekStart) - ->where('teacher_id', $teacherId) - ->findAll(); + ->where('teacher_id', $teacherId); + $this->applyProgressSchoolYearScope($existingReports, $schoolYear); + $existingReports = $existingReports->findAll(); if (! $confirmOverwrite && ! empty($existingReports)) { return redirect()->back() @@ -175,7 +180,7 @@ class ClassProgressController extends BaseController 'homework' => $homework ?: null, 'status' => $status, 'flags_json' => $this->normalizeFlags($this->request->getPost('flags')), - ]; + ] + $this->progressReportSchoolYearData($schoolYear); $reportId = $this->reportModel->insert($data, true); $attachmentField = "attachment_$slug"; @@ -198,6 +203,7 @@ class ClassProgressController extends BaseController public function history() { $teacherId = (int) session()->get('user_id'); + [$semester, $schoolYear] = $this->resolveCurrentTerm(); $assignments = $this->loadTeacherSections($teacherId); $selectedSectionId = (int) $this->request->getGet('class_section_id'); $validSectionIds = array_column($assignments, 'class_section_id'); @@ -207,7 +213,6 @@ class ClassProgressController extends BaseController if ($selectedSectionId && ! in_array($selectedSectionId, $validSectionIds, true)) { $selectedSectionId = $validSectionIds[0] ?? null; } - [$semester, $schoolYear] = $this->resolveCurrentTerm(); $allowedTeacherIds = $this->resolveAssignedTeacherIds($selectedSectionId, $semester, $schoolYear); if (empty($allowedTeacherIds)) { $allowedTeacherIds = [$teacherId]; @@ -217,6 +222,7 @@ class ClassProgressController extends BaseController ->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') ->whereIn('teacher_id', $allowedTeacherIds); + $this->applyProgressSchoolYearScope($builder, $schoolYear, true); if ($selectedSectionId) { $builder->where('class_progress_reports.class_section_id', $selectedSectionId); } @@ -251,24 +257,27 @@ class ClassProgressController extends BaseController 'subjectSections' => self::SUBJECT_SECTIONS, 'classSectionOptions' => $sectionOptions, 'selectedSectionId' => $selectedSectionId, + 'schoolYear' => $schoolYear, + 'isSchoolYearReadonly' => $this->resolveSchoolYearContext()->isReadonly(), ]); } public function view($id) { $teacherId = (int) session()->get('user_id'); - $row = $this->reportModel + [$semester, $schoolYear] = $this->resolveCurrentTerm(); + $rowQuery = $this->reportModel ->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name') ->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') - ->where('class_progress_reports.id', (int) $id) - ->first(); + ->where('class_progress_reports.id', (int) $id); + $this->applyProgressSchoolYearScope($rowQuery, $schoolYear, true); + $row = $rowQuery->first(); if (! $row) { throw new PageNotFoundException('Progress report not found.'); } - [$semester, $schoolYear] = $this->resolveCurrentTerm(); $allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear); if (empty($allowedTeacherIds)) { if ($teacherId !== (int) $row['teacher_id']) { @@ -286,7 +295,9 @@ class ClassProgressController extends BaseController ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') ->whereIn('teacher_id', $allowedTeacherIds) ->where('class_progress_reports.class_section_id', $row['class_section_id']) - ->where('week_start', $row['week_start']) + ->where('week_start', $row['week_start']); + $this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear); + $weeklyReports = $weeklyReportsQuery ->orderBy('subject', 'ASC') ->findAll(); @@ -313,17 +324,18 @@ class ClassProgressController extends BaseController public function edit($id) { $teacherId = (int) session()->get('user_id'); - $row = $this->reportModel + [$semester, $schoolYear] = $this->resolveCurrentTerm(); + $rowQuery = $this->reportModel ->select('class_progress_reports.*, cs.class_section_name') ->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left') - ->where('class_progress_reports.id', (int) $id) - ->first(); + ->where('class_progress_reports.id', (int) $id); + $this->applyProgressSchoolYearScope($rowQuery, $schoolYear); + $row = $rowQuery->first(); if (! $row) { throw new PageNotFoundException('Progress report not found.'); } - [$semester, $schoolYear] = $this->resolveCurrentTerm(); $allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear); if (empty($allowedTeacherIds)) { if ($teacherId !== (int) $row['teacher_id']) { @@ -338,7 +350,9 @@ class ClassProgressController extends BaseController ->select('class_progress_reports.*') ->whereIn('teacher_id', $allowedTeacherIds) ->where('class_section_id', $row['class_section_id']) - ->where('week_start', $row['week_start']) + ->where('week_start', $row['week_start']); + $this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear); + $weeklyReports = $weeklyReportsQuery ->orderBy('subject', 'ASC') ->findAll(); @@ -405,18 +419,23 @@ class ClassProgressController extends BaseController 'isEdit' => true, 'formAction' => base_url('teacher/progress/update/' . (int) $row['id']), 'submitLabel' => 'Update Progress', + 'isSchoolYearReadonly' => $this->resolveSchoolYearContext()->isReadonly(), ]); } public function update($id) { + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); $teacherId = (int) session()->get('user_id'); - $row = $this->reportModel->find((int) $id); + [$semester, $schoolYear] = $this->resolveCurrentTerm(); + $rowQuery = $this->reportModel + ->where('class_progress_reports.id', (int) $id); + $this->applyProgressSchoolYearScope($rowQuery, $schoolYear); + $row = $rowQuery->first(); if (! $row) { throw new PageNotFoundException('Progress report not found.'); } - [$semester, $schoolYear] = $this->resolveCurrentTerm(); $allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear); if (empty($allowedTeacherIds)) { if ($teacherId !== (int) $row['teacher_id']) { @@ -473,8 +492,9 @@ class ClassProgressController extends BaseController ->select('id') ->where('class_section_id', $classSectionId) ->where('week_start', $weekStart) - ->where('teacher_id', $teacherId) - ->findAll(); + ->where('teacher_id', $teacherId); + $this->applyProgressSchoolYearScope($conflicts, $schoolYear); + $conflicts = $conflicts->findAll(); if (! $confirmOverwrite && ! empty($conflicts)) { return redirect()->back() ->withInput() @@ -497,7 +517,9 @@ class ClassProgressController extends BaseController ->select('class_progress_reports.*') ->whereIn('teacher_id', $allowedTeacherIds) ->where('class_section_id', $classSectionId) - ->where('week_start', $row['week_start']) + ->where('week_start', $row['week_start']); + $this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear); + $weeklyReports = $weeklyReportsQuery ->orderBy('subject', 'ASC') ->findAll(); @@ -533,7 +555,7 @@ class ClassProgressController extends BaseController 'unit_title' => $unitTitle, 'covered' => $covered, 'homework' => $homework ?: null, - ]; + ] + $this->progressReportSchoolYearData($schoolYear); if ($flagsInput !== null) { $data['flags_json'] = $this->normalizeFlags($flagsInput); } @@ -698,8 +720,7 @@ class ClassProgressController extends BaseController protected function loadTeacherSections(int $teacherId): array { - $schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); - $semester = (string) ($this->configModel->getConfig('semester') ?? ''); + [$semester, $schoolYear] = $this->resolveCurrentTerm(); return $this->teacherClassModel->getClassAssignmentsByUserId($teacherId, $schoolYear, $semester); } @@ -878,7 +899,7 @@ class ClassProgressController extends BaseController protected function resolveProgressDateRange(): ?array { - $schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); + $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? '')); if ($schoolYear === '') { return null; } @@ -946,11 +967,65 @@ class ClassProgressController extends BaseController protected function resolveCurrentTerm(): array { - $schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); + $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? '')); $semester = (string) ($this->configModel->getConfig('semester') ?? ''); return [$semester, $schoolYear]; } + protected function applyProgressSchoolYearScope($builder, string $schoolYear, bool $canScopeBySection = false): void + { + if ($schoolYear === '') { + return; + } + + $db = db_connect(); + $hasReportYear = $db->fieldExists('school_year', 'class_progress_reports'); + $hasSectionYear = $db->fieldExists('school_year', 'classSection'); + $semesterResolver = new SemesterRangeService($this->configModel); + [$rangeStart, $rangeEnd] = $semesterResolver->getSchoolYearRange($schoolYear); + + if ($hasReportYear) { + if ($rangeStart !== '' && $rangeEnd !== '') { + $builder + ->groupStart() + ->where('class_progress_reports.school_year', $schoolYear) + ->orGroupStart() + ->groupStart() + ->where('class_progress_reports.school_year IS NULL', null, false) + ->orWhere('class_progress_reports.school_year', '') + ->groupEnd() + ->where('class_progress_reports.week_start >=', $rangeStart) + ->where('class_progress_reports.week_start <=', $rangeEnd) + ->groupEnd() + ->groupEnd(); + return; + } + + $builder->where('class_progress_reports.school_year', $schoolYear); + return; + } + + if ($canScopeBySection && $hasSectionYear) { + $builder->where('cs.school_year', $schoolYear); + return; + } + + if ($rangeStart !== '' && $rangeEnd !== '') { + $builder + ->where('class_progress_reports.week_start >=', $rangeStart) + ->where('class_progress_reports.week_start <=', $rangeEnd); + } + } + + protected function progressReportSchoolYearData(string $schoolYear): array + { + if ($schoolYear === '' || ! db_connect()->fieldExists('school_year', 'class_progress_reports')) { + return []; + } + + return ['school_year' => $schoolYear]; + } + protected function resolveAssignedTeacherIds(?int $classSectionId, string $semester, string $schoolYear): array { if (! $classSectionId || $schoolYear === '') { diff --git a/app/Controllers/ParentProgressController.php b/app/Controllers/ParentProgressController.php index de6f577..94ce102 100644 --- a/app/Controllers/ParentProgressController.php +++ b/app/Controllers/ParentProgressController.php @@ -29,6 +29,7 @@ class ParentProgressController extends BaseController public function index() { + $schoolYear = $this->currentSchoolYearName(); $students = $this->getParentStudents(); $sectionIds = array_values(array_unique(array_filter(array_map( static fn (array $student): int => (int) ($student['class_section_id'] ?? 0), @@ -42,7 +43,8 @@ class ParentProgressController extends BaseController ->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name') ->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') - ->whereIn('class_progress_reports.class_section_id', $sectionIds); + ->whereIn('class_progress_reports.class_section_id', $sectionIds) + ->where('class_progress_reports.school_year', $schoolYear); $rows = $builder ->orderBy('week_start', 'DESC') @@ -75,10 +77,12 @@ class ParentProgressController extends BaseController public function view($id) { + $schoolYear = $this->currentSchoolYearName(); $row = $this->reportModel ->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name') ->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') + ->where('class_progress_reports.school_year', $schoolYear) ->find((int) $id); if (! $row || ! $this->isSectionAccessible($row['class_section_id'] ?? null)) { @@ -92,6 +96,7 @@ class ParentProgressController extends BaseController ->select('class_progress_reports.*') ->where('class_section_id', $row['class_section_id']) ->where('week_start', $row['week_start']) + ->where('school_year', $schoolYear) ->orderBy('subject', 'ASC') ->findAll(); @@ -165,6 +170,7 @@ class ParentProgressController extends BaseController $rows = $this->enrollmentModel ->select('class_section_id') ->where('parent_id', $parentId) + ->where('school_year', $this->currentSchoolYearName()) ->where('is_withdrawn', 0) ->groupBy('class_section_id') ->findAll(); @@ -213,6 +219,7 @@ class ParentProgressController extends BaseController ->join('students s', 's.id = e.student_id') ->join('classSection cs', 'cs.class_section_id = e.class_section_id', 'left') ->where('e.parent_id', $parentId) + ->where('e.school_year', $this->currentSchoolYearName()) ->where('e.is_withdrawn', 0) ->orderBy('e.updated_at', 'DESC') ->orderBy('e.created_at', 'DESC') diff --git a/app/Controllers/ParentReportCardController.php b/app/Controllers/ParentReportCardController.php index c0665a2..776057e 100644 --- a/app/Controllers/ParentReportCardController.php +++ b/app/Controllers/ParentReportCardController.php @@ -31,7 +31,7 @@ class ParentReportCardController extends BaseController return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.'); } - $schoolYear = trim((string) ($this->request->getGet('school_year') ?? $this->currentSchoolYearName())); + $schoolYear = trim((string) $this->currentSchoolYearName()); $semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? '')); $builder = $this->db->table('students s') @@ -82,7 +82,7 @@ class ParentReportCardController extends BaseController throw new PageNotFoundException('Student not found.'); } - $schoolYear = trim((string) ($this->request->getGet('school_year') ?? $this->currentSchoolYearName())); + $schoolYear = trim((string) $this->currentSchoolYearName()); $semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? '')); $this->touchAcknowledgement($parentId, (int) $studentId, $schoolYear, $semester, [ diff --git a/app/Controllers/PrintRequests.php b/app/Controllers/PrintRequests.php index 7802ed5..4979191 100644 --- a/app/Controllers/PrintRequests.php +++ b/app/Controllers/PrintRequests.php @@ -12,6 +12,7 @@ use App\Models\TeacherClassModel; use App\Models\AdminNotificationSubjectModel; use App\Models\NotificationModel; use App\Models\UserNotificationModel; +use App\Services\SemesterRangeService; use CodeIgniter\Exceptions\PageNotFoundException; class PrintRequests extends BaseController @@ -46,18 +47,26 @@ class PrintRequests extends BaseController public function teacher_index() { $teacher_id = session()->get('user_id'); + $context = $this->resolveSchoolYearContext(); + $schoolYear = $context->yearName(); - $data['print_requests'] = $this->printRequestModel + $printRequestsQuery = $this->printRequestModel ->select('print_requests.*, admins.firstname as admin_firstname, admins.lastname as admin_lastname') ->join('users as admins', 'admins.id = print_requests.admin_id', 'left') - ->where('print_requests.teacher_id', $teacher_id) + ->join('classSection cs', 'cs.class_section_id = print_requests.class_id', 'left') + ->where('print_requests.teacher_id', $teacher_id); + $this->applyPrintRequestSchoolYearScope($printRequestsQuery, $schoolYear); + $data['print_requests'] = $printRequestsQuery + ->orderBy('print_requests.required_by', 'DESC') + ->orderBy('print_requests.id', 'DESC') ->findAll(); - $teacher_classes = $this->teacherClassModel->getClassByTeacherId($teacher_id); + $teacher_classes = $this->teacherClassModel->getClassAssignmentsByUserId((int) $teacher_id, $schoolYear); $data['class_id'] = !empty($teacher_classes) ? $teacher_classes[0]['class_section_id'] : null; $dateOptions = $this->buildRequiredByOptions(); $data['sundays'] = $dateOptions['sundays']; $data['times'] = $dateOptions['times']; + $data['isSchoolYearReadonly'] = $context->isReadonly(); return view('print_requests/teacher_index', $data); } @@ -94,6 +103,9 @@ class PrintRequests extends BaseController public function create() { + $context = $this->resolveSchoolYearContext(); + $this->assertSchoolYearWritable($context); + $schoolYear = $context->yearName(); $validationRules = [ 'file' => 'uploaded[file]|max_size[file,5120]|ext_in[file,pdf,jpg,png,jpeg,doc,docx,txt]', 'page_selection' => 'permit_empty|regex_match[/^\\s*\\d+(?:\\s*-\\s*\\d+)?(?:\\s*,\\s*\\d+(?:\\s*-\\s*\\d+)?)*\\s*$/]', @@ -125,7 +137,7 @@ class PrintRequests extends BaseController 'required_by' => $this->request->getPost('required_by'), 'pickup_method' => $this->request->getPost('pickup_method'), 'status' => 'not_assigned', - ]; + ] + $this->printRequestSchoolYearData($schoolYear); $printRequestId = (int) $this->printRequestModel->insert($data, true); if ($printRequestId > 0) { @@ -175,6 +187,7 @@ class PrintRequests extends BaseController // Case 2: Teacher edit if ($this->request->getPost('num_copies')) { + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); $user_id = session()->get('user_id'); if ($request['teacher_id'] != $user_id) { return redirect()->to('teacher/print-requests')->with('error', 'You are not authorized to edit this request.'); @@ -230,6 +243,7 @@ class PrintRequests extends BaseController public function delete($id) { + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); $teacher_id = session()->get('user_id'); $request = $this->printRequestModel->find($id); @@ -260,6 +274,9 @@ class PrintRequests extends BaseController public function copy($id) { $teacher_id = session()->get('user_id'); + $context = $this->resolveSchoolYearContext(); + $this->assertSchoolYearWritable($context); + $schoolYear = $context->yearName(); $request = $this->printRequestModel->find($id); if (!$request) { @@ -296,7 +313,7 @@ class PrintRequests extends BaseController 'required_by' => $request['required_by'], 'pickup_method' => $request['pickup_method'], 'status' => 'not_assigned', - ]; + ] + $this->printRequestSchoolYearData($schoolYear); $copiedId = (int) $this->printRequestModel->insert($data, true); if ($copiedId > 0) { @@ -308,6 +325,9 @@ class PrintRequests extends BaseController public function createCopy() { + $context = $this->resolveSchoolYearContext(); + $this->assertSchoolYearWritable($context); + $schoolYear = $context->yearName(); $validationRules = [ 'num_copies' => 'required|integer|greater_than[0]', 'required_by' => 'required|valid_date', @@ -329,7 +349,7 @@ class PrintRequests extends BaseController 'required_by' => $this->request->getPost('required_by'), 'pickup_method' => $this->request->getPost('pickup_method'), 'status' => 'not_assigned', - ]; + ] + $this->printRequestSchoolYearData($schoolYear); $printRequestId = (int) $this->printRequestModel->insert($data, true); if ($printRequestId > 0) { @@ -374,6 +394,95 @@ class PrintRequests extends BaseController ]; } + private function applyPrintRequestSchoolYearScope($query, string $schoolYear): void + { + if ($schoolYear === '') { + return; + } + + $hasPrintRequestYear = $this->printRequestsHaveSchoolYearColumn(); + $hasClassSectionYear = db_connect()->fieldExists('school_year', 'classSection'); + $range = (new SemesterRangeService($this->configModel))->getSchoolYearRange($schoolYear); + [$rangeStart, $rangeEnd] = $range; + + if ($hasPrintRequestYear && $hasClassSectionYear) { + $query->groupStart() + ->where('print_requests.school_year', $schoolYear) + ->orGroupStart() + ->groupStart() + ->where('print_requests.school_year IS NULL', null, false) + ->orWhere('print_requests.school_year', '') + ->groupEnd() + ->where('cs.school_year', $schoolYear) + ->groupEnd(); + + if ($rangeStart !== '' && $rangeEnd !== '') { + $query + ->orGroupStart() + ->groupStart() + ->where('print_requests.school_year IS NULL', null, false) + ->orWhere('print_requests.school_year', '') + ->groupEnd() + ->groupStart() + ->where('cs.school_year IS NULL', null, false) + ->orWhere('cs.school_year', '') + ->groupEnd() + ->where('print_requests.required_by >=', $rangeStart . ' 00:00:00') + ->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59') + ->groupEnd(); + } + + $query->groupEnd(); + return; + } + + if (! $hasPrintRequestYear && $hasClassSectionYear) { + $query->where('cs.school_year', $schoolYear); + return; + } + + if ($rangeStart !== '' && $rangeEnd !== '') { + if ($hasPrintRequestYear) { + $query + ->groupStart() + ->where('print_requests.school_year', $schoolYear) + ->orGroupStart() + ->groupStart() + ->where('print_requests.school_year IS NULL', null, false) + ->orWhere('print_requests.school_year', '') + ->groupEnd() + ->where('print_requests.required_by >=', $rangeStart . ' 00:00:00') + ->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59') + ->groupEnd() + ->groupEnd(); + return; + } + + $query + ->where('print_requests.required_by >=', $rangeStart . ' 00:00:00') + ->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59'); + return; + } + + if ($hasPrintRequestYear) { + $query->where('print_requests.school_year', $schoolYear); + } + } + + private function printRequestSchoolYearData(string $schoolYear): array + { + if ($schoolYear === '' || ! $this->printRequestsHaveSchoolYearColumn()) { + return []; + } + + return ['school_year' => $schoolYear]; + } + + private function printRequestsHaveSchoolYearColumn(): bool + { + return db_connect()->fieldExists('school_year', 'print_requests'); + } + public function serveFile(string $filename, string $mode = 'inline') { $safeName = basename(trim($filename)); diff --git a/app/Controllers/View/ExamDraftController.php b/app/Controllers/View/ExamDraftController.php index f4862f6..c0ecf44 100644 --- a/app/Controllers/View/ExamDraftController.php +++ b/app/Controllers/View/ExamDraftController.php @@ -91,6 +91,9 @@ class ExamDraftController extends BaseController public function teacherIndex() { + $this->syncAcademicContext(); + $context = $this->resolveSchoolYearContext(); + $teacherId = (int) (session()->get('user_id') ?? 0); if ($teacherId <= 0) { return redirect()->to('/login'); @@ -175,11 +178,15 @@ class ExamDraftController extends BaseController 'maxUploadBytes' => self::MAX_UPLOAD_BYTES, 'validation' => $validation, 'printableDraftIds' => $printableIds, + 'isSchoolYearReadonly' => $context->isReadonly(), ]); } public function teacherStore() { + $this->syncAcademicContext(); + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); + $teacherId = (int) (session()->get('user_id') ?? 0); if ($teacherId <= 0) { return redirect()->to('/login'); @@ -193,10 +200,13 @@ class ExamDraftController extends BaseController return redirect()->back()->withInput()->with('error', 'Select a class section before submitting.'); } - $assignment = $this->teacherClassModel + $assignmentQuery = $this->teacherClassModel ->where('teacher_id', $teacherId) - ->where('class_section_id', $classSectionId) - ->first(); + ->where('class_section_id', $classSectionId); + if ($this->schoolYear !== '') { + $assignmentQuery->where('school_year', $this->schoolYear); + } + $assignment = $assignmentQuery->first(); if (empty($assignment)) { return redirect()->back()->withInput()->with('error', 'You are not assigned to the selected class section.'); @@ -785,6 +795,8 @@ class ExamDraftController extends BaseController public function teacherStatusFeed() { + $this->syncAcademicContext(); + $teacherId = (int) (session()->get('user_id') ?? 0); if ($teacherId <= 0) { return $this->response->setStatusCode(401); @@ -848,6 +860,8 @@ class ExamDraftController extends BaseController ->join('classSection cs', 'cs.class_section_id = exam_drafts.class_section_id', 'left') ->join('users u', 'u.id = exam_drafts.' . $this->authorIdColumn, 'left'); + $this->applyExamDraftYearScope($query); + if (empty($classSectionIds)) { return $query->where('exam_drafts.' . $this->authorIdColumn, $teacherId); } @@ -859,19 +873,25 @@ class ExamDraftController extends BaseController ->where('exam_drafts.' . $this->authorIdColumn . ' !=', $teacherId) ->where('exam_drafts.status !=', 'draft'); - if ($this->schoolYear !== '') { - $query->where('exam_drafts.school_year', $this->schoolYear); - } - if ($this->semester !== '') { - $query->where('exam_drafts.semester', $this->semester); - } - $query->groupEnd() ->groupEnd(); return $query; } + private function syncAcademicContext(): void + { + $this->schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? '')); + $this->semester = (string) ($this->configModel->getConfig('semester') ?? ''); + } + + private function applyExamDraftYearScope($query): void + { + if ($this->schoolYear !== '') { + $query->where('exam_drafts.school_year', $this->schoolYear); + } + } + private function attachTeacherDraftContext(array $row, int $viewerId): array { $isOwn = $this->draftTeacherId($row) === $viewerId; diff --git a/app/Controllers/View/GradingController.php b/app/Controllers/View/GradingController.php index 1f271b4..967020c 100644 --- a/app/Controllers/View/GradingController.php +++ b/app/Controllers/View/GradingController.php @@ -1065,13 +1065,7 @@ class GradingController extends Controller public function belowSixty() { - $configuredYear = (string) $this->schoolYear; - - $schoolYear = trim((string)($this->request->getGet('school_year') ?? '')); - - if ($schoolYear === '') { - $schoolYear = $configuredYear; - } + $schoolYear = $this->currentSchoolYearName((string) $this->schoolYear); // This page is Fall only. $semester = 'fall'; @@ -1124,6 +1118,16 @@ public function belowSixty() 'canViewGrading' => $canViewGrading, ]); } + + private function currentSchoolYearName(?string $fallback = null): string + { + try { + return service('schoolYearContext')->resolve($this->request)->yearName(); + } catch (\Throwable) { + return trim((string) ($fallback ?? '')); + } + } + public function editBelowSixtyEmail() { $studentId = (int)$this->request->getGet('student_id'); diff --git a/app/Controllers/View/HomeworkController.php b/app/Controllers/View/HomeworkController.php index b0a238d..7ea2104 100644 --- a/app/Controllers/View/HomeworkController.php +++ b/app/Controllers/View/HomeworkController.php @@ -5,7 +5,7 @@ namespace App\Controllers\View; use App\Models\HomeworkModel; use App\Models\StudentModel; use App\Models\StudentClassModel; -use CodeIgniter\Controller; +use App\Controllers\BaseController; use App\Models\TeacherClassModel; use App\Models\ConfigurationModel; use App\Models\UserModel; @@ -16,7 +16,7 @@ use App\Controllers\View\GradingController; use App\Models\GradingLockModel; use App\Models\MissingScoreOverrideModel; -class HomeworkController extends Controller +class HomeworkController extends BaseController { protected $db; protected $semesterScoreService; @@ -59,11 +59,12 @@ class HomeworkController extends Controller } } - public function updateHomeworkScores(array $scores = null, int $updatedBy = null, int $classSectionId = null) + public function updateHomeworkScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null) { $scores = $this->request->getPost('scores'); $semester = $this->request->getPost('semester') ?? $this->semester; - $schoolYear = $this->request->getPost('school_year') ?? $this->schoolYear; + $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); if ($updatedBy === null) { $updatedBy = session()->get('user_id'); @@ -195,6 +196,8 @@ class HomeworkController extends Controller $selectedSemester = $this->getSelectedSemester(); $normalized = $this->normalizeSemesterSelection($selectedSemester); $semesterLabel = $normalized !== '' ? ucfirst($normalized) : $selectedSemester; + $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); // Step 1: Get the highest existing homework_index $existingIndexes = $this->homeworkModel @@ -202,7 +205,7 @@ class HomeworkController extends Controller ->where('class_section_id', $classSectionId) //->where('teacher_id', $updatedBy) ->whereIn('semester', $this->getSemesterVariants($semesterLabel)) - ->where('school_year', $this->schoolYear) + ->where('school_year', $schoolYear) ->groupBy('homework_index') ->orderBy('homework_index', 'DESC') ->findAll(); @@ -219,6 +222,7 @@ class HomeworkController extends Controller // Step 2: Get all students in the class $students = $this->studentClassModel ->where('class_section_id', $classSectionId) + ->where('school_year', $schoolYear) ->findAll(); // Step 3: Insert a new homework row for each student if not already exists @@ -233,7 +237,7 @@ class HomeworkController extends Controller ->where('class_section_id', $classSectionId) //->where('teacher_id', $updatedBy) ->whereIn('semester', $this->getSemesterVariants($semesterLabel)) - ->where('school_year', $this->schoolYear) + ->where('school_year', $schoolYear) ->first(); if ($existing) continue; @@ -246,7 +250,7 @@ class HomeworkController extends Controller 'homework_index' => $nextIndex, 'score' => null, 'semester' => $semesterLabel, - 'school_year' => $this->schoolYear, + 'school_year' => $schoolYear, 'created_at' => utc_now(), 'updated_at' => utc_now() ]; @@ -269,15 +273,23 @@ class HomeworkController extends Controller public function showHomework() { $updatedBy = session()->get('user_id'); - $classSectionId = $this->getClassSectionIdForTeacher($updatedBy); + $semester = $this->getSelectedSemester(); + $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + $classSectionId = (int) ( + $this->request->getGet('class_section_id') + ?? $this->request->getPost('class_section_id') + ?? 0 + ); + if ($classSectionId <= 0) { + $classSectionId = (int) ($this->getClassSectionIdForTeacher($updatedBy, $schoolYear, $semester) ?? 0); + } if (!$classSectionId) { return redirect()->back()->with('status', 'No class section found for the current teacher.'); } session()->set('class_section_id', $classSectionId); - $semester = $this->getSelectedSemester(); - $homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $semester, $this->schoolYear); + $homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $semester, $schoolYear); if (empty($homeworkHeaders)) { $homeworkHeaders = [1]; } @@ -285,15 +297,15 @@ class HomeworkController extends Controller $classSectionId, $homeworkHeaders, $semester, - $this->schoolYear + $schoolYear ); - $missingOkMap = $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $this->schoolYear, 'homework'); + $missingOkMap = $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $schoolYear, 'homework'); return view('teacher/add_homework', [ 'students' => $students, 'homeworkHeaders' => $homeworkHeaders, 'semester' => $semester, - 'schoolYear' => $this->schoolYear, + 'schoolYear' => $schoolYear, 'class_section_id' => $classSectionId, 'missingOkMap' => $missingOkMap, ]); @@ -503,9 +515,16 @@ class HomeworkController extends Controller return $students; } - private function getClassSectionIdForTeacher($updatedBy) + private function getClassSectionIdForTeacher($updatedBy, ?string $schoolYear = null, ?string $semester = null) { - $class = $this->teacherClassModel->where('teacher_id', $updatedBy)->first(); + $builder = $this->teacherClassModel->where('teacher_id', $updatedBy); + if ($schoolYear !== null && $schoolYear !== '') { + $builder->where('school_year', $schoolYear); + } + if ($semester !== null && $semester !== '') { + $builder->where('semester', $semester); + } + $class = $builder->first(); return $class['class_section_id'] ?? null; } diff --git a/app/Controllers/View/InvoiceController.php b/app/Controllers/View/InvoiceController.php index d9a205d..692adca 100644 --- a/app/Controllers/View/InvoiceController.php +++ b/app/Controllers/View/InvoiceController.php @@ -1644,23 +1644,8 @@ private function getGradeLevel($grade): array return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.'); } - $currentSchoolYear = $this->schoolYear; - - // Get available school years from invoices - $schoolYears = $this->invoiceModel - ->select('school_year') - ->distinct() - ->where('parent_id', $parentId) - ->orderBy('school_year', 'DESC') - ->findAll(); - - // Determine which year to show - $selectedYear = $this->request->getGet('school_year'); - - if (empty($selectedYear)) { - $hasCurrentYear = in_array($currentSchoolYear, array_column($schoolYears, 'school_year')); - $selectedYear = $hasCurrentYear ? $currentSchoolYear : (!empty($schoolYears) ? $schoolYears[0]['school_year'] : null); - } + $currentSchoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + $selectedYear = $currentSchoolYear; // Fetch invoices for the selected year $invoices = []; @@ -1718,7 +1703,6 @@ private function getGradeLevel($grade): array return view('/parent/invoice_payment', [ 'invoices' => $invoices, - 'schoolYears' => $schoolYears, 'selectedYear' => $selectedYear, 'currentSchoolYear' => $currentSchoolYear, 'dueDate' => $this->dueDate, diff --git a/app/Controllers/View/ParentAttendanceReportController.php b/app/Controllers/View/ParentAttendanceReportController.php index ec1119f..a48bd0d 100644 --- a/app/Controllers/View/ParentAttendanceReportController.php +++ b/app/Controllers/View/ParentAttendanceReportController.php @@ -58,7 +58,7 @@ class ParentAttendanceReportController extends BaseController [$sundays, $defaultDate] = $this->computeSundays(); // Load upcoming reports for preview/edit (today and forward within this school year) - $schoolYear = (string) $this->configModel->getConfig('school_year'); + $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? '')); $todayYmd = local_date(utc_now(), 'Y-m-d'); $previewRows = $this->reportModel->builder() ->select('parent_attendance_reports.*, s.firstname, s.lastname') @@ -141,6 +141,7 @@ class ParentAttendanceReportController extends BaseController $dismissTime = $post['dismiss_time'] ?? null; $reasonRaw = $post['reason'] ?? null; $reason = is_string($reasonRaw) ? trim($reasonRaw) : null; + $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? '')); // Enforce Sunday-only and future-or-today selection $todayCheck = new \DateTime('today'); @@ -157,7 +158,7 @@ class ParentAttendanceReportController extends BaseController } $validDates = []; - $cap = $this->firstSundayOfJune((string) $this->configModel->getConfig('school_year')); + $cap = $this->firstSundayOfJune($schoolYear); $lateCutoff = null; $lateNow = null; @@ -236,7 +237,6 @@ class ParentAttendanceReportController extends BaseController // Upper bound already enforced by $validDates building $semester = (string) $this->configModel->getConfig('semester'); - $schoolYear = (string) $this->configModel->getConfig('school_year'); $semesterResolver = new SemesterRangeService($this->configModel); $inserted = 0; @@ -493,7 +493,7 @@ class ParentAttendanceReportController extends BaseController } // Upper bound: first Sunday of June (school year end) - $schoolYear = (string) $this->configModel->getConfig('school_year'); + $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? '')); $cap = $this->firstSundayOfJune($schoolYear); $dates = []; @@ -1458,12 +1458,15 @@ class ParentAttendanceReportController extends BaseController return $this->response->setJSON(['ok' => true, 'students' => []]); } + $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? '')); + // Fetch existing submissions for these students on this date $rows = $this->reportModel->builder() ->select('parent_attendance_reports.student_id, parent_attendance_reports.type, parent_attendance_reports.report_date, s.firstname, s.lastname') ->join('students s', 's.id = parent_attendance_reports.student_id', 'left') ->whereIn('parent_attendance_reports.report_date', $dateValues) ->whereIn('parent_attendance_reports.student_id', $studentIds) + ->where('parent_attendance_reports.school_year', $schoolYear) ->get()->getResultArray(); $map = []; diff --git a/app/Controllers/View/ParentController.php b/app/Controllers/View/ParentController.php index 6118574..27cb8eb 100644 --- a/app/Controllers/View/ParentController.php +++ b/app/Controllers/View/ParentController.php @@ -164,10 +164,7 @@ class ParentController extends BaseController return redirect()->back()->with('error', 'Parent session not found.'); } - $currentSchoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); - - // Get selected school year (no semester filter on parent view) - $selectedYear = $this->request->getVar('school_year') ?? $currentSchoolYear; + $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); // Build query to retrieve attendance $builder = $this->db->table('attendance_data'); @@ -180,19 +177,10 @@ class ParentController extends BaseController $query = $builder->get(); $attendanceResults = $query->getResultArray(); - // Get list of available school years - $schoolYears = $this->db->table('attendance_data') - ->select('school_year') - ->distinct() - ->orderBy('school_year', 'DESC') - ->get() - ->getResultArray(); - // If no records found, set a flag to show message in the view if (empty($attendanceResults)) { return view('/parent/attendance', [ 'attendance' => null, - 'schoolYears' => $schoolYears, 'selectedYear' => $selectedYear, 'selectedSemester' => null, //'error' => 'No attendance records found for the selected school year and semester.' @@ -202,7 +190,6 @@ class ParentController extends BaseController // Return view with attendance results return view('/parent/attendance', [ 'attendance' => $attendanceResults, - 'schoolYears' => $schoolYears, 'selectedYear' => $selectedYear, 'selectedSemester' => null, 'error' => null @@ -212,7 +199,6 @@ class ParentController extends BaseController return view('/parent/attendance', [ 'attendance' => null, - 'schoolYears' => [], 'selectedYear' => $selectedYear ?? null, 'selectedSemester' => null, 'error' => 'Failed to retrieve attendance data. Please try again later.' @@ -269,9 +255,9 @@ class ParentController extends BaseController return redirect()->back()->with('error', 'Configuration error: School year missing.'); } - // Get selected school year from request or fallback - $selectedYear = $this->request->getGet('school_year') ?? $this->schoolYear; - $isEditable = ($selectedYear === $this->schoolYear); + $context = $this->resolveSchoolYearContext(); + $selectedYear = $context->yearName(); + $isEditable = ! $context->isReadonly(); // Get parent ID from session $parentId = session()->get('user_id'); @@ -370,22 +356,9 @@ class ParentController extends BaseController ); } - // Fetch available school years - $schoolYears = $this->db->table('enrollments') - ->select('school_year') - ->distinct() - ->orderBy('school_year', 'DESC') - ->get() - ->getResultArray(); - - if (empty($schoolYears)) { - $schoolYears[] = ['school_year' => $this->schoolYear]; - } - // Render view return view('/parent/enroll_classes', [ 'students' => $students, - 'schoolYears' => $schoolYears, 'selectedYear' => $selectedYear, 'isEditable' => $isEditable, 'withdrawalDeadline' => $this->withdrawalDeadline, diff --git a/app/Controllers/View/ProjectController.php b/app/Controllers/View/ProjectController.php index 69c2901..9152cf5 100644 --- a/app/Controllers/View/ProjectController.php +++ b/app/Controllers/View/ProjectController.php @@ -5,7 +5,7 @@ namespace App\Controllers\View; use App\Models\ProjectModel; use App\Models\StudentModel; use App\Models\StudentClassModel; -use CodeIgniter\Controller; +use App\Controllers\BaseController; use App\Models\TeacherClassModel; use App\Models\ConfigurationModel; use RuntimeException; @@ -15,7 +15,7 @@ use App\Models\GradingLockModel; use App\Models\MissingScoreOverrideModel; -class ProjectController extends Controller +class ProjectController extends BaseController { protected $db; protected ConfigurationModel $configModel; @@ -42,16 +42,26 @@ class ProjectController extends Controller $studentModel = new StudentModel(); $projectModel = new ProjectModel(); - $teacherClass = $teacherClassModel->where('teacher_id', $updatedBy)->first(); - if (!$teacherClass) { - return redirect()->back()->with('status', 'No class section found for the current teacher.'); - } - - $classSectionId = $teacherClass['class_section_id']; - session()->set('class_section_id', $classSectionId); - $semester = $this->getTeacherSelectedSemester(); $schoolYear = $this->getTeacherSchoolYear(); + $classSectionId = (int) ( + $this->request->getGet('class_section_id') + ?? $this->request->getPost('class_section_id') + ?? 0 + ); + if ($classSectionId <= 0) { + $teacherClass = $teacherClassModel + ->where('teacher_id', $updatedBy) + ->where('school_year', $schoolYear) + ->where('semester', $semester) + ->first(); + if (!$teacherClass) { + return redirect()->back()->with('status', 'No class section found for the current teacher.'); + } + $classSectionId = (int) $teacherClass['class_section_id']; + } + + session()->set('class_section_id', $classSectionId); $projectRows = $projectModel ->select('id, project_index') @@ -71,6 +81,7 @@ class ProjectController extends Controller $studentsClasses = $studentClassModel ->active() ->where('student_class.class_section_id', $classSectionId) + ->where('student_class.school_year', $schoolYear) ->findAll(); $students = []; @@ -115,7 +126,7 @@ class ProjectController extends Controller ]); } - public function updateProjectScores(array $scores = null, int $updatedBy = null, int $classSectionId = null) + public function updateProjectScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null) { $scores = $this->request->getPost('scores'); $projectModel = new ProjectModel(); @@ -127,6 +138,7 @@ class ProjectController extends Controller $classSectionId = session()->get('class_section_id'); $semester = $this->getTeacherSelectedSemester(); $schoolYear = $this->getTeacherSchoolYear(); + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); $classSectionId = (int) ($classSectionId ?? 0); if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) { return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.'); @@ -190,7 +202,8 @@ class ProjectController extends Controller 'student_id' => $studentId, 'project_index' => $index, 'class_section_id' => $classSectionId, - 'semester' => $semester + 'semester' => $semester, + 'school_year' => $schoolYear, ])->first(); $data = [ @@ -231,6 +244,7 @@ class ProjectController extends Controller $classSectionId = session()->get('class_section_id'); $semester = $this->getTeacherSelectedSemester(); $schoolYear = $this->getTeacherSchoolYear(); + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); $projectModel = new ProjectModel(); $studentClassModel = new StudentClassModel(); @@ -257,6 +271,7 @@ class ProjectController extends Controller $students = $studentClassModel ->active() ->where('student_class.class_section_id', $classSectionId) + ->where('student_class.school_year', $schoolYear) ->findAll(); foreach ($students as $student) { @@ -533,6 +548,6 @@ class ProjectController extends Controller private function getTeacherSchoolYear(): string { - return (string) $this->schoolYear; + return $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); } } diff --git a/app/Controllers/View/QuizController.php b/app/Controllers/View/QuizController.php index 88923eb..602b59f 100644 --- a/app/Controllers/View/QuizController.php +++ b/app/Controllers/View/QuizController.php @@ -2,7 +2,7 @@ namespace App\Controllers\View; -use CodeIgniter\Controller; +use App\Controllers\BaseController; use App\Models\TeacherClassModel; use App\Models\StudentClassModel; use App\Models\StudentModel; @@ -17,7 +17,7 @@ use Config\Services; use App\Models\GradingLockModel; use App\Models\MissingScoreOverrideModel; -class QuizController extends Controller +class QuizController extends BaseController { protected $db; protected $semesterScoreService; @@ -50,7 +50,7 @@ class QuizController extends Controller $this->missingScoreOverrideModel = new MissingScoreOverrideModel(); } - public function updateQuizScores(array $scores = null, int $updatedBy = null, int $classSectionId = null) + public function updateQuizScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null) { $scores = $this->request->getPost('scores'); log_message('error', '✅ Raw Scores: ' . print_r($scores, true)); @@ -60,7 +60,8 @@ class QuizController extends Controller } $classSectionId = $this->request->getPost('class_section_id') ?? session()->get('class_section_id'); $semester = $this->request->getPost('semester') ?? $this->getTeacherSelectedSemester(); - $schoolYear = $this->request->getPost('school_year') ?? $this->schoolYear; + $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); $classSectionId = (int) ($classSectionId ?? 0); if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) { return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.'); @@ -206,7 +207,7 @@ class QuizController extends Controller // 3) Headers: distinct quiz_index for this class/term $semester = $this->getTeacherSelectedSemester(); - $schoolYear = $this->schoolYear; + $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); $quizHeaderRows = $this->quizModel->select('quiz_index') ->where('class_section_id', $classSectionId) ->where('school_year', $schoolYear) @@ -272,9 +273,9 @@ class QuizController extends Controller 'students' => $students, 'quizHeaders' => $quizHeaders, // e.g., [1,2,3] 'semester' => $semester, - 'schoolYear' => $this->schoolYear, + 'schoolYear' => $schoolYear, 'classSectionId' => $classSectionId, - 'missingOkMap' => $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $this->schoolYear, 'quiz'), + 'missingOkMap' => $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $schoolYear, 'quiz'), ]); } @@ -294,7 +295,8 @@ class QuizController extends Controller try { $semester = $this->getTeacherSelectedSemester(); - $schoolYear = $this->schoolYear; + $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); + $this->assertSchoolYearWritable($this->resolveSchoolYearContext()); $existingQuizNumbers = $this->quizModel ->select('quiz_index') ->where('class_section_id', $classSectionId) @@ -323,6 +325,7 @@ class QuizController extends Controller $students = $this->studentClassModel ->where('class_section_id', $classSectionId) + ->where('school_year', $schoolYear) ->findAll(); if (empty($students)) { @@ -341,8 +344,8 @@ class QuizController extends Controller 'quiz_index' => $nextQuizNumber, 'class_section_id' => $classSectionId, 'updated_by' => $updatedBy, - 'semester' => $this->semester, - 'school_year' => $this->schoolYear, + 'semester' => $semester, + 'school_year' => $schoolYear, ]) ->first(); diff --git a/app/Controllers/View/ScoreCommentController.php b/app/Controllers/View/ScoreCommentController.php index a1390b4..3c8a259 100644 --- a/app/Controllers/View/ScoreCommentController.php +++ b/app/Controllers/View/ScoreCommentController.php @@ -46,9 +46,11 @@ class ScoreCommentController extends BaseController public function saveComments() { + $schoolYearContext = $this->resolveSchoolYearContext(); + $this->assertSchoolYearWritable($schoolYearContext); $comments = $this->request->getPost('comments'); $classSectionId = (int)($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0); - $schoolYear = (string) ($this->request->getPost('school_year') ?? $this->schoolYear); + $schoolYear = $schoolYearContext->yearName(); $rawSelectedSemester = $this->request->getPost('selected_semester'); $normalizedSelected = $this->normalizeSemesterSelection($rawSelectedSemester); if ($normalizedSelected !== '') { diff --git a/app/Controllers/View/ScoreController.php b/app/Controllers/View/ScoreController.php index 13950e2..f4a89d8 100644 --- a/app/Controllers/View/ScoreController.php +++ b/app/Controllers/View/ScoreController.php @@ -2,7 +2,7 @@ namespace App\Controllers\View; -use CodeIgniter\Controller; +use App\Controllers\BaseController; use App\Models\TeacherClassModel; use App\Models\StudentClassModel; use App\Models\StudentModel; @@ -28,7 +28,7 @@ use App\Models\MissingScoreOverrideModel; -class ScoreController extends Controller +class ScoreController extends BaseController { protected $semesterScoreService; protected $db; @@ -88,6 +88,8 @@ class ScoreController extends Controller public function index() { log_message('debug', 'ScoreController::index invoked'); + $schoolYearContext = $this->resolveSchoolYearContext(); + $this->schoolYear = $schoolYearContext->yearName(); $semesterChoiceParam = $this->request->getGet('semester_choice'); $session = session(); if ($this->request->getGet('choose_semester') !== null) { @@ -139,25 +141,13 @@ class ScoreController extends Controller ); if (empty($assignments)) { - // Fallback: any class regardless of term - $fallback = $this->teacherClassModel - ->where('teacher_id', $teacherId) - ->first(); - if ($fallback) { - $assignments = [[ - 'class_section_id' => $fallback['class_section_id'], - 'class_section_name' => null, - 'school_year' => $fallback['school_year'] ?? $effectiveSchoolYear, - 'semester' => $fallback['semester'] ?? $effectiveSemester, - ]]; - $effectiveSemester = $fallback['semester'] ?? $effectiveSemester; - $effectiveSchoolYear = $fallback['school_year'] ?? $effectiveSchoolYear; - } else { - log_message('info', 'No class section assigned to teacher ID: ' . $teacherId); - return redirect() - ->to('no-classes') - ->with('message', 'You do not have an assigned class yet. Please contact the administration.'); - } + log_message( + 'info', + "No {$effectiveSchoolYear} {$effectiveSemester} class section assigned to teacher ID: {$teacherId}" + ); + return redirect() + ->to('no-classes') + ->with('message', 'You do not have an assigned class for the selected school year and semester. Please contact the administration.'); } $allowedIds = array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments); @@ -222,6 +212,7 @@ class ScoreController extends Controller $csRow = $this->db->table('classSection') ->select('class_section_name') ->where('class_section_id', $classSectionId) + ->where('school_year', $effectiveSchoolYear) ->get()->getRowArray(); $classSectionName = $csRow['class_section_name'] ?? ''; log_message('debug', "ScoreController::index teacher {$teacherId} classSection {$classSectionId} semester {$effectiveSemester}"); @@ -281,6 +272,7 @@ class ScoreController extends Controller 'focusTarget' => $focusTarget, 'schoolYear' => $effectiveSchoolYear, 'scoresLocked' => $scoresLocked, + 'isSchoolYearReadonly' => $schoolYearContext->isReadonly(), ]; return view('/teacher/scores', $data); @@ -288,11 +280,13 @@ class ScoreController extends Controller public function submitScoresLock() { + $schoolYearContext = $this->resolveSchoolYearContext(); + $this->assertSchoolYearWritable($schoolYearContext); $classSectionId = (int) ($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0); $semesterRaw = (string) ($this->request->getPost('semester') ?? $this->request->getPost('selected_semester') ?? ''); $semesterNormalized = $this->normalizeSemester($semesterRaw); $semester = $semesterNormalized !== '' ? ucfirst($semesterNormalized) : $this->semester; - $schoolYear = (string) ($this->request->getPost('school_year') ?? $this->schoolYear); + $schoolYear = $schoolYearContext->yearName(); if ($classSectionId <= 0 || $semester === '' || $schoolYear === '') { return redirect()->back()->with('error', 'Missing class section or term.'); @@ -496,6 +490,7 @@ class ScoreController extends Controller $studentClasses = $this->studentClassModel ->active() ->where('student_class.class_section_id', $classSectionId) + ->where('student_class.school_year', $schoolYear) ->findAll(); foreach ($studentClasses as $studentClass) { @@ -1086,6 +1081,9 @@ class ScoreController extends Controller public function updateScores(string $table, ?string $redirectUrl = null) { + $schoolYearContext = $this->resolveSchoolYearContext(); + $this->assertSchoolYearWritable($schoolYearContext); + $this->schoolYear = $schoolYearContext->yearName(); $builder = $this->db->table($table); $scores = $this->request->getPost('final_score'); // name must match input form @@ -1322,18 +1320,7 @@ public function viewStudentScore() return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.'); } - // Handle selected school year (parents can view scores for any available year) - $selectedYear = $this->request->getVar('school_year') ?? $this->schoolYear; - - // Fetch distinct school years only from semester_scores - $query = $this->db->table('semester_scores') - ->select('school_year') - ->distinct() - ->get(); - - $schoolYears = array_column($query->getResultArray(), 'school_year'); - $schoolYears = array_unique($schoolYears); - rsort($schoolYears); + $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); // Initialize scores array $scores = []; @@ -1402,7 +1389,6 @@ $students = $this->db->table('students') return view('/parent/scores', [ 'organizedScores' => $scores, - 'schoolYears' => $schoolYears, 'selectedYear' => $selectedYear, 'semesterOrder' => ['Fall', 'Spring'], 'showExamScores' => $releaseAny, diff --git a/app/Models/ClassProgressReportModel.php b/app/Models/ClassProgressReportModel.php index d5ad154..565a098 100644 --- a/app/Models/ClassProgressReportModel.php +++ b/app/Models/ClassProgressReportModel.php @@ -21,6 +21,7 @@ class ClassProgressReportModel extends Model protected $allowedFields = [ 'class_section_id', 'teacher_id', + 'school_year', 'week_start', 'week_end', 'subject', @@ -50,6 +51,7 @@ class ClassProgressReportModel extends Model protected $validationRules = [ 'class_section_id' => 'required|integer', 'teacher_id' => 'required|integer', + 'school_year' => 'permit_empty|string|max_length[9]', 'week_start' => 'required|valid_date[Y-m-d]', 'week_end' => 'required|valid_date[Y-m-d]', 'subject' => 'required|string|max_length[160]', diff --git a/app/Models/PrintRequestModel.php b/app/Models/PrintRequestModel.php index 3b72cba..f9e91ad 100644 --- a/app/Models/PrintRequestModel.php +++ b/app/Models/PrintRequestModel.php @@ -15,6 +15,7 @@ class PrintRequestModel extends Model 'teacher_id', 'admin_id', 'class_id', + 'school_year', 'file_path', 'page_selection', 'num_copies', diff --git a/app/Models/SubjectCurriculumModel.php b/app/Models/SubjectCurriculumModel.php index d83e308..d974e6b 100644 --- a/app/Models/SubjectCurriculumModel.php +++ b/app/Models/SubjectCurriculumModel.php @@ -24,11 +24,87 @@ class SubjectCurriculumModel extends Model public function getOptionsForClass(int $classId, string $subject): array { - return $this->where('class_id', $classId) - ->where('subject', $subject) + $rows = $this->orderedOptionsBuilder() + ->where('subject_curriculum_items.class_id', $classId) + ->where('subject_curriculum_items.subject', $subject) + ->get() + ->getResultArray(); + + if (! empty($rows)) { + return $rows; + } + + $fallbackClassIds = $this->classIdsWithSameName($classId); + if (empty($fallbackClassIds)) { + return []; + } + + $rows = $this->orderedOptionsBuilder() + ->whereIn('subject_curriculum_items.class_id', $fallbackClassIds) + ->where('subject_curriculum_items.subject', $subject) + ->get() + ->getResultArray(); + + return $this->uniqueCurriculumRows($rows); + } + + private function orderedOptionsBuilder() + { + return $this->db->table($this->table) + ->select('subject_curriculum_items.*') ->orderBy('unit_number', 'ASC') ->orderBy("CAST(SUBSTRING_INDEX(chapter_name, '.', 1) AS UNSIGNED)", 'ASC', false) ->orderBy('chapter_name', 'ASC') - ->findAll(); + ->orderBy('id', 'ASC'); + } + + private function classIdsWithSameName(int $classId): array + { + $class = $this->db->table('classes') + ->select('class_name') + ->where('id', $classId) + ->get() + ->getRowArray(); + + $className = trim((string) ($class['class_name'] ?? '')); + if ($className === '') { + return []; + } + + $rows = $this->db->table('classes') + ->select('id') + ->where('class_name', $className) + ->where('id !=', $classId) + ->get() + ->getResultArray(); + + return array_values(array_filter(array_map( + static fn (array $row): int => (int) ($row['id'] ?? 0), + $rows + ))); + } + + private function uniqueCurriculumRows(array $rows): array + { + $seen = []; + $unique = []; + + foreach ($rows as $row) { + $key = implode('|', [ + (string) ($row['subject'] ?? ''), + (string) ($row['unit_number'] ?? ''), + (string) ($row['unit_title'] ?? ''), + (string) ($row['chapter_name'] ?? ''), + ]); + + if (isset($seen[$key])) { + continue; + } + + $seen[$key] = true; + $unique[] = $row; + } + + return $unique; } } diff --git a/app/Views/administrator/class_assignment.php b/app/Views/administrator/class_assignment.php index b2e0e2d..82ed525 100644 --- a/app/Views/administrator/class_assignment.php +++ b/app/Views/administrator/class_assignment.php @@ -4,30 +4,6 @@