diff --git a/app/Controllers/View/StudentController.php b/app/Controllers/View/StudentController.php index f9dfe52..ea8b025 100644 --- a/app/Controllers/View/StudentController.php +++ b/app/Controllers/View/StudentController.php @@ -919,7 +919,7 @@ class StudentController extends BaseController } // Fetch lettered sections for this class - $letters = $this->classSectionModel->getLetterSectionsByClassId($classId); + $letters = $this->letterSectionsForDistribution($classId, $year); if (empty($letters)) { $msg = 'No lettered sections found for the selected class.'; return $isAjax ? $json(['ok' => false, 'message' => $msg], 400) : redirect()->back()->with('error', $msg); @@ -1061,6 +1061,24 @@ class StudentController extends BaseController return $out; } + private function letterSectionsForDistribution(int $classId, string $year): array + { + $query = $this->classSectionModel + ->where('class_id', $classId) + ->like('class_section_name', '-', 'both') + ->orderBy('class_section_name', 'ASC'); + if ($year !== '' && $this->db->fieldExists('school_year', 'classSection')) { + $query->where('school_year', $year); + } + + $sections = $query->findAll(); + if (!empty($sections) || $year === '' || ! $this->db->fieldExists('school_year', 'classSection')) { + return $sections; + } + + return $this->classSectionModel->getLetterSectionsByClassId($classId); + } + private function decisionDistributionCandidates(int $classId, string $targetSchoolYear): array { $previousSchoolYear = $this->previousSchoolYearName($targetSchoolYear); @@ -1315,12 +1333,22 @@ class StudentController extends BaseController { try { $year = trim((string)($this->request->getGet('school_year') ?? $this->schoolYear)); + $includeClassIds = $this->parseIncludedClassIds($this->request->getGet('include_class_ids')); // Fetch base sections (no dash) and filter to KG, 1..9, youth - $bases = $this->classSectionModel + $baseQuery = $this->classSectionModel ->where("class_section_name NOT LIKE '%-%'", null, false) - ->orderBy('class_id', 'ASC') - ->findAll(); + ->orderBy('class_id', 'ASC'); + if ($year !== '' && $this->db->fieldExists('school_year', 'classSection')) { + $baseQuery->where('school_year', $year); + } + $bases = $baseQuery->findAll(); + if (empty($bases) && $year !== '' && $this->db->fieldExists('school_year', 'classSection')) { + $bases = $this->classSectionModel + ->where("class_section_name NOT LIKE '%-%'", null, false) + ->orderBy('class_id', 'ASC') + ->findAll(); + } $wanted = []; foreach ($bases as $r) { @@ -1337,10 +1365,27 @@ class StudentController extends BaseController $num = (int)$name; if ($num >= 1 && $num <= 9) { $wanted[] = $r; + continue; } } + + if (in_array((int)($r['class_id'] ?? 0), $includeClassIds, true)) { + $wanted[] = $r; + } } + $deduped = []; + $seenClassIds = []; + foreach ($wanted as $r) { + $classId = (int)($r['class_id'] ?? 0); + if ($classId <= 0 || isset($seenClassIds[$classId])) { + continue; + } + $seenClassIds[$classId] = true; + $deduped[] = $r; + } + $wanted = $deduped; + $out = []; foreach ($wanted as $r) { $classId = (int)$r['class_id']; @@ -1371,6 +1416,22 @@ class StudentController extends BaseController } } + private function parseIncludedClassIds($raw): array + { + if ($raw === null || $raw === '') { + return []; + } + + if (!is_array($raw)) { + $raw = explode(',', (string)$raw); + } + + return array_values(array_unique(array_filter( + array_map('intval', $raw), + static fn(int $id): bool => $id > 0 + ))); + } + private function savedDistributionSections(int $classId, string $year): array { if (! $this->db->tableExists('student_section_distribution_drafts')) { diff --git a/app/Database/Migrations/2026-07-31-000100_BackfillAddedSchoolYearColumns.php b/app/Database/Migrations/2026-07-31-000100_BackfillAddedSchoolYearColumns.php new file mode 100644 index 0000000..447e10f --- /dev/null +++ b/app/Database/Migrations/2026-07-31-000100_BackfillAddedSchoolYearColumns.php @@ -0,0 +1,88 @@ +configuredSchoolYear(); + + foreach ($this->tablesWithSchoolYearColumn() as $table) { + $this->db->query( + sprintf( + 'UPDATE %s SET `school_year` = ? WHERE `school_year` IS NULL OR TRIM(`school_year`) = \'\'', + $this->quoteIdentifier($table) + ), + [$schoolYear] + ); + } + } + + public function down(): void + { + // This is a data backfill. Do not erase school_year values on rollback. + } + + private function configuredSchoolYear(): string + { + if (! $this->db->tableExists('configuration')) { + throw new RuntimeException('Cannot backfill school_year columns: configuration table is missing.'); + } + + $row = $this->db->table('configuration') + ->select('config_value') + ->where('config_key', 'school_year') + ->where('config_value IS NOT NULL', null, false) + ->where('TRIM(config_value) <>', '') + ->orderBy('id', 'DESC') + ->get(1) + ->getRowArray(); + + $schoolYear = trim((string)($row['config_value'] ?? '')); + if (preg_match('/^\d{4}-\d{4}$/', $schoolYear) !== 1) { + throw new RuntimeException( + 'Cannot backfill school_year columns: configuration.school_year must be set as YYYY-YYYY.' + ); + } + + return $schoolYear; + } + + /** + * @return list + */ + private function tablesWithSchoolYearColumn(): array + { + $rows = $this->db->query( + "SELECT c.TABLE_NAME AS table_name + FROM information_schema.COLUMNS c + INNER JOIN information_schema.TABLES t + ON t.TABLE_SCHEMA = c.TABLE_SCHEMA + AND t.TABLE_NAME = c.TABLE_NAME + WHERE c.TABLE_SCHEMA = DATABASE() + AND c.COLUMN_NAME = 'school_year' + AND t.TABLE_TYPE = 'BASE TABLE' + ORDER BY c.TABLE_NAME" + )->getResultArray(); + + $tables = []; + foreach ($rows as $row) { + $table = trim((string)($row['table_name'] ?? '')); + if ($table === '' || $table === 'school_years') { + continue; + } + $tables[] = $table; + } + + return $tables; + } + + private function quoteIdentifier(string $identifier): string + { + return '`' . str_replace('`', '``', $identifier) . '`'; + } +} diff --git a/app/Views/administrator/sections_auto_distribute.php b/app/Views/administrator/sections_auto_distribute.php index 39f0f25..924f7da 100644 --- a/app/Views/administrator/sections_auto_distribute.php +++ b/app/Views/administrator/sections_auto_distribute.php @@ -1,4 +1,69 @@ extend('layout/management_layout') ?> + +section('styles') ?> + +endSection() ?> + section('content') ?>
@@ -28,6 +93,32 @@
+
+
+ + +
+
+ +
+
+
+
+
@@ -37,6 +128,7 @@ + @@ -57,50 +149,37 @@
Total Sections ActionsDistribution