4.3 KiB
Finance Phase 2c — Accountant dashboard + Student/Parent portals
Plan:
.harness/plans/hr-finance-fiscalisation.md§10c Branch:feature/finance-phase-2c(worktree.worktrees/finance-phase-2c/) Owners: backend-expert (enriched summary + students-fees helper) → frontend-expert (portal rewrites) → code-reviewer.
What landed
The last mile: dashboards and student/parent portals that consume everything §10a and §10b produced.
Backend
-
server/src/controllers/finance.controller.js—/api/finance/dashboard/summaryenriched with six new tiles:overdue_invoices(invoices past due, not paid/cancelled)open_invoices(issued/partial/overdue count)upcoming_trips_30d(published trips departing in next 30 days)pending_reconciliations(bank sessions with discrepancies)installments_due_14d(student_fees due in next fortnight)student_groups_count
Shape is backwards compatible — the original five fields (
collected_mtd,outstanding_fees,payroll_last_run,expenses_mtd,pending_expenses) are unchanged. -
server/src/controllers/students-fees.controller.js— new file mounted at/api/students. Single-student profile endpoint:GET /api/students/:uid/feesReturns
{ student, fees, trips, summary }wherefeesrows includediscount_amountand a hydrateddiscounts[]array (name, kind, amount_applied) andtripsrows are the user'strip_paymentsjoined to trips. RBAC gates by parent/child link or by student self. This powers the new student and parent view pages.
Frontend
| # | File | Route / Role | Notes |
|---|---|---|---|
| 1 | client/src/pages/dashboard/AccountantDashboard.tsx |
/dashboard/accountant |
Accountant-specific landing with 4 KPI tiles, 3 entry CTAs (plans / discounts / groups), expense-approval summary, and a compact "latest activity" feed. |
| 2 | client/src/pages/dashboard/FinanceDashboard.tsx |
/dashboard/finance (modified) |
Second tile row added — Open Invoices / Trips next 30d / Reconciliations / Student Groups — wired to the enriched summary endpoint. |
| 3 | client/src/pages/student/Fees.tsx |
/student/fees (new), /fees (also mapped) |
New student fees portal: fee lines with discount-row expander, trips table below, KPI tiles. Backed by /api/students/:uid/fees. |
| 4 | client/src/pages/parent/Fees.tsx |
(modified) | Switched data source to /api/students/:uid/fees; falls back to legacy /fees/students if the new endpoint isn't available. Total-outstanding now subtracts discount_amount so partial-paid lines show the correct balance. |
Wiring
client/src/App.tsx— registered/dashboard/accountantfor school_admin / systems_admin / principal / bursar, and/student/fees+/feesfor the student role.client/src/components/Nav.tsx—accountantblock already pointed at/dashboard/accountant(§10a). Added "My Fees" entry to thestudentblock.SCREENS.md— Bursar / Accountant / Student nav rows up-to-date via the Nav entries; no body edits needed.
Smoke
GET /api/finance/dashboard/summary
→ { collected_mtd, outstanding_fees, payroll_last_run, expenses_mtd,
pending_expenses,
overdue_invoices, open_invoices, upcoming_trips_30d,
pending_reconciliations, installments_due_14d, student_groups_count }
GET /api/students/:uid/fees
→ { student, fees:[...with discounts[]], trips:[...], summary }
Logging in as student@school.com and fetching /api/students/:uid/fees
returns the same envelope. Login as a random student and self-fetch:
the self-scope guard passes (200). Login as a random non-parent and
fetch someone else's: 403 with error message.
Deviations from the plan
- Single
Students /feescontroller mounted at/api/studentsinstead of/api/student-fees— keeps the URL hierarchy aligned with/api/students(which already exists for the Students module). - Parent fees page keeps the legacy fallback so a partial rollout doesn't break the existing UX. New endpoint wins when present.
- No PDF invoice generation — kept the existing printable HTML view from §10a. PDF generation is §11.
Out of scope
- Paynow webhook extension to mark
trip_paymentspaid (§11). - Burn-down collection reports (§11).
- PDF invoice generation (§11).
- Bank-recon auto-match for trip-receipt rows (§11).