diff --git a/app/Controllers/Administrator/SchoolYearClosingController.php b/app/Controllers/Administrator/SchoolYearClosingController.php
index 20baf38..f5660af 100644
--- a/app/Controllers/Administrator/SchoolYearClosingController.php
+++ b/app/Controllers/Administrator/SchoolYearClosingController.php
@@ -14,11 +14,13 @@ class SchoolYearClosingController extends BaseController
$targetId = $this->normalizeInt($this->request->getGet('target_school_year_id'));
$preview = service('schoolYearClosing')->preview($id, $targetId);
$promotionTable = $this->promotionTablePayload($preview['promotion']['rows'] ?? []);
+ $carryForwardTable = $this->carryForwardTablePayload($preview['carry_forward'] ?? []);
$latestBatch = service('schoolYearClosing')->latestBatch($id);
return view('school_years/closing_preview', [
'preview' => $preview,
'promotionTable' => $promotionTable,
+ 'carryForwardTable' => $carryForwardTable,
'latestBatch' => $latestBatch,
'missingCarryForwardInvoices' => $this->missingCarryForwardInvoiceCount($latestBatch),
'schoolYears' => (new SchoolYearModel())->orderBy('name', 'DESC')->findAll(),
@@ -118,13 +120,10 @@ class SchoolYearClosingController extends BaseController
private function promotionTablePayload(array $rows): array
{
$allowedSorts = ['student', 'school_id', 'class', 'year_score', 'decision', 'source', 'queue', 'target', 'status'];
- $sort = (string) ($this->request->getGet('sort') ?? 'class');
- $sort = in_array($sort, $allowedSorts, true) ? $sort : 'class';
- $order = strtolower((string) ($this->request->getGet('order') ?? 'asc')) === 'desc' ? 'desc' : 'asc';
- $page = max(1, (int) ($this->request->getGet('page') ?? 1));
- $perPage = (int) ($this->request->getGet('per_page') ?? 25);
+ $sort = 'class';
+ $order = 'asc';
+ $perPage = 25;
$allowedPerPage = [10, 25, 50, 100];
- $perPage = in_array($perPage, $allowedPerPage, true) ? $perPage : 25;
usort($rows, function (array $a, array $b) use ($sort, $order): int {
$comparison = $this->comparePromotionRows($a, $b, $sort);
@@ -142,21 +141,18 @@ class SchoolYearClosingController extends BaseController
});
$total = count($rows);
- $pageCount = max(1, (int) ceil($total / $perPage));
- $page = min($page, $pageCount);
- $offset = ($page - 1) * $perPage;
return [
- 'rows' => array_slice($rows, $offset, $perPage),
+ 'rows' => $rows,
'sort' => $sort,
'order' => $order,
- 'page' => $page,
+ 'page' => 1,
'perPage' => $perPage,
'total' => $total,
- 'pageCount' => $pageCount,
+ 'pageCount' => 1,
'allowedPerPage' => $allowedPerPage,
- 'from' => $total === 0 ? 0 : $offset + 1,
- 'to' => min($offset + $perPage, $total),
+ 'from' => $total === 0 ? 0 : 1,
+ 'to' => $total,
];
}
@@ -198,6 +194,58 @@ class SchoolYearClosingController extends BaseController
};
}
+ private function carryForwardTablePayload(array $rows): array
+ {
+ $allowedSorts = ['family', 'parent', 'source_balance', 'credits', 'adjustments', 'net'];
+ $sort = (string) ($this->request->getGet('cf_sort') ?? 'family');
+ $sort = in_array($sort, $allowedSorts, true) ? $sort : 'family';
+ $order = strtolower((string) ($this->request->getGet('cf_order') ?? 'asc')) === 'desc' ? 'desc' : 'asc';
+
+ usort($rows, function (array $a, array $b) use ($sort, $order): int {
+ $comparison = $this->compareCarryForwardRows($a, $b, $sort);
+ if ($comparison === 0 && $sort !== 'family') {
+ $comparison = $this->compareCarryForwardRows($a, $b, 'family');
+ }
+ if ($comparison === 0) {
+ $comparison = ((int) ($a['family_id'] ?? 0)) <=> ((int) ($b['family_id'] ?? 0));
+ }
+
+ return $order === 'desc' ? -$comparison : $comparison;
+ });
+
+ return [
+ 'rows' => $rows,
+ 'sort' => $sort,
+ 'order' => $order,
+ ];
+ }
+
+ private function compareCarryForwardRows(array $a, array $b, string $sort): int
+ {
+ $numericSorts = ['source_balance', 'credits', 'adjustments', 'net'];
+ $aValue = $this->carryForwardSortValue($a, $sort);
+ $bValue = $this->carryForwardSortValue($b, $sort);
+
+ if (in_array($sort, $numericSorts, true)) {
+ return (float) $aValue <=> (float) $bValue;
+ }
+
+ return strnatcasecmp((string) $aValue, (string) $bValue);
+ }
+
+ private function carryForwardSortValue(array $row, string $sort): mixed
+ {
+ return match ($sort) {
+ 'family' => (string) ($row['family'] ?? ''),
+ 'parent' => trim((string) ($row['parent'] ?? '') . ' ' . (string) ($row['parent_email'] ?? '')),
+ 'source_balance' => (float) ($row['source_balance'] ?? 0),
+ 'credits' => (float) ($row['credit_amount'] ?? 0),
+ 'adjustments' => (float) ($row['adjustment_amount'] ?? 0),
+ 'net' => (float) ($row['carry_forward_amount'] ?? 0),
+ default => '',
+ };
+ }
+
private function userId(): ?int
{
$id = session('user_id') ?? session('id');
diff --git a/app/Controllers/View/GradingController.php b/app/Controllers/View/GradingController.php
index fc0bd35..ffa8860 100644
--- a/app/Controllers/View/GradingController.php
+++ b/app/Controllers/View/GradingController.php
@@ -31,6 +31,7 @@ use App\Models\GradingLockModel;
use App\Models\BelowSixtyDecisionModel;
use App\Models\StudentDecisionModel;
use App\Services\NavbarService;
+use App\Support\Enrollment\DeliberationDecision;
//use App\Models\ScoreModel;
@@ -3399,13 +3400,16 @@ public function allDecisions()
's.firstname',
's.lastname',
's.gender',
+ 's.dob',
'ss.class_section_id',
'cs.class_section_name',
+ 'c.class_name',
'LOWER(TRIM(ss.semester)) AS sem_key',
'ss.semester_score',
])
->join('students s', 's.id = ss.student_id', 'inner')
->join('classSection cs', 'cs.class_section_id = ss.class_section_id', 'left')
+ ->join('classes c', 'c.id = cs.class_id', 'left')
->where('s.is_active', 1)
->where('ss.school_year', $schoolYear)
->whereIn('LOWER(TRIM(ss.semester))', ['fall', 'spring'])
@@ -3432,7 +3436,9 @@ public function allDecisions()
'firstname' => $sr['firstname'] ?? '',
'lastname' => $sr['lastname'] ?? '',
'gender' => $sr['gender'] ?? '',
+ 'dob' => $sr['dob'] ?? '',
'class_section_id' => (int)($sr['class_section_id'] ?? 0),
+ 'class_name' => $sr['class_name'] ?? '',
'class_section_name' => $sr['class_section_name'] ?? '',
'fall_score' => null,
'spring_score' => null,
@@ -3513,18 +3519,30 @@ public function allDecisions()
$notes = '';
}
+ $currentClassSectionName = trim((string)($info['class_section_name'] ?? ''));
+ if ($currentClassSectionName === '' && isset($savedMap[$sid])) {
+ $currentClassSectionName = trim((string)($savedMap[$sid]['class_section_name'] ?? ''));
+ }
+ $currentClassName = trim((string)($info['class_name'] ?? ''));
+ if ($currentClassName === '') {
+ $currentClassName = $this->classOnlyLabel($currentClassSectionName);
+ }
+
$rows[] = [
'student_id' => $sid,
'school_id' => $info['school_id'],
'firstname' => $info['firstname'],
'lastname' => $info['lastname'],
'gender' => $info['gender'] ?? '',
+ 'dob' => $info['dob'] ?? '',
'class_section_id' => (int)($info['class_section_id'] ?? 0),
- 'class_section_name' => $info['class_section_name'],
+ 'class_name' => $currentClassName,
+ 'class_section_name' => $currentClassSectionName,
'fall_score' => $fall,
'spring_score' => $spring,
'year_score' => $yearScore,
'decision' => $decision,
+ 'next_year_placement' => $this->nextYearPlacementLabel($decision, $currentClassName, $currentClassSectionName, (string)($info['dob'] ?? ''), $schoolYear),
'source' => $source,
'notes' => $notes,
'saved' => isset($savedMap[$sid]),
@@ -3741,6 +3759,76 @@ public function generateAllDecisions()
->with('status', "Decisions generated for {$savedCount} students.");
}
+private function nextYearPlacementLabel(
+ ?string $decision,
+ string $currentClassName,
+ string $currentClassSectionName,
+ string $dob,
+ string $schoolYear
+): string
+{
+ $normalizedDecision = DeliberationDecision::normalize($decision);
+ $classLabel = $this->classOnlyLabel($currentClassName !== '' ? $currentClassName : $currentClassSectionName);
+
+ if ($this->isKgClass($classLabel)) {
+ $kgPlacement = $this->kgPlacementByComingSeptember($dob, $schoolYear);
+ if ($kgPlacement !== '') {
+ return $kgPlacement;
+ }
+ }
+
+ if ($normalizedDecision === DeliberationDecision::REPEAT_CLASS) {
+ return $classLabel;
+ }
+
+ return '';
+}
+
+private function classOnlyLabel(string $className): string
+{
+ return trim((string) preg_replace('/-.+$/', '', $className));
+}
+
+private function isKgClass(string $className): bool
+{
+ $value = strtoupper(trim($className));
+
+ return preg_match('/(^|[^A-Z0-9])KG([^A-Z0-9]|$)/', $value) === 1 || str_contains($value, 'KINDERGARTEN');
+}
+
+private function kgPlacementByComingSeptember(string $dob, string $schoolYear): string
+{
+ $dob = trim($dob);
+ if ($dob === '') {
+ return '';
+ }
+
+ try {
+ $birthDate = new \DateTimeImmutable($dob);
+ $cutoff = $this->comingSeptemberFirstCutoff($schoolYear);
+ } catch (\Throwable) {
+ return '';
+ }
+
+ if ($birthDate > $cutoff) {
+ return '';
+ }
+
+ return $birthDate->diff($cutoff)->y >= 6 ? '1' : 'KG';
+}
+
+private function comingSeptemberFirstCutoff(string $schoolYear): \DateTimeImmutable
+{
+ if (preg_match('/^(\d{4})-\d{4}$/', $schoolYear, $matches) === 1) {
+ return new \DateTimeImmutable(((int) $matches[1] + 1) . '-09-01');
+ }
+
+ $today = new \DateTimeImmutable('today');
+ $cutoff = new \DateTimeImmutable($today->format('Y') . '-09-01');
+
+ return $today <= $cutoff ? $cutoff : $cutoff->modify('+1 year');
+}
+
private function calculateTrophyThreshold(array $scores, float $percentile = 75.0): array
{
$scores = array_values(array_filter(
diff --git a/app/Services/SchoolYearClosingService.php b/app/Services/SchoolYearClosingService.php
index 0b4db28..86ead1e 100644
--- a/app/Services/SchoolYearClosingService.php
+++ b/app/Services/SchoolYearClosingService.php
@@ -595,9 +595,10 @@ final class SchoolYearClosingService
$builder = $this->db->table('student_class sc')
->select('sc.student_id, sc.class_section_id, sc.created_at, sc.updated_at')
->select('s.school_id, s.firstname, s.lastname')
- ->select('cs.class_section_name')
+ ->select('cs.class_section_name, cs.class_id, c.class_name')
->join('students s', 's.id = sc.student_id', 'inner')
->join('classSection cs', 'cs.class_section_id = sc.class_section_id', 'left')
+ ->join('classes c', 'c.id = cs.class_id', 'left')
->where('sc.school_year', $schoolYear)
->where('sc.class_section_id IS NOT NULL', null, false);
@@ -639,6 +640,8 @@ final class SchoolYearClosingService
'school_id' => (string) ($row['school_id'] ?? ''),
'student_name' => trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? '')),
'class_section_id' => (int) ($row['class_section_id'] ?? 0),
+ 'class_id' => (int) ($row['class_id'] ?? 0),
+ 'class_name' => (string) ($row['class_name'] ?? ''),
'class_section_name' => (string) ($row['class_section_name'] ?? ''),
'dob' => (string) ($row['dob'] ?? ''),
'registration_grade' => (string) ($row['registration_grade'] ?? ''),
@@ -703,6 +706,14 @@ final class SchoolYearClosingService
$student['target_section'] = $queue['target_section'];
}
+ if ($this->isKgStudent($student)) {
+ $kgPlacement = $this->kgPlacementLabelByTargetYearStartCutoff((string) ($student['dob'] ?? ''), $targetSchoolYear);
+ if ($kgPlacement !== '') {
+ $student['target_section'] = '';
+ $student['target_class'] = $kgPlacement;
+ }
+ }
+
$summary['total_students']++;
if ($student['status'] === 'missing') {
$summary['missing_decision']++;
@@ -712,10 +723,22 @@ final class SchoolYearClosingService
$summary['with_decision']++;
if (($student['normalized_decision'] ?? null) === DeliberationDecision::PASSED) {
$summary['pass']++;
+ if ($student['target_class'] === '') {
+ $student['target_class'] = $this->nextClassLabelForPromotionPreview(
+ (string) ($student['class_name'] ?: $student['class_section_name']),
+ $targetSchoolYear
+ );
+ }
if ($queue === null && $student['auto_kg_pass'] !== true) {
$summary['missing_queue']++;
}
} else {
+ if (($student['normalized_decision'] ?? null) === DeliberationDecision::REPEAT_CLASS) {
+ $student['target_section'] = '';
+ $student['target_class'] = $student['target_class'] !== ''
+ ? $student['target_class']
+ : $this->classOnlyLabel((string) ($student['class_name'] ?: $student['class_section_name']));
+ }
$summary['other_decision']++;
}
}
@@ -740,9 +763,9 @@ final class SchoolYearClosingService
private function isKgStudent(array $student): bool
{
- foreach (['class_section_name', 'registration_grade'] as $field) {
+ foreach (['class_name', 'class_section_name'] as $field) {
$value = strtoupper(trim((string) ($student[$field] ?? '')));
- if ($value === 'KG' || str_starts_with($value, 'KG-') || str_contains($value, 'KINDERGARTEN')) {
+ if (preg_match('/(^|[^A-Z0-9])KG([^A-Z0-9]|$)/', $value) === 1 || str_contains($value, 'KINDERGARTEN')) {
return true;
}
}
@@ -753,13 +776,13 @@ final class SchoolYearClosingService
private function kgAgeStatusByTargetYearStartCutoff(string $dob, ?string $targetSchoolYear): string
{
$dob = trim($dob);
- if ($dob === '' || $targetSchoolYear === null || ! preg_match('/^(\d{4})-\d{4}$/', $targetSchoolYear, $matches)) {
+ if ($dob === '') {
return '';
}
try {
$birthDate = new \DateTimeImmutable($dob);
- $cutoff = new \DateTimeImmutable($matches[1] . '-09-01');
+ $cutoff = $this->septemberFirstCutoff($targetSchoolYear);
} catch (\Throwable) {
return '';
}
@@ -776,6 +799,27 @@ final class SchoolYearClosingService
return 'keep_kg';
}
+ private function kgPlacementLabelByTargetYearStartCutoff(string $dob, ?string $targetSchoolYear): string
+ {
+ return match ($this->kgAgeStatusByTargetYearStartCutoff($dob, $targetSchoolYear)) {
+ 'pass' => '1',
+ 'keep_kg' => 'KG',
+ default => '',
+ };
+ }
+
+ private function septemberFirstCutoff(?string $targetSchoolYear): \DateTimeImmutable
+ {
+ if ($targetSchoolYear !== null && preg_match('/^(\d{4})-\d{4}$/', $targetSchoolYear, $matches) === 1) {
+ return new \DateTimeImmutable($matches[1] . '-09-01');
+ }
+
+ $today = new \DateTimeImmutable('today');
+ $cutoff = new \DateTimeImmutable($today->format('Y') . '-09-01');
+
+ return $today <= $cutoff ? $cutoff : $cutoff->modify('+1 year');
+ }
+
private function promotionDecisionRows(array $studentIds, string $schoolYear): array
{
if (! $this->db->tableExists('student_decisions')) {
@@ -880,6 +924,83 @@ final class SchoolYearClosingService
return $queueRows;
}
+ private function nextClassLabelForPromotionPreview(string $sourceClassName, ?string $targetSchoolYear): string
+ {
+ $base = $this->classBaseName($sourceClassName);
+ $target = match (true) {
+ $base === 'KG' || str_contains($base, 'KINDERGARTEN') => '1',
+ ctype_digit($base) => (string) ((int) $base + 1),
+ $base === 'YOUTH' => 'YOUTH',
+ default => '',
+ };
+
+ if ($target === '') {
+ return '';
+ }
+
+ if ($targetSchoolYear !== null && $targetSchoolYear !== '' && $this->db->tableExists('classes')) {
+ $targetClass = $this->classByNameForPromotionPreview($target, $targetSchoolYear);
+ if ($targetClass !== null) {
+ return (string) ($targetClass['class_name'] ?? $target);
+ }
+
+ if (ctype_digit($base) && (int) $base >= 9) {
+ $youthClass = $this->classByNameForPromotionPreview('YOUTH', $targetSchoolYear);
+ if ($youthClass !== null) {
+ return (string) ($youthClass['class_name'] ?? 'YOUTH');
+ }
+ }
+ }
+
+ return $target;
+ }
+
+ private function classOnlyLabel(string $className): string
+ {
+ return trim((string) preg_replace('/-.+$/', '', $className));
+ }
+
+ private function classBaseName(string $className): string
+ {
+ $base = strtoupper(trim((string) preg_replace('/-.+$/', '', $className)));
+ $base = preg_replace('/\b(CLASS|GRADE)\b/i', '', $base) ?? $base;
+ $base = trim(preg_replace('/\s+/', ' ', $base) ?? $base);
+
+ if (str_contains($base, 'KINDERGARTEN')) {
+ return 'KG';
+ }
+
+ if (preg_match('/(^|[^A-Z0-9])KG([^A-Z0-9]|$)/', $base) === 1) {
+ return 'KG';
+ }
+
+ if (preg_match('/\d+/', $base, $matches) === 1) {
+ return (string) (int) $matches[0];
+ }
+
+ return $base;
+ }
+
+ private function classByNameForPromotionPreview(string $className, string $schoolYear): ?array
+ {
+ $builder = $this->db->table('classes')->where('UPPER(class_name)', strtoupper($className));
+ if ($this->db->fieldExists('school_year', 'classes')) {
+ $builder->where('school_year', $schoolYear);
+ }
+
+ $row = $builder->orderBy('id', 'DESC')->limit(1)->get()->getRowArray();
+ if ($row !== null || ! $this->db->fieldExists('school_year', 'classes')) {
+ return $row ?: null;
+ }
+
+ return $this->db->table('classes')
+ ->where('UPPER(class_name)', strtoupper($className))
+ ->orderBy('id', 'DESC')
+ ->limit(1)
+ ->get()
+ ->getRowArray() ?: null;
+ }
+
private function countBySchoolYear(string $table, string $schoolYear, string $distinctField): int
{
if (! $this->db->tableExists($table) || ! $this->db->fieldExists('school_year', $table)) {
diff --git a/app/Views/grading/all_decisions.php b/app/Views/grading/all_decisions.php
index 7520ccc..a9051e8 100644
--- a/app/Views/grading/all_decisions.php
+++ b/app/Views/grading/all_decisions.php
@@ -214,6 +214,7 @@
Spring Score |
Year Score |
Decision |
+ Next year placement |
Source |
Notes |
@@ -251,6 +252,7 @@
—
+ = esc((string)($row['next_year_placement'] ?? '') ?: '—') ?> |
= esc($srcLabel) ?>
|
@@ -312,7 +314,7 @@
order: [[1, 'asc'], [0, 'asc']],
pageLength: 100,
lengthMenu: [25, 50, 100, 200],
- columnDefs: [{ orderable: false, targets: [7] }]
+ columnDefs: [{ orderable: false, targets: [8] }]
});
} catch (_) {}
});
diff --git a/app/Views/school_years/closing_preview.php b/app/Views/school_years/closing_preview.php
index c77b7ec..b2b4346 100644
--- a/app/Views/school_years/closing_preview.php
+++ b/app/Views/school_years/closing_preview.php
@@ -8,17 +8,8 @@
$finance = $preview['finance'] ?? [];
$promotion = $preview['promotion'] ?? ['summary' => [], 'rows' => []];
$promotionSummary = $promotion['summary'] ?? [];
- $promotionTable = $promotionTable ?? [];
- $promotionRows = $promotionTable['rows'] ?? ($promotion['rows'] ?? []);
- $promotionSort = (string) ($promotionTable['sort'] ?? 'class');
- $promotionOrder = (string) ($promotionTable['order'] ?? 'asc');
- $promotionPage = (int) ($promotionTable['page'] ?? 1);
- $promotionPerPage = (int) ($promotionTable['perPage'] ?? 25);
- $promotionTotal = (int) ($promotionTable['total'] ?? count($promotionRows));
- $promotionPageCount = (int) ($promotionTable['pageCount'] ?? 1);
- $promotionFrom = (int) ($promotionTable['from'] ?? ($promotionTotal === 0 ? 0 : 1));
- $promotionTo = (int) ($promotionTable['to'] ?? count($promotionRows));
- $promotionPerPageOptions = $promotionTable['allowedPerPage'] ?? [10, 25, 50, 100];
+ $promotionRows = $promotion['rows'] ?? [];
+ $promotionTotal = count($promotionRows);
$blockers = $preview['blockers'] ?? [];
$warnings = $preview['warnings'] ?? [];
$carryForward = $preview['carry_forward'] ?? [];
@@ -27,27 +18,61 @@
$missingCarryForwardInvoices = (int) ($missingCarryForwardInvoices ?? 0);
$money = static fn ($value): string => '$' . number_format((float) $value, 2);
$score = static fn ($value): string => is_numeric($value) ? number_format((float) $value, 2) : '-';
- $promotionUrl = static function (array $overrides = []) use ($source, $target, $promotionSort, $promotionOrder, $promotionPage, $promotionPerPage): string {
- $query = array_merge([
- 'target_school_year_id' => $target['id'] ?? null,
- 'sort' => $promotionSort,
- 'order' => $promotionOrder,
- 'page' => $promotionPage,
- 'per_page' => $promotionPerPage,
- ], $overrides);
- $query = array_filter($query, static fn ($value): bool => $value !== null && $value !== '');
+ $ageBySchoolYearSecondSeptember = static function (array $row) use ($source): string {
+ $dob = trim((string) ($row['dob'] ?? ''));
+ $schoolYear = (string) ($source['name'] ?? '');
+ if ($dob === '' || preg_match('/^\d{4}-(\d{4})$/', $schoolYear, $matches) !== 1) {
+ return '';
+ }
- return site_url('administrator/school-years/' . (int) ($source['id'] ?? 0) . '/closing/preview?' . http_build_query($query));
+ try {
+ $birthDate = new DateTimeImmutable($dob);
+ $cutoff = new DateTimeImmutable($matches[1] . '-09-01');
+ } catch (Throwable) {
+ return '';
+ }
+
+ if ($birthDate > $cutoff) {
+ return '';
+ }
+
+ return (string) $birthDate->diff($cutoff)->y;
};
- $sortLink = static function (string $key, string $label, string $class = '') use ($promotionSort, $promotionOrder, $promotionUrl): string {
- $nextOrder = $promotionSort === $key && $promotionOrder === 'asc' ? 'desc' : 'asc';
- $indicator = $promotionSort === $key ? ($promotionOrder === 'asc' ? ' (asc)' : ' (desc)') : '';
+ $kgPlacementFallback = static function (array $row) use ($target): string {
+ $classText = strtoupper(trim(
+ (string) ($row['class_name'] ?? '') . ' '
+ . (string) ($row['class_section_name'] ?? '')
+ ));
+ if (preg_match('/(^|[^A-Z0-9])KG([^A-Z0-9]|$)/', $classText) !== 1 && ! str_contains($classText, 'KINDERGARTEN')) {
+ return '';
+ }
- return '' . esc($label . $indicator) . '';
+ $dob = trim((string) ($row['dob'] ?? ''));
+ if ($dob === '') {
+ return '';
+ }
+
+ try {
+ $targetName = (string) ($target['name'] ?? '');
+ if (preg_match('/^(\d{4})-\d{4}$/', $targetName, $matches) === 1) {
+ $cutoff = new DateTimeImmutable($matches[1] . '-09-01');
+ } else {
+ $today = new DateTimeImmutable('today');
+ $cutoff = new DateTimeImmutable($today->format('Y') . '-09-01');
+ if ($today > $cutoff) {
+ $cutoff = $cutoff->modify('+1 year');
+ }
+ }
+ $birthDate = new DateTimeImmutable($dob);
+ } catch (Throwable) {
+ return '';
+ }
+
+ if ($birthDate > $cutoff) {
+ return '';
+ }
+
+ return $birthDate->diff($cutoff)->y >= 6 ? '1' : 'KG';
};
?>
@@ -188,7 +213,7 @@
-