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:
@@ -17,8 +17,10 @@ Each printer instance (a `devices` row, category `printer`) declares a **role**
|
||||
config:
|
||||
|
||||
- **`entry-dispenser`** — outside, at the lane. Prints the entry ticket the driver takes.
|
||||
- **`booth-receipt`** — inside the booth. Prints receipts at exit/payment, AND serves as the
|
||||
**backup** for entry tickets.
|
||||
- **`booth-receipt`** — inside the booth. Prints receipts at exit/payment, the booth till's
|
||||
Z-reports and vouchers, AND serves as the **backup** for entry tickets (and for wash slips).
|
||||
- **`wash-desk`** — at the Car Wash desk (added 2026-09-06). Prints the wash till's slips:
|
||||
its Z-report and drawer vouchers (see [[shift]] §Tills). Nothing else ever prints here.
|
||||
|
||||
It also declares a **`failoverRank`** (higher = preferred within a role) to order multiple
|
||||
printers of the same role deterministically (ties broken by id).
|
||||
@@ -33,6 +35,16 @@ The reverse is **deliberately not** done: a **receipt** never prints on the outs
|
||||
Receipts are a booth-only job; an entry dispenser falling back to print receipts makes no
|
||||
physical sense.
|
||||
|
||||
For a **wash slip** (`wantRole = wash-desk`): the desk printers first, then the **booth
|
||||
printer** — a site that has not bought a desk printer keeps printing the wash till's Z-report
|
||||
and vouchers in the booth, exactly as it did before the role existed. Nothing falls back *to*
|
||||
the wash desk: a booth receipt or an entry ticket never prints there. `ShiftService` resolves
|
||||
the role from the till (`TILL_PRINTER_ROLE`: booth → `booth-receipt`, carwash → `wash-desk`)
|
||||
and keeps one legacy fallback — a booth with a single printer that carries no booth role still
|
||||
prints its slips on it. `printerRoleOf(config)` is the one reading of the saved `role` field,
|
||||
so every loader (entry flow, booth receipts, shift slips) agrees on what a printer is; the
|
||||
device footer shows a desk printer as "at wash desk".
|
||||
|
||||
## Where the logic lives
|
||||
|
||||
- The driver (`rongta`) is **role-agnostic** — role/rank are just config; the transport doesn't
|
||||
|
||||
+19
-2
@@ -305,8 +305,25 @@ manifest (Car Wash → `carwash`; a future Bar → `bar`). Rules:
|
||||
touch the booth by construction; the header button, the hub's start buttons and the
|
||||
drawer switch never offer a till the server would refuse. (A first cut that borrowed
|
||||
`session:read` as "works the booth till" lived for a few hours and is gone.)
|
||||
- Not done: the per-shift *activity log* is still a time window over the whole chain (money
|
||||
figures are per till, the event list is not); bay slips print on the booth printer.
|
||||
- **The activity log is per till too (2026-09-06).** `tillOfEvent(type, payload)` in
|
||||
`@parking/shared` extends the money rule to every event: a money event names its till, any
|
||||
other event belongs to the till of the module that owns its type (a `carwash_order` is
|
||||
wash-desk activity though no money moved), everything else — entries, exits, barrier
|
||||
commands, pre-till events — is the booth's. `/api/events?till=` applies the same rule in
|
||||
SQL (so the page limit applies after the filter); the hub's shift log, the Drawer "today"
|
||||
panel and the booth feed (history and live pushes) pass their till. The events route also
|
||||
admits a role without `event:read` that holds a module's feed permission, and then returns
|
||||
only that module's event types — the live-socket rule, so a wash operator's hub shows the
|
||||
wash shift's log.
|
||||
- **The booth Z-report breaks module money out (2026-09-06).** `chargesByModuleMinor`
|
||||
(`{ carwash: <minor> }`, only when any was taken) sums the `chargeLines` on the till's
|
||||
payments by owning module; the ticket bucket EXCLUDES it, so `Bileta` is parking money only
|
||||
and ticket + subscriptions + Σcharges = cash + card. Printed as `Lavazh (në biletë): X` on
|
||||
the booth slip; the wash till's own slip prints its takings under `Lavazh:` (it sells no
|
||||
tickets or subscriptions). Signed on `shift_z_report`; older reports read back as `{}`.
|
||||
- **The wash till prints on its own printer (2026-09-06).** Printer role `wash-desk`; the
|
||||
wash till's Z-report and vouchers go there, falling back to the booth printer — see
|
||||
[[printer-roles-failover]].
|
||||
|
||||
## Where the fraud control actually lives
|
||||
|
||||
|
||||
Reference in New Issue
Block a user