feat(booth): payment receipt / exit voucher — transparency slip + CP852 fixes
After a completed payment the customer always gets a transparency record:
entry time, payment time, duration parked, amount + tender. One shared
ESC/POS renderer (renderReceipt + ReceiptData in @parking/devices), two
modes: VOUCHER = those figures PLUS the scannable Code128 barcode and an
emphasised walk-back-grace line, so the one slip both proves payment and
self-exits at a distant exit reader (replaced the old barcode-only voucher);
STANDALONE = detail-only, auto-printed at payment when no voucher is issued.
Figures fold from the SIGNED ledger (latest payment event); printed on the
booth printer (failover to dispenser). Best-effort: a printer fault never
blocks the exit that already happened — the modal shows a note and offers
"Reprint receipt".
Server: booth-print.ts printPaymentReceipt() + receiptFigures(); routes
POST /api/voucher (voucher) + new POST /api/receipt (standalone/reprint).
Both ESC/POS drivers gained printReceipt(). Web: BoothPayModal auto-prints
after a non-voucher payment + reprint button; api.ts printReceipt().
CP852 fixes found on a real printout: (1) uppercase Ë was mapped to 0xEB
(that's ű) — correct byte is 0xD3; (2) Intl.NumberFormat injects a NO-BREAK
SPACE (U+00A0/U+202F) that isn't in CP852 and printed as "?" — line() now
normalises it to a plain space ("1000 Lekë"); (3) grace line wrapped
mid-word — split into two short lines.
Full build green; both receipt modes render-verified; routes live.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -34,6 +34,20 @@ trigger. Decided 2026-06-17.
|
||||
runs the existing exit validation — which now finds the session **paid + within walk-back grace**,
|
||||
so it opens. No new identity or code type; the "biletë dalje" is a *paid reprint* of the entry
|
||||
ticket id. Reuses [[tariff|walk-back grace]] exactly.
|
||||
- **The customer always gets a transparency receipt after paying (built 2026-06-18).** Entry time,
|
||||
payment time, duration parked, amount + tender — one shared `ReceiptData`/`renderReceipt` in
|
||||
`@parking/devices`, two modes: (a) **voucher mode** = those figures PLUS the scannable barcode and
|
||||
an emphasised walk-back-grace line ("Dilni brenda N min — skanoni këtë biletë në dalje"), so the
|
||||
one slip both proves payment and self-exits — this **replaced the old barcode-only voucher**;
|
||||
(b) **standalone mode** = detail-only (no barcode), **auto-printed at payment** when the checkbox
|
||||
is OFF (booth at the exit, no voucher). Both fold the figures from the SIGNED ledger (latest
|
||||
`payment` event) and print on the booth printer (failover to the dispenser). The standalone
|
||||
auto-print is **best-effort**: a printer fault must NOT block the exit that already happened — the
|
||||
modal shows a note and offers **"Reprint receipt"** (also used if the slip jams or the customer
|
||||
asks later). Routes: `POST /api/voucher` (voucher mode), `POST /api/receipt` (standalone/reprint;
|
||||
requires paid, allows an already-exited session so a reprint still works). Receipt timestamps use
|
||||
the **host-local clock** (the appliance runs in site time) — distinct from the tariff's frozen tz,
|
||||
which governs pricing reproducibility, not display.
|
||||
- **The checkbox default lives in `site_config`** (`exit_voucher_default`, a site-wide boolean edited
|
||||
in Site settings) — because it's booth geography, not per-ticket. The operator may override per
|
||||
transaction. (Per-exit-point config deferred until a site has both a near and a far exit.)
|
||||
@@ -154,6 +168,8 @@ off the latest payment. See [[tariff]] (walk-back grace) for the pricing side of
|
||||
reader path (one code path, two triggers).
|
||||
- **Open: walk-back grace renews on every payment** — see the flagged section above (voucher overstay
|
||||
re-grants a full grace window; pick a fix before production).
|
||||
- Voucher print = reprint the ticket id barcode on the booth printer ([[ticket-encoding]]).
|
||||
- Voucher/receipt print = `renderReceipt` (shared ESC/POS) on the booth printer ([[ticket-encoding]],
|
||||
[[printer-status-monitoring]]): voucher mode = figures + barcode + grace; standalone = figures only,
|
||||
auto-printed at payment. `POST /api/voucher`, `POST /api/receipt`.
|
||||
- Open: a force-open **override** (lost ticket / equipment fault) — deferred; would be a separately
|
||||
audited signed event, not folded into the validated path.
|
||||
|
||||
Reference in New Issue
Block a user