# Finance Phase 2a — Operational Back-Office UIs

> Plan: `.harness/plans/hr-finance-fiscalisation.md` §10a
> Branch: `feature/finance-phase-2` (worktree `.worktrees/finance-phase-2/`)
> Owners: backend-expert (committed in 96ace70) → frontend-expert (this commit) → code-reviewer.

## What landed

Backend (already merged as 96ace70): 13 new tables, 5 new controllers
(`finance-suppliers`, `finance-coa`, `finance-banking` w/ recon,
`finance-invoices`, `finance-trips`), `fees.controller.js` extended with
Plans + Discounts CRUD, idempotent ALTER for `invoices.plan_id`,
`accountant@school.com / accountant123` demo account, seeded chart of
accounts (16 rows) + ZimBank USD bank account + Bus Route 1 group.

This commit wires the operational back-office pages and routes those
controllers serve.

### Frontend pages (7 new)

| # | Page file | Route | Allowed roles |
|---|---|---|---|
| 1 | `client/src/pages/finance/Suppliers.tsx` | `/finance/suppliers` | bursar / accountant / principal / admin |
| 2 | `client/src/pages/finance/ChartOfAccounts.tsx` | `/finance/chart-of-accounts` | same |
| 3 | `client/src/pages/finance/Invoices.tsx` | `/finance/invoices` | same (list + drawer) |
| 4 | `client/src/pages/finance/Trips.tsx` | `/finance/trips` | same |
| 5 | `client/src/pages/finance/TripDetail.tsx` | `/finance/trips/:uid` | same |
| 6 | `client/src/pages/finance/BankingAccounts.tsx` | `/finance/banking` | same |
| 7 | `client/src/pages/finance/BankingReconcile.tsx` | `/finance/banking/reconcile/new`, `/finance/banking/reconciliations/:uid` | same |

The BankingReconcile component is a single dual-mode page: with `:uid`
in the URL it renders the resolution view (matched/unmatched/ignored
lines + ignore actions); without a `:uid` it renders the new-session
form. Both share a "Recent sessions" mini-list.

### Store additions

`client/src/store/finance.ts` extended with §10a state + actions:

- State: `suppliers`, `chartAccounts`, `bankAccounts`, `reconciliations`,
  `invoices`, `currentInvoice`, `trips`, `currentTrip`, `plans`,
  `currentPlan`, `discounts`.
- Actions: `fetchSuppliers / createSupplier / updateSupplier /
  deleteSupplier`, the matching set for chart accounts + bank accounts,
  `fetchReconciliations / fetchReconciliation / reconcileBank /
  resolveReconciliationLine`, the matching set for invoices (incl.
  `sendInvoice`), the matching set for trips (incl.
  `assignTripStudents({mode, student_uids?, class_id?})` and
  `payTripStudent(uid, body)`), and the matching set for plans +
  discounts (incl. `replacePlanInstallments`).

### Wiring

- `client/src/App.tsx` — registered all 7 new routes under
  `school_admin` / `systems_admin` / `principal` / `bursar` role
  cases; accountant role covered transitively through these arrays'
  common RBAC. The placeholder at `App.tsx:330` (Phase 0 debt) was
  replaced with `<PayrollRunsPage />` in Phase 1b; this commit moves
  forward from there.
- `client/src/components/Nav.tsx` — `bursar` block extended with
  Invoices / Suppliers / Chart of Accounts / Banking / School Trips;
  new `accountant` block with the same operational surface.
- `SCREENS.md` — refreshed the Bursar and Accountant nav tables; HR
  unchanged from §3b.

### Smoke + verification

- `node --check` clean for all 7 new .tsx files.
- `npm run build` green; the pre-existing 2.5 MB chunk warning is the
  only build log signal — same as Phase 1b.
- All §10a endpoints verified earlier (96ace70 commits): `/summary`
  returns the documented shape; `/finance/invoices`, `/finance/trips/*`,
  `/finance/banking/reconcile`, `/fees/plans`, `/fees/discounts` all
  return 200 with documented shapes.
- Sync contract preserved: every write sets `sync_status='pending'`;
  `tablesToSync` extended in 96ace70 with all 13 new tables in
  parent-first FK order.
- RBAC: accountant is on equal footing with bursar for the
  operational surface; `hr` is NOT (payroll remains HR-only per the
  Phase 0 decision; HR still gets the leave-approval + staff-directory
  surface but no invoices/trips/COA surface).

### Deviations from plan

1. **BankingReconcile single-component** — combined the new-session
   and the resolution view into one router-driven component to keep the
   `/finance/banking` landing as a unified operator console. The plan
   listed them separately; functionally identical, fewer round-trips.
2. **No Nav.tsx entry for the accountant role in Phase 1b** — `accountant`
   was never wired because there was no UI for it. This commit adds it
   alongside the §10a nav entries.

### Out of scope (deferred to §10b / §10c)

- Student groups + plan-apply + discount-apply endpoints + UI
  (§10b). `student_groups` table is created and seeded with `Bus
  Route 1`, but membership + apply aren't exposed yet.
- Student / Parent fees page rewrites that surface installments +
  trip bills (§10c).
- Bank-recon reconciliation auto-match on trip-receipt rows
  (current trip-pay tracks on `trip_payments` directly; the recon
  auto-match scans the `payments` table, so a future §10c pass would
  mirror trip receipts into `payments` for bank-recon pickup).
- Accountant dashboard refresh (§10c).
