# 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/summary`
  enriched 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/fees
  ```

  Returns `{ student, fees, trips, summary }` where `fees` rows include
  `discount_amount` and a hydrated `discounts[]` array (name, kind,
  amount_applied) and `trips` rows are the user's `trip_payments`
  joined 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/accountant` for
  school_admin / systems_admin / principal / bursar, and
  `/student/fees` + `/fees` for the student role.
- `client/src/components/Nav.tsx` — `accountant` block already pointed at
  `/dashboard/accountant` (§10a). Added "My Fees" entry to the `student`
  block.
- `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

1. **Single `Students /fees` controller mounted at `/api/students`**
   instead of `/api/student-fees` — keeps the URL hierarchy aligned
   with `/api/students` (which already exists for the Students module).
2. **Parent fees page keeps the legacy fallback** so a partial rollout
   doesn't break the existing UX. New endpoint wins when present.
3. **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_payments` paid (§11).
- Burn-down collection reports (§11).
- PDF invoice generation (§11).
- Bank-recon auto-match for trip-receipt rows (§11).
