= $this->extend('layout/management_layout') ?> = $this->section('content') ?> [], 'rows' => []]; $promotionSummary = $promotion['summary'] ?? []; $promotionRows = $promotion['rows'] ?? []; $promotionTotal = count($promotionRows); $blockers = $preview['blockers'] ?? []; $warnings = $preview['warnings'] ?? []; $carryForward = $preview['carry_forward'] ?? []; $inventory = $preview['inventory'] ?? ['rows' => [], 'summary' => []]; $inventoryRows = $inventory['rows'] ?? []; $carryForwardTotal = array_reduce( $carryForward, static fn (float $total, array $row): float => $total + (float) ($row['carry_forward_amount'] ?? 0), 0.0 ); $status = (string) ($source['status'] ?? ''); $batchStatus = (string) ($latestBatch['status'] ?? ''); $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) : '-'; $sourceId = (int) ($source['id'] ?? 0); $targetId = (int) ($target['id'] ?? 0); $flowStep = 1; if ($status === 'closing' && $batchStatus === 'started') { $flowStep = 3; } elseif ($status === 'closing' && $batchStatus === 'executed') { $flowStep = 4; } elseif ($status === 'closed' || $batchStatus === 'completed') { $flowStep = 5; } elseif ($status === 'active') { $flowStep = $blockers === [] && $target ? 2 : 1; } $initialModalStep = $flowStep === 2 ? 1 : $flowStep; $flowSteps = [ 1 => [ 'title' => 'Review checklist', 'description' => 'Confirm promotion decisions, blocking issues, warnings, and financial totals before the year is locked.', ], 2 => [ 'title' => 'Start close', 'description' => 'Lock the current year into end-year review and connect it to the selected next school year.', ], 3 => [ 'title' => 'Carry forward', 'description' => 'Create or update target-year balances from the family balances shown in this checklist.', ], 4 => [ 'title' => 'Finish close', 'description' => 'Mark the current school year closed and activate the next school year.', ], 5 => [ 'title' => 'Closed', 'description' => 'The end-year process is complete. The closed year remains available for reporting and audit review.', ], ]; $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 ''; } 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; }; $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 ''; } $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'; }; ?> getFlashdata('success')): ?>
No blocking issues found.
No warnings found.
| Student | School ID | Class | Age Sep 1 | Year Score | Decision | Source | Next Year Placement | Status |
|---|---|---|---|---|---|---|---|---|
| = esc($row['student_name'] ?? ('Student #' . (int) ($row['student_id'] ?? 0))) ?> | = esc($row['school_id'] ?? '') ?> | = esc($row['class_section_name'] ?? '') ?> | = esc($ageSepOne !== '' ? $ageSepOne : '-') ?> | = esc($score($row['year_score'] ?? null)) ?> | = esc(($row['decision'] ?? '') !== '' ? $row['decision'] : '-') ?> | = esc($sourceLabel) ?> | = esc($targetLabel !== '' ? $targetLabel : '-') ?> | = esc(ucfirst($rowStatus)) ?> |
| Book | Opening | Net Movements | System Closing | Physical Count | Variance | Charge Price | Target Opening |
|---|---|---|---|---|---|---|---|
|
= esc($row['item_name'] ?? '') ?>
= esc(trim((string)($row['isbn'] ?? '') . ' ' . (string)($row['edition'] ?? ''))) ?>
|
= (int) ($row['opening_quantity'] ?? 0) ?> | = (int) ($row['movement_total'] ?? 0) ?> | = (int) ($row['system_closing_quantity'] ?? 0) ?> | = ($row['counted_closing_quantity'] ?? null) === null ? 'Missing' : (int) $row['counted_closing_quantity'] ?> | = ($row['variance_quantity'] ?? null) === null ? '-' : (int) $row['variance_quantity'] ?> | $= number_format(((int) ($row['charge_price_cents'] ?? 0)) / 100, 2) ?> | = (int) ($row['target_opening_quantity'] ?? 0) ?> |
| No book inventory item-year rows found for this source year. | |||||||
| Family | Parent | Source Balance | Credits | Adjustments | Net Carry-Forward |
|---|---|---|---|---|---|
| = esc($row['family']) ?> |
= esc($row['parent'] ?? ('Parent #' . (int) ($row['family_id'] ?? 0))) ?>
= esc($row['parent_email']) ?>
|
= esc($money($row['source_balance'])) ?> | = esc($money($row['credit_amount'])) ?> | = esc($money($row['adjustment_amount'])) ?> | = esc($money($row['carry_forward_amount'])) ?> |