feat(reports): Purchasing spend analytics (Cost Centres + Accounting Codes) #117

Merged
shad0w merged 2 commits from feat/reports into master 2026-06-24 06:03:29 +00:00
Owner

Implements the wiki Reports Mockup as a Reports → Purchasing sidebar section, wired to real approved-PO spend.

What's included

Two report families, each an index → drill/detail pair:

  • Cost Centres (/reports/cost-centres) — approved spend compared across vessels (the PO cost centre). A row opens /reports/cost-centres/[id]: KPIs, spend trend, and a Top accounting codes breakdown re-pivotable by tier (Heading / Sub-heading / Leaf) and Top-N.
  • Accounting Codes (/reports/accounting-codes) — drills the Account tree (headings → sub-headings → leaves) via ?parent=. A leaf opens /reports/accounting-codes/[id]: trend + breakdown by cost centre (or, for a non-leaf, by sub-account).

Shared filter toolbar (Granularity Monthly/Yearly · Financial Year · Show Top 5/Top 10/Bottom 5/All) — values live in the URL query so the server component re-renders (no client fetching; views are shareable). KPI tiles, recharts comparison/trend/breakdown charts, per-row trend sparklines, and CSV export (/api/reports/spend).

How it works

  • lib/reports.ts — the pure, unit-tested aggregation core. Spend = a PO once it reaches POST_APPROVAL_STATUSES, dated by approvedAt, valued at totalAmount (the dashboard's basis). Indian Apr–Mar FY; each PO's leaf accountId rolled up to parents. One query in getReportDataset(); everything else is pure functions.
  • Sidebar — new collapsible Reports section with a Purchasing subheading (subgroup support added to the Section model). Gated by view_analytics (Manager / SuperUser / Auditor / Admin); export by the same.

Scope / deferred

Documented in the mockup: synthetic Weekly granularity, the "Add to graph" custom multi-select comparison, and line-item-level account allocation (v1 attributes a PO's whole amount to its PO-level accounting code). Sites are not cost centres — only vessels.

Verification

  • 11 unit tests for the aggregation core + 3 for the sidebar Reports section → pnpm test 303 passed; tsc --noEmit clean.
  • Smoke-tested every route end-to-end against seed data with a real session: index / drill / detail / CSV export all 200; a non-view_analytics role gets 307 → /dashboard (page) and 403 (export).

Wiki

  • Reports Mockup — marked implemented (shipped vs deferred).
  • Pages and Navigation — lists the new /reports/* routes + the Reports section.

🤖 Generated with Claude Code

Implements the wiki **Reports Mockup** as a **Reports → Purchasing** sidebar section, wired to real approved-PO spend. ## What's included Two report families, each an **index → drill/detail** pair: - **Cost Centres** (`/reports/cost-centres`) — approved spend compared across **vessels** (the PO cost centre). A row opens **`/reports/cost-centres/[id]`**: KPIs, spend trend, and a **Top accounting codes** breakdown re-pivotable by tier (Heading / Sub-heading / Leaf) and Top-N. - **Accounting Codes** (`/reports/accounting-codes`) — drills the `Account` tree (headings → sub-headings → leaves) via `?parent=`. A leaf opens **`/reports/accounting-codes/[id]`**: trend + breakdown **by cost centre** (or, for a non-leaf, by sub-account). **Shared filter toolbar** (Granularity Monthly/Yearly · Financial Year · Show Top 5/Top 10/Bottom 5/All) — values live in the **URL query** so the server component re-renders (no client fetching; views are shareable). KPI tiles, **recharts** comparison/trend/breakdown charts, per-row trend sparklines, and **CSV export** (`/api/reports/spend`). ## How it works - **`lib/reports.ts`** — the pure, unit-tested aggregation core. **Spend** = a PO once it reaches `POST_APPROVAL_STATUSES`, dated by `approvedAt`, valued at `totalAmount` (the dashboard's basis). Indian **Apr–Mar** FY; each PO's leaf `accountId` rolled up to parents. One query in `getReportDataset()`; everything else is pure functions. - **Sidebar** — new collapsible **Reports** section with a **Purchasing** subheading (subgroup support added to the `Section` model). Gated by **`view_analytics`** (Manager / SuperUser / Auditor / Admin); export by the same. ## Scope / deferred Documented in the mockup: synthetic **Weekly** granularity, the **"Add to graph"** custom multi-select comparison, and **line-item-level** account allocation (v1 attributes a PO's whole amount to its PO-level accounting code). Sites are **not** cost centres — only vessels. ## Verification - 11 unit tests for the aggregation core + 3 for the sidebar Reports section → `pnpm test` **303 passed**; `tsc --noEmit` clean. - Smoke-tested every route end-to-end against seed data with a real session: index / drill / detail / CSV export all **200**; a non-`view_analytics` role gets **307 → /dashboard** (page) and **403** (export). ## Wiki - **Reports Mockup** — marked implemented (shipped vs deferred). - **Pages and Navigation** — lists the new `/reports/*` routes + the Reports section. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
shad0w added 1 commit 2026-06-24 02:22:49 +00:00
feat(reports): Purchasing spend analytics (Cost Centres + Accounting Codes)
All checks were successful
PR checks / checks (pull_request) Successful in 46s
PR checks / integration (pull_request) Successful in 31s
8c6bbd8304
Implements the wiki "Reports Mockup" as a Reports → Purchasing sidebar section,
wired to real approved-PO spend.

Two report families, each index → drill/detail:
- Cost Centres (/reports/cost-centres) — spend compared across vessels; row
  opens a cost-centre report with a Top-accounting-codes breakdown re-pivotable
  by tier (Heading/Sub/Leaf) + Top-N.
- Accounting Codes (/reports/accounting-codes) — drills the Account tree
  (headings → sub → leaves) via ?parent=; a leaf opens its report broken down by
  cost centre (or, for a non-leaf, by sub-account).

Shared: a pinned filter toolbar (Granularity Monthly/Yearly, Financial Year,
Show Top5/Top10/Bottom5/All) whose values live in the URL query so the server
component re-renders — no client fetching. KPI tiles, recharts comparison/trend/
breakdown charts, per-row trend sparklines, and CSV export (/api/reports/spend).

- lib/reports.ts: the pure, unit-tested aggregation core. Spend = a PO once it
  reaches POST_APPROVAL_STATUSES, dated by approvedAt, valued at totalAmount
  (the dashboard's basis); Indian Apr–Mar FY; each PO's leaf accountId rolled up
  to parents. One query in getReportDataset(), everything else pure.
- Sidebar: new collapsible "Reports" section with a "Purchasing" subheading
  (subgroup support added to the Section model). Gated by view_analytics
  (Manager/SuperUser/Auditor/Admin); export by the same.

Deferred (documented): synthetic Weekly granularity, the "Add to graph" custom
multi-select, and line-item-level account allocation (v1 uses the PO-level
account). Sites are not cost centres — only vessels.

Tests: 11 unit cases for the aggregation core + 3 sidebar cases for the Reports
section. Full unit suite 303 green; tsc clean. Smoke-tested all routes end to
end against seed data (index/drill/detail/export 200; non-analytics role 307/403).

Wiki: "Reports Mockup" marked implemented; "Pages and Navigation" lists the new
routes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
shad0w added 1 commit 2026-06-24 05:55:08 +00:00
feat(reports): weekly granularity, custom compare, line-item allocation
All checks were successful
PR checks / checks (pull_request) Successful in 48s
PR checks / integration (pull_request) Successful in 31s
47ac2c7813
Picks up the three pieces deferred from the initial reports PR:

#3 Line-item account allocation — allocatePoSpend() splits each PO across the
accounting codes its line items carry (line accountId, falling back to the
PO-level account), proportionally so per-PO rows sum back to totalAmount. The
accounting-code report now attributes multi-account POs correctly. SpendRow
gains poId; poCount is now distinct POs, not row count.

#2 Custom "Add to graph" — tick rows on either index (SelectCheckbox links
write ?sel=id1,id2), then "Compare selected" (?cmp=1) shows a custom comparison
of just those entities. Fully server-rendered + shareable; export honours sel.

#1 Weekly granularity — a third Granularity that focuses one FY month and
buckets spend by week-of-month (W1–W5) from approvedAt, with a Month picker in
the toolbar. Real buckets (not the mockup's synthetic split).

All three are URL-driven like the rest, so no client fetching. Charts/KPIs/
detail trends all branch on the new mode.

Tests: +8 unit cases (allocation proportional/fallback/empty, weekly buckets,
sel parse/toggle, month + granularity parsing); fixture updated for poId/week.
Full unit suite 311 green; tsc clean. Smoke-tested weekly + custom-compare +
exports end-to-end (all 200). Docs + wiki updated to mark them implemented.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
shad0w merged commit 91349f7564 into master 2026-06-24 06:03:29 +00:00
Sign in to join this conversation.
No description provided.