wiki: design the business layer (session, tariff, permit, vision, shift, ops)
Pivot from the hardware/integrity layer to the parking operation. All wiki-only; no code yet. Core principle throughout: business entities are projections over the signed append-only event log, never mutable tables. New concepts: parking-session, tariff (composable/versioned, FX-ready), shift (manned-only Z-report), capacity-occupancy, validation-discounts, reporting-analytics, clock-integrity, ticket-encoding, anti-passback. New entities: permit, opencv-anpr-service, blocklist. Decisions: session-model, vision-service (host-side ANPR + vehicle verification; scoped AGPL exception for the isolated service). Updates: append-only-event-chain (new event types + vision witness), local-jwt-auth (drop 8h expiry -> until logout; code change pending), lpr-camera (host-side recognition supersedes edge-AI), standing-decisions (AGPL exception), open-questions (+FX, +pay-station money corners, backup). Deferred + flagged: intercom/help-call, receipts/refunds/change, FX engine, lane topology (#1).
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
---
|
||||
type: concept
|
||||
tags: [parking, domain, business, shifts, anti-fraud]
|
||||
sources: []
|
||||
updated: 2026-06-15
|
||||
status: open
|
||||
---
|
||||
|
||||
# Shift (manned mode) & the Z-Report
|
||||
|
||||
A **shift** is one operator's accountability period at a manned booth: from the moment they take
|
||||
over to the moment they hand over, however long that is. At the end, the system signs and **prints
|
||||
a Z-report** — the cash and POS totals taken during the shift. (Decisions 2026-06-15.)
|
||||
|
||||
## Shifts exist ONLY in manned mode
|
||||
|
||||
A shift is fundamentally a **human accountability boundary** — "this person was responsible for the
|
||||
takings from here to here." In the [[autonomous-direction|fully-automated / unmanned]] system there
|
||||
is **no operator and no shift**; what replaces it is the pay station's **cash-collection cycle**
|
||||
(who emptied the vault, when, how much vs. what the signed log expected) plus ongoing
|
||||
[[reconciliation]] — a separate concept, not a shift. So shifts are scoped to manned operation;
|
||||
don't force one model across both.
|
||||
|
||||
## A shift is NOT time-based
|
||||
|
||||
It is delimited by **explicit operator action**, never by a clock:
|
||||
|
||||
- Booth reality: relief comes late, doesn't show, or one operator is **forced to work two shifts in
|
||||
a row**. A fixed 8h boundary (or an 8h token expiry) would be wrong — it could strand an active
|
||||
operator. So the [[local-jwt-auth|login token has no time expiry]] (valid until logout).
|
||||
- **Start Shift / End Shift are explicit, and independent of login.** One login can span many
|
||||
shifts; a back-to-back double is simply *End Shift → Start Shift again*, no re-login. The
|
||||
operator (the same person or the next) marks the boundary.
|
||||
|
||||
```
|
||||
login ——————————————————————————————————————————————→ (until logout)
|
||||
[Start shift] … takings … [End shift→sign+print Z] [Start shift] … [End shift] …
|
||||
```
|
||||
|
||||
## What End Shift does
|
||||
|
||||
1. Determine the shift's payment set: the signed `payment` events ([[parking-session]],
|
||||
[[append-only-event-chain]]) between this shift's start mark and now.
|
||||
2. Sum by **tender**: `cashTotal`, and `cardTotal` from the POS/terminal **if a POS is configured**
|
||||
(the card line is omitted when there's no terminal).
|
||||
3. Append a signed **`shift_z_report`** event (type already in `packages/shared`): `{ operator,
|
||||
startedAt, endedAt, cashTotal, cardTotal?, paymentCount, eventRange, prevZHash }` — chained to
|
||||
the prior Z so a missing/out-of-order Z-report is itself visible.
|
||||
4. **Print the Z-report** (cash total, POS total if any, counts, shift window, operator) on the
|
||||
booth printer.
|
||||
|
||||
That's the whole human-side requirement: **print the cash and the POS (if any).** No blind count,
|
||||
no variance gate, no manager override.
|
||||
|
||||
## Where the fraud control actually lives
|
||||
|
||||
Deliberately **not** in a shift-close ceremony. Because every payment is a **signed event in the
|
||||
append-only chain**, the printed cash figure *is* the system's tamper-evident truth. A manager
|
||||
reconciles the signed Z-report against the actual drawer and the bank/POS batch **later** — that's
|
||||
[[reconciliation]], the real control (deferred). The tradeoff vs. a heavier control is purely
|
||||
*when* a skim is caught (after the fact, by a human), not *whether*.
|
||||
|
||||
> **Optional enhancement (not building now): blind cash count.** Have the operator enter the
|
||||
> counted cash *before* the system reveals the expected figure, and record the variance into the
|
||||
> `shift_z_report`. Blindness removes the operator's ability to back-fill their declaration to match
|
||||
> expectation, catching a skim **at close** rather than later. Explicitly out of scope per
|
||||
> 2026-06-15; documented as a clean add-on if ever wanted.
|
||||
|
||||
## Open
|
||||
|
||||
- **Shift ↔ session boundary:** a vehicle may enter under one shift and pay under another — the
|
||||
Z-report sums by **payment time** (when cash/card was taken), which is the operator who handled
|
||||
the money. Confirm that's the intended accountability (vs. by entry).
|
||||
- **Mid-shift report / X-report** (read-only "so far" total without closing) — add if booths want
|
||||
it; the sum is the same projection.
|
||||
- **Multiple lanes/booths** — whether a shift is per-operator, per-booth, or per-site
|
||||
(relates to [[open-questions]] #1 lane topology).
|
||||
Reference in New Issue
Block a user