docs(wiki): merchant validations settled + as-built; scan input decided (camera paths postponed)
Build desktop / desktop (push) Successful in 5m5s
CI / check (push) Successful in 47s
Build & push images / images (push) Successful in 2m59s

validation-discounts: driving cases → the settled validation-only model (all
money/paper at the booth) → setup UX/storage/RBAC → full as-built record.
DECIDED: merchant stations scan with a USB/HID barcode scanner on the
web/desktop app (hand-keying + Luhn as fallback); POSTPONED with analysis:
web getUserMedia scanning (secure-context TLS prerequisite on the LAN +
Code128-via-camera weakness → QR-on-ticket first) and a Tauri v2 Android
merchant app (native ML Kit scanning; Android build/sideload overhead +
configurable-server-URL prerequisite). Also: wsl-dev-networking gains the
mirrored-mode gotcha where a Windows-side listener makes a port EADDRINUSE
inside WSL while invisible to ss — Vite auto-increments and tauri dev's fixed
devUrl waits on the wrong port.

Claude-Session: https://claude.ai/code/session_01YYkpEsLmoQPaize5ec3oUm
This commit is contained in:
2026-07-13 19:50:09 +02:00
parent 692dff5f89
commit 28bd838696
4 changed files with 199 additions and 2 deletions
+59
View File
@@ -2560,3 +2560,62 @@ privilege + a signed ledger event with actor+reason, with the booth as enforcer
fail-open that can't depend on the cloud. Four open questions parked (real-time definition, where
reports are computed, hosting/licensing, custodianship-as-liability). Cross-linked; index count
7→8 decisions.
## [2026-07-13] update | Owner requirement — in-park merchant validations (car-wash "lavazh", bar)
The [[validation-discounts]] feature is now asked-for, not just an industry-survey gap: the park
may host an in-park car-wash and/or bar whose customers the owner wants discharged for the stay —
full comp, free-first-N-minutes (`time-credit`), or consumption-offset (`fixed`, variable amount:
300 ALL consumed vs 500 ALL fee → pay 200). Must be admin-composable at runtime like
tariffs/subscription plans. Driving-cases section added to [[validation-discounts]]. Open: merchant
ownership (owner-run → pure discount; tenant → [[validation-sponsorship]] settlement), who applies
(operator vs merchant code/portal), stacking rules, caps.
## [2026-07-13] update | Merchant validations refined — merchant STATIONS (users), not sponsors
Second pass on the [[validation-discounts]] requirement: ownership immaterial, sponsor layer
dropped. Merchant = a system user on their own device who scans the ticket to validate (signed,
attributed); admin checkbox per station = may collect parking payments (then shift + till +
Z-report apply to them like the booth); paid/zero-due tickets self-exit at the reader.
Consequences: per-station shifts/drawers (breaks the site-wide single-open invariant), exit-reader
live due=0 branch. Details on [[validation-discounts]].
## [2026-07-13] update | Merchant validations settled — validation-only merchants, all money at the booth
Third pass, settled: the merchant-collects-payments variant is REJECTED. Merchant users only scan
+ validate (signed, attributed); every car checks in at the booth to settle (net may be 0 — still
a signed payment) and gets the detailed gross/discount/net receipt there. Per-station
shifts/drawers and the exit-reader due=0 branch are no longer needed — shift/drawer/exit flows
stay as built; Z/X-reports gain discount lines. Build surface: validation_programs master data,
signed validation event, priceSession validations[] extension, merchant scan page, booth
quote/receipt/Z-report lines. Details on [[validation-discounts]].
## [2026-07-13] decision | Merchant validations — design SETTLED, build started
Setup UX on /setup/site (Bar/Lavazh checkboxes → right-column config panel, tabs when both);
fixed UI over generic storage (validation_programs + user binding, well-known bar/lavazh rows,
mutable config — the signed validation event carries resolved values); RBAC = new `validation`
resource (create/read), guard = permission AND station binding; merchant-only users land on
/validate; merchants may void their own unused validation. See [[validation-discounts]].
## [2026-07-13] update | Merchant validations BUILT end-to-end (bar / lavazh)
Shipped the settled design: `validation` permission + ledger event (resolved values, refId-void),
priceSession validations[] canonical fold (timeCredit→percent→fixed→comp, Σ lines ≡ gross−net),
validation_programs(+users) tables (migration 0024, reset-db config category), routes/validations.ts
(programs PUT signs config_change; apply guards: binding → open transient → no dup → maxPerDay →
amount cap; void own-unused-only), PayStation quote/pay/lookup net folding + payment consumption
(grossMinor/discountMinor/validationIds/validationLines), receipt gross+discount lines, Z/X-report
discountTotalMinor ("Zbritje (validime)", printed only when >0), /setup/site two-column Bar/Lavazh
checkboxes + config panel (tabs), /validate merchant screen (merchant-only users land there),
booth-modal gross→lines→net, feed label VALIDIM. 8 new route integration tests + shared fold suite;
workspace build/typecheck/test green. As-built + remaining polish on [[validation-discounts]].
## [2026-07-13] decision | Merchant scan input: HID barcode scanner on web/desktop; camera paths postponed
The bar/lavazh stations use a USB/HID scanner (or hand-keying + Luhn) into /validate on the
web/desktop app. Two evaluated camera alternatives deliberately POSTPONED: web getUserMedia
scanning (blocked on secure-context TLS for LAN phones + weak Code128-via-camera — would want
QR-on-ticket first) and a Tauri v2 Android merchant app (native ML Kit scanning via the official
barcode-scanner plugin; deferred over Android build/distribution overhead + the
configurable-server-URL prerequisite). Full analysis on [[validation-discounts]].