feat(tariff-lab): DB-backed draft tariffs + named published versions
Experimenting used to mean publishing — churning the immutable version
history and risking real tickets pricing against a half-baked card while
the admin iterated. The lab is now a true sandbox:
- tariff_drafts table (migration 0021): MUTABLE by design — the one
exception to "editing publishes a version"; a draft prices nothing and
signs nothing. Drafts are validated + tz-stamped on save exactly like a
publish, so a saved draft always simulates and never fails at publish.
- CRUD under /api/tariff/drafts (list tariff:read, mutations
tariff:update); publishing a draft goes through the normal immutable
POST /api/tariff/versions path.
- Lab UI rebuilt: sidebar lists lab drafts AND the full published history
(click any to price against it); main pane cut to pure entry/exit
(ticket loader, payment, category inputs dropped); the composer form is
extracted to TariffEditorForm.tsx and reused in a modal (new drafts
prefill from the active card); per-draft Publish with confirm.
- tariff_versions.name (migration 0022): optional label stamped at
publish — carried from the lab draft, or typed in the composer's new
optional field — so history reads "Winter 2027", not UUID prefixes.
- Includes the composer UI + sq/en labels for the package mode (engine
landed in d9e6c13) and the "Flat price / hour" relabel.
5 new server integration tests (RBAC, roundtrip, validation, tz-stamp +
simulate + publish w/ name); server suite 288 green.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
+38
@@ -2282,3 +2282,41 @@ ISO-8601 UTC + level names (pinoDbStream hardened to accept both encodings so th
|
||||
silently break). Rotation: docker json-file caps in docker-compose.prod.yml resized from 10m×3
|
||||
(≈30 MB!) to ≈2 months by volume (server 20m×30, vision 20m×10, proxy 10m×5; json-file rotates by
|
||||
SIZE — time-based isn't a driver feature). app_logs retention default aligned 30→60 days.
|
||||
|
||||
## [2026-07-05] update | Window-package tariff mode (packageMinor) + honest flat labels
|
||||
|
||||
Tariff-lab verification of a 1,850 ALL bill exposed a field misread: the V2 card "flat price" is
|
||||
PER INCREMENT (400/h), not per window — park-buzi's "night 400" card billed each night hour 400.
|
||||
Built the missing concept on [[tariff-time-tiers]]: `packageMinor`, a whole-window package
|
||||
("any presence in 20:00–07:00 = 400 total"). Operator decisions: per-occurrence repeat (two nights
|
||||
= two charges), any-touch-pays-full, windowed-cards-only (base "price per day" = a 1-row up-to
|
||||
table). Engine charges once per contiguous run of increments the card wins, tracked across
|
||||
rolling-day segments (a night crossing the 24h boundary charges once). Validator: exclusive with
|
||||
flat/blocks/steps, no per-card cap, defaultCard forbidden. Composer offers the mode on tier cards;
|
||||
flat relabeled "Flat price / hour" (sq+en). Also flagged from the same session: windowed-card
|
||||
dailyCapMinor is inert by design (only the base card's cap clamps a day) — park-buzi's weekend
|
||||
card carries a dead 1000 cap. Tests: shared 93 green (6 new), server 283 green.
|
||||
|
||||
## [2026-07-05] update | Tariff Lab redesign: DB-backed drafts, sidebar, composer modal
|
||||
|
||||
The lab previously simulated only against PUBLISHED versions, so experimenting meant publishing —
|
||||
churning the immutable history and risking real tickets pricing against a half-baked card while
|
||||
the admin iterated (operator: "we risk taking tickets with a grossly wrong version"). Redesign on
|
||||
[[tariff]] (Tariff Lab section): new mutable `tariff_drafts` table (migration 0021 — the one
|
||||
deliberate exception to "editing publishes a version"; a draft prices/signs nothing, only the
|
||||
normal publish path makes it real), drafts validated + tz-stamped on save exactly like a publish,
|
||||
CRUD under /api/tariff/drafts (list tariff:read, mutations tariff:update). UI rebuilt: sidebar
|
||||
lists active card + drafts (click to price against), main pane cut to pure entry/exit (ticket
|
||||
loader, payment, category inputs dropped), composer form extracted to TariffEditorForm.tsx and
|
||||
reused in a modal (new drafts prefill from the active card), per-draft Publish with confirm.
|
||||
Simulation passes the draft's stored structure inline to the existing /api/tariff/simulate.
|
||||
Tests: server 288 green (5 new: RBAC, roundtrip, validation, tz-stamp + simulate + publish flow).
|
||||
|
||||
## [2026-07-05] update | Published tariff versions get optional names + lab sidebar lists history
|
||||
|
||||
Follow-up to the lab redesign (same session): the sidebar now also lists the PUBLISHED versions
|
||||
(active first, then the immutable history; click to price against by tariffVersionId), and
|
||||
`tariff_versions` gained a nullable `name` (migration 0022) — stamped at publish, immutable like
|
||||
the row. Publishing a lab draft carries the draft's name onto the version; the composer page grew
|
||||
an optional version-name field (never prefilled — republishing a tweak under last season's name
|
||||
would mislabel history). Details on [[tariff]] (Tariff Lab section).
|
||||
|
||||
Reference in New Issue
Block a user