[T05] Stock Reports

Summary

Tenant-level reporting surface for race-number stock and assignments. Surfaces existing backend data; CSV export per report.

Actor & Context

Actor: tenant admin, finance, organiser preparing for an event. Frequency: monthly+; ad-hoc before each event. Precondition: user has TENANT_ADMIN (or EVENT_MANAGER for event-scoped reports) permission. Entry point: a new tenant-scope Reports row in the Inventory sidebar group (admin-gated, alongside Stock / Return / Stock activity — see C01 § Sidebar Scopes). Event-scoped reports (Assignments per event) are additionally deep-linked from E01.

Main Flow

  1. Operator picks a report from a list (sidebar or tab strip — TBD by design pass).

  2. Report renders with appropriate filters (date range, NumberType, event).

  3. Operator exports to CSV when needed.

Reports in scope (one Task each under US #557):

  • Stock status (Task #788) — counts by state and type. By-state ships now; by-type depends on the groupBy=type backend extension (Task #792).

  • Assignments per event (Task #789) — driven by event scope. Blocked on the new assignments endpoint (Task #793).

  • UNFIT summary (Task #790) — date filtered. Buildable against existing endpoints.

  • Person pick-list (Task #791) — per person_id → numbers ordered by last_used (the WS1b endpoint). Deep-link only (?personId=N) — no in-report person search; reached from T02 or a person record. Buildable against existing endpoints.

CSV export is client-side per report (build the file from the rendered/loaded rows); server-side streaming (AF-2) is deferred until a report’s dataset proves too large.

Alternative Flows

  • AF-1: Report empty — empty state with hint.

  • AF-2: CSV export large dataset — server-side pagination + streaming export (TBD).

Acceptance Criteria

  • Use-case page authored.

  • Status design-todo → handoff-ready after Claude Design pass.

  • Reports surface against existing endpoints.

  • CSV export works for each report.

API Surface

Call Purpose

GET /api/race-numbers/stats?groupBy=state (live) / groupBy=type (Task #792)

Stock status report. By-state is supported today; groupBy=type / state,type is a pending backend extension (Task #792).

GET /api/events/{id}/race-numbers/assignments (Task #793)

Assignments per event. Endpoint does not exist yet — new backend Task #793.

GET /api/race-numbers?state=UNFIT_FOR_SERVICE&…​ (live)

UNFIT summary. Existing list endpoint; confirm date-range criteria support, else a small criteria addition.

GET /api/people/{personId}/race-numbers (live)

Person pick-list (WS1b — PersonRaceNumberResourceEx). NB: the real path is /race-numbers, not the earlier-documented /number-pickList.

Out of Scope

  • Stock browsing/drill-down — T02.

  • Stock return — T03.

  • Bulk actions — T04.

Design Anchors

Design Decisions (2026-06-24)

Resolved with the product owner ahead of the Claude Design pass:

  • Scope — design all four reports (design runs ahead of backend). US #557 split into one Task per report (#788–#791) plus two backend Tasks for the gaps (#792 stats groupBy=type, #793 assignments-per-event endpoint).

  • Placement — new tenant-scope Reports row in the Inventory sidebar group, admin-gated like its siblings (not the existing portfolio-scope Reports placeholder). One screen, four reports.

  • Person pick-list — deep-link only (?personId=N); no in-report person search.

  • CSV — client-side per report; server-side streaming deferred (AF-2).

Design received 2026-06-24 (:design-url: above, T05-stock-reports/Stock Reports.html). Structure to implement: tab strip (Stock status · Assignments per event · UNFIT summary · Person pick-list); Stock status = five KPI tiles + a type×state breakdown table with a totals row; UNFIT + Assignments + Person = filter row + results table reusing the T02 state-pill / table chrome; each report has an Export-CSV button (top-right, badged with live row count) and its own reasoned empty state. Filters live in the report (preserved across tab switches); the URL reflects report + filter for shareability. Open questions parked as display-only (KPI tiles not cross-filtering) and preset date ranges for now.

Notes

Reports surface existing backend data (two reports await the backend Tasks above). Likely the simplest D2b screen once the gaps land.