feat(tills): per-till activity log, wash bucket on the booth Z-report, wash-desk printer role

Closes the three known follow-ups of the Tills decision (venue-modules.md):

- Activity log per till: `tillOfEvent(type, payload)` in @parking/shared (money events
  by payload till, other events by their owning module's till, everything else booth),
  applied by `/api/events?till=` in SQL and passed by the hub log, the Drawer "today"
  panel and the booth feed (history + live pushes). The events route admits a role that
  holds a module feed permission without event:read and returns only that module's
  event types — the live-socket rule.
- Booth Z-report: `chargesByModuleMinor` sums the chargeLines on the till's payments by
  module; the ticket bucket excludes them (Bileta = parking only); printed
  "Lavazh (në biletë)" only when any was taken. The wash till's slip prints "Lavazh:".
- Printer role `wash-desk`: the wash till's Z-report and vouchers print there, falling
  back to the booth printer; nothing falls back to the desk. `printerRoleOf()` is the
  one reading of the role field (the entry/booth loaders treated any non-booth role as
  an entry dispenser). Footer label "at wash desk".

Also: `GET /api/carwash/settings` opens to carwash:read OR site:read (new
requireAnyPermission) — the Wash operator job could not load the desk's category and
service pickers. Tests for all four; wiki (shift, printer-roles-failover, venue-modules,
log) updated.

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
2026-09-06 12:37:26 +02:00
parent ea304bbfd1
commit e14e31a840
27 changed files with 414 additions and 91 deletions
+21
View File
@@ -3049,3 +3049,24 @@ design error: the discount ENGINE (validation program rows + `applyValidation()`
`validation` module is only the merchant's scan screen. Fixed: `dependsOn: ["parking"]`; the
program routes are plain site:read/site:update; the merchant routes (mine/lookup/apply/void)
stay module-gated. Tests updated. Recorded on [[venue-modules]] (v1 answers item 4 + As-built).
## [2026-09-06] ingest | Tills follow-ups closed: per-till activity log, wash bucket on the Z, wash-desk printer
The three "known follow-ups" of the Tills decision are built. (1) `tillOfEvent(type, payload)`
in `@parking/shared` — money events by payload `till`, other events by their owning module's
till, everything else booth — is applied by `/api/events?till=` in SQL and passed by the hub
log, the Drawer "today" panel and the booth feed; the events route now admits module-feed
roles (a wash operator's `carwash:read`) and returns only their module's types, the same rule
the live socket uses. (2) `chargesByModuleMinor` on the shift report/summary/signed payload:
module charges on the till's payments by module; the ticket bucket excludes them; printed
`Lavazh (në biletë)` on the booth slip; the wash till's slip prints `Lavazh:` for its own
takings. (3) Printer role `wash-desk`: the wash till's Z-report and vouchers print there with
failover to the booth printer; `printerRoleOf()` is the one reading of the role field so a
desk printer is never mistaken for an entry dispenser; footer label "at wash desk". Updated
[[shift]] §Tills, [[printer-roles-failover]], [[venue-modules]].
## [2026-09-06] ingest | Wash operator job could not load the desk's price list
User built a role from the "Wash operator" chip (carwash:read/create/update/cash) and the desk's
category/service pickers stayed empty. Cause: `GET /api/carwash/settings` was guarded by
`site:read` only — the price list is Setup's data AND the desk's working data. Fixed with a
new `requireAnyPermission(...)` guard (auth.ts): the read opens to `carwash:read` OR
`site:read`; the write stays `site:update`. Regression test in carwash.test.ts.