Org analytics (multi-office rollup)
How cross-office reporting works: pure rollup functions over injected rows (src/lib/analytics/office-rollup.ts), served ADMIN-only through the offices rollup API. Small cohorts suppress their figures so small offices cannot be re-identified — counts stay visible, money does not.
Suppression rule
MIN_COHORT_SIZE = 5. Any cohort (office, region, fee band) with fewer than five matters reports suppressed: true with null figures. Benchmarks exclude suppressed cohorts entirely, so ordering can never leak them. The org total counts suppressed matters but sums only reportable figures, and suppresses itself when reportable matters fall under five.
Attribution
A matter attributes to the office of its primary assignment, falling back to its first assignment. Matters with no scoped assignment land in the unscoped cohort. Region rollup sums only reportable offices through the firm officeRegions map (src/lib/org/office-regions.ts); offices with no region entry are skipped, never implicitly grouped.
Fee bands
bucketFee places each matter fee by its cents value (lower bound inclusive, upper bound exclusive, so a boundary fee lands in the higher band). Defaults: under-10k, 10k-50k, over-50k. Bounds are caller-configurable; matters matching no band are skipped.
API: GET /api/firms/[firmId]/offices/rollup
ADMIN-only (ADMIN/SUPER_ADMIN) and firm-scoped — the caller firm must match firmId. Views via ?view= (default cohorts; unknown views 400):
| View | Returns | |--------------|------------------------------------------------------| | cohorts | Per-office settlement/fee cohorts with suppression | | benchmarks | Reportable offices ranked by average settlement | | regions | Region cohorts via the firm office-region map | | org | Single org-wide KPI total | | fees | Per-office fee-per-matter bands with suppression |
Empty firms return the empty shape for the requested view. Benchmark rank 1 is the highest average settlement; ties share a rank.
Money rules
Settlement cents follow the disbursement rule (outcome amount, else settlementAmount); the fee percent comes from the matter contingency tier, defaulting to 33.33. Insurance-settled amounts stay out by design — the route says so rather than mixing ledgers.