schoolYearModel = new SchoolYearModel(); } public function index() { $schoolYears = $this->schoolYearModel ->orderBy('name', 'DESC') ->findAll(); $activeYear = null; $nextDraftYear = null; $closingYear = null; $archivedCount = 0; foreach ($schoolYears as $year) { $status = (string) ($year['status'] ?? ''); if ($status === SchoolYearStatus::ACTIVE && $activeYear === null) { $activeYear = $year; } if ($status === SchoolYearStatus::DRAFT && $nextDraftYear === null) { $nextDraftYear = $year; } if ($status === SchoolYearStatus::CLOSING && $closingYear === null) { $closingYear = $year; } if ($status === SchoolYearStatus::ARCHIVED) { $archivedCount++; } } return view('school_years/index', [ 'schoolYears' => $schoolYears, 'statuses' => SchoolYearStatus::ALL, 'activeYear' => $activeYear, 'nextDraftYear' => $nextDraftYear, 'closingYear' => $closingYear, 'archivedCount' => $archivedCount, 'latestTransitions' => service('schoolYearManagement')->latestTransitionByYear(), 'yearVerification' => $this->verificationByYear($schoolYears), 'schoolYearNamesById' => array_column($schoolYears, 'name', 'id'), ]); } public function store() { try { service('schoolYearManagement')->createDraft($this->request->getPost(), $this->userId()); return redirect()->to('/administrator/school-years')->with('success', 'Draft school year created.'); } catch (Throwable $e) { return redirect()->back()->withInput()->with('error', $e->getMessage()); } } public function update(int $id) { try { service('schoolYearManagement')->updateMetadata($id, $this->request->getPost(), $this->userId()); return redirect()->to('/administrator/school-years')->with('success', 'School year metadata updated.'); } catch (Throwable $e) { return redirect()->back()->withInput()->with('error', $e->getMessage()); } } public function activate(int $id) { try { if ($this->request->getPost('confirm_activation') !== '1') { return redirect()->to('/administrator/school-years')->with('error', 'Activation confirmation is required.'); } service('schoolYearManagement')->activate($id, $this->userId()); return redirect()->to('/administrator/school-years')->with('success', 'School year activated. Any previous active year is now in closing.'); } catch (Throwable $e) { return redirect()->to('/administrator/school-years')->with('error', $e->getMessage()); } } public function deleteDraft(int $id) { try { service('schoolYearManagement')->deleteDraft($id, $this->userId()); return redirect()->to('/administrator/school-years')->with('success', 'Draft school year deleted.'); } catch (Throwable $e) { return redirect()->to('/administrator/school-years')->with('error', $e->getMessage()); } } public function archive(int $id) { try { service('schoolYearManagement')->archive($id, $this->userId()); return redirect()->to('/administrator/school-years')->with('success', 'School year archived.'); } catch (Throwable $e) { return redirect()->to('/administrator/school-years')->with('error', $e->getMessage()); } } public function reopen(int $id) { try { service('schoolYearManagement')->reopen($id, (string) $this->request->getPost('reason'), $this->userId()); return redirect()->to('/administrator/school-years')->with('success', 'School year reopened.'); } catch (Throwable $e) { return redirect()->to('/administrator/school-years')->with('error', $e->getMessage()); } } private function userId(): ?int { $id = session('user_id') ?? session('id'); return is_numeric($id) ? (int) $id : null; } private function verificationByYear(array $schoolYears): array { $db = db_connect(); $verification = []; $hasStudentClass = $db->tableExists('student_class'); $hasStudentDecisions = $db->tableExists('student_decisions'); $hasPromotionQueue = $db->tableExists('promotion_queue'); $hasInvoices = $db->tableExists('invoices'); $hasClosingBatches = $db->tableExists('school_year_closing_batches'); foreach ($schoolYears as $year) { $id = (int) ($year['id'] ?? 0); $name = (string) ($year['name'] ?? ''); if ($id <= 0 || $name === '') { continue; } $summary = [ 'students' => 0, 'decisions' => 0, 'promoted' => 0, 'queued' => 0, 'carry_forward_balance' => 0.0, 'positive_balance' => 0.0, 'credit_balance' => 0.0, 'closing_status' => '', ]; if ($hasStudentClass && $db->fieldExists('school_year', 'student_class')) { $row = $db->table('student_class') ->select('COUNT(DISTINCT student_id) AS total', false) ->where('school_year', $name) ->get() ->getRowArray(); $summary['students'] = (int) ($row['total'] ?? 0); } if ($hasStudentDecisions && $db->fieldExists('school_year', 'student_decisions')) { $row = $db->table('student_decisions') ->select('COUNT(DISTINCT student_id) AS decisions', false) ->select("COUNT(DISTINCT CASE WHEN LOWER(decision) = 'pass' THEN student_id END) AS promoted", false) ->where('school_year', $name) ->get() ->getRowArray(); $summary['decisions'] = (int) ($row['decisions'] ?? 0); $summary['promoted'] = (int) ($row['promoted'] ?? 0); } if ($hasPromotionQueue && $db->fieldExists('school_year_from', 'promotion_queue')) { $row = $db->table('promotion_queue') ->select('COUNT(DISTINCT student_id) AS total', false) ->where('school_year_from', $name) ->get() ->getRowArray(); $summary['queued'] = (int) ($row['total'] ?? 0); } if ($hasInvoices && $db->fieldExists('school_year', 'invoices')) { $row = $db->table('invoices') ->select('COALESCE(SUM(balance), 0) AS carry_forward_balance') ->select('COALESCE(SUM(CASE WHEN balance > 0 THEN balance ELSE 0 END), 0) AS positive_balance', false) ->select('COALESCE(SUM(CASE WHEN balance < 0 THEN ABS(balance) ELSE 0 END), 0) AS credit_balance', false) ->where('school_year', $name) ->get() ->getRowArray(); $summary['carry_forward_balance'] = round((float) ($row['carry_forward_balance'] ?? 0), 2); $summary['positive_balance'] = round((float) ($row['positive_balance'] ?? 0), 2); $summary['credit_balance'] = round((float) ($row['credit_balance'] ?? 0), 2); } if ($hasClosingBatches) { $batch = $db->table('school_year_closing_batches') ->select('status') ->where('source_school_year_id', $id) ->orderBy('id', 'DESC') ->get(1) ->getRowArray(); $summary['closing_status'] = (string) ($batch['status'] ?? ''); } $verification[$id] = $summary; } return $verification; } }