feat(po): cancel POs + optional supersede link (#53) #56

Merged
shad0w merged 2 commits from feat/po-cancel-supersede into master 2026-06-21 06:58:32 +00:00
Owner

Closes #53.

What

Managers and superusers can cancel a PO from any state, and optionally link a cancelled PO to the existing PO that supersedes it.

Behaviour (per the #53 answers)

  • Cancel: a confirmation modal requires typing cancel and a mandatory reason. Terminal CANCELLED status; submitter + Accounts notified; audit row logged.
  • Roles: cancel_po → MANAGER + SUPERUSER.
  • From any state (except already-cancelled).
  • Financials excluded: cancelling flips status to CANCELLED, which is absent from POST_APPROVAL_STATUSES and every spend whitelist, so the value drops out of all trackers/graphs automatically.
  • Supersede: link an existing PO by number (no vessel/account/vendor match enforced, addable any time). The replacement shows the reciprocal "supersedes" link.
  • Visibility: cancelled POs stay visible (greyed in history) and exportable, with a diagonal CANCELLED watermark on the PDF (CSS) and XLSX (embedded image).
  • Inventory reversal: deferred to #55 (inventory is feature-flagged off) and recorded on the wiki Tech-Debt register.

Changes

  • Schema: POStatus.CANCELLED; cancelledAt/cancellationReason; self-referential supersededById; ActionType.CANCELLED/SUPERSEDED (+ migration).
  • lib/po-state-machine.ts canCancel; cancel_po permission; PO_CANCELLED notification.
  • cancelPo / supersedePo server actions.
  • Cancel modal + supersede form; cancelled banner with reciprocal links; CANCELLED badge; greyed history rows.
  • Exhaustive CANCELLED entries across all status label/variant maps.
  • Export watermark (PDF + XLSX).

Verification

  • pnpm type-check clean; pnpm test green (185 unit).
  • New integration test cancel-supersede.test.ts — 10 tests, all passing against a fresh migrated+seeded DB (cancel from any state, reason/role guards, already-cancelled, spend-exclusion, supersede + reciprocal, not-cancelled/unknown/self guards).
  • Watermark image visually verified.

Note: the integration suite as a whole is only green on top of #54 (integration-test repairs); this test is self-contained (builds POs via db.create) so it passes independently.

🤖 Generated with Claude Code

Closes #53. ## What Managers and superusers can **cancel** a PO from **any** state, and optionally link a cancelled PO to the existing PO that **supersedes** it. ## Behaviour (per the #53 answers) - **Cancel**: a confirmation modal requires typing `cancel` and a **mandatory reason**. Terminal `CANCELLED` status; submitter + Accounts notified; audit row logged. - **Roles**: `cancel_po` → MANAGER + SUPERUSER. - **From any state** (except already-cancelled). - **Financials excluded**: cancelling flips status to `CANCELLED`, which is absent from `POST_APPROVAL_STATUSES` and every spend whitelist, so the value drops out of all trackers/graphs automatically. - **Supersede**: link an **existing** PO by number (no vessel/account/vendor match enforced, addable any time). The replacement shows the **reciprocal** "supersedes" link. - **Visibility**: cancelled POs stay visible (greyed in history) and **exportable**, with a diagonal **CANCELLED** watermark on the PDF (CSS) and XLSX (embedded image). - **Inventory reversal**: deferred to #55 (inventory is feature-flagged off) and recorded on the wiki Tech-Debt register. ## Changes - Schema: `POStatus.CANCELLED`; `cancelledAt`/`cancellationReason`; self-referential `supersededById`; `ActionType.CANCELLED/SUPERSEDED` (+ migration). - `lib/po-state-machine.ts` `canCancel`; `cancel_po` permission; `PO_CANCELLED` notification. - `cancelPo` / `supersedePo` server actions. - Cancel modal + supersede form; cancelled banner with reciprocal links; CANCELLED badge; greyed history rows. - Exhaustive `CANCELLED` entries across all status label/variant maps. - Export watermark (PDF + XLSX). ## Verification - `pnpm type-check` clean; `pnpm test` green (185 unit). - New integration test `cancel-supersede.test.ts` — 10 tests, all passing against a fresh migrated+seeded DB (cancel from any state, reason/role guards, already-cancelled, spend-exclusion, supersede + reciprocal, not-cancelled/unknown/self guards). - Watermark image visually verified. > Note: the integration suite as a whole is only green on top of #54 (integration-test repairs); this test is self-contained (builds POs via `db.create`) so it passes independently. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
shad0w added 1 commit 2026-06-21 06:53:40 +00:00
feat(po): cancel POs (manager/superuser) + optional supersede link (#53)
All checks were successful
PR checks / checks (pull_request) Successful in 32s
0b10ba5e54
Managers and superusers can cancel a PO from any state via a confirmation modal
that requires typing "cancel" and a mandatory reason. A cancelled PO becomes a
terminal CANCELLED state and drops out of every spend tracker/graph (those filter
on POST_APPROVAL_STATUSES / explicit whitelists, none of which include CANCELLED).

A cancelled PO may optionally be linked to the existing PO that supersedes it
(by PO number); the replacement shows the reciprocal "supersedes" link. No
vessel/account/vendor match is enforced and the link can be added any time.

Cancelled POs remain visible (greyed in history) and exportable, with a diagonal
"CANCELLED" watermark on both the PDF and XLSX exports.

- schema: POStatus CANCELLED; cancelledAt/cancellationReason; self-referential
  supersededById relation; ActionType CANCELLED/SUPERSEDED (+ migration)
- state machine canCancel(); cancel_po permission (MANAGER + SUPERUSER)
- cancelPo / supersedePo server actions + PO_CANCELLED notification
- cancel modal + supersede form; cancelled banner with reciprocal links
- exhaustive CANCELLED entries in all status label/variant maps
- diagonal CANCELLED watermark embedded for PDF (CSS) and XLSX (image)
- integration tests (cancel from any state, reason/role guards, supersede)

Inventory reversal on cancel is deferred to #55 (inventory is feature-flagged off).

Closes #53

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
shad0w added 1 commit 2026-06-21 06:56:41 +00:00
Merge branch 'master' into feat/po-cancel-supersede
All checks were successful
PR checks / checks (pull_request) Successful in 32s
PR checks / integration (pull_request) Successful in 27s
a8d772d63b
shad0w merged commit 9de60200f9 into master 2026-06-21 06:58:32 +00:00
Sign in to join this conversation.
No description provided.