J-07 — Walk-In Booking with Cash Deposit (Offline)
One-liner: Internet down at the front desk; receptionist creates a new reservation, takes cash, issues a key, and trusts that everything reconciles when the link is back.
1. Purpose
The internet is down at the front desk. A walk-in guest needs a room now. The receptionist must create a new reservation, take cash, issue a key, and trust that the system will reconcile when the link is back. Outcome: a reservation in Confirmed (and possibly Checked-in) state, a folio with a cash deposit, an issued key — all locally — that reconciles cleanly when the network returns.
2. Persona Context
- Persona: Receptionist.
- Surface: Electron Desktop (
apps/desktop), offline-primary. - Primary "BFF": None online. The Electron app's local SQLite + outbox + signed offline-cert acts as the system of record for the duration of the outage.
- Backing services (when reconnected):
booking-service,folio-service,payments-service,lock-and-key-service,audit-service. - Preconditions: Electron app has fresh "primer" pack (today's published rates + room inventory + tenant policies + offline-cert valid for the day). Cash drawer is opened and counted at start-of-shift.
- Trigger: A walk-in guest at the desk + connectivity indicator showing "Offline".
3. Entry Points
| # | Entry | Notes |
|---|---|---|
| 1 | "New booking" CTA on Front Desk screen with offline pill visible | Default |
| 2 | Quick action keyboard shortcut (e.g., Ctrl+N) | Power user |
4. Screen-by-Screen Flow
4.1 OfflineBanner (always visible during this journey)
- Layout: Persistent banner: "Offline mode - changes will sync when connection returns. Pending:
events." - Components:
OfflineStatusPill,SyncCenterButton. - Offline: Always reflects local state.
- A11y: Banner announced at offline-state change; status pill is keyboard-reachable and opens Sync Center.
4.2 WalkInBookingWizardStep1RoomTypeSelection
- Layout: Same as J-03 §4.1 but reads inventory from local SQLite snapshot; rate plan from primer.
- Components:
RoomTypeListCard,OccupancyEditor. - Offline: Primary source. Rate display shows "Snapshot 14:30" timestamp.
- AI: None.
- Errors: Snapshot stale (> 24 h) -> blocking modal "Reconnect to refresh inventory".
- Loading: Sub-200 ms.
- A11y: Same as J-03; offline pill prominent.
- RTL: Mirror.
- Perf: <= 200 ms.
- Telemetry:
frontend.walkin.step_viewed { step: 1, mode: offline }.
4.3 WalkInBookingWizardStep2GuestInformation
- Layout: Same fields as J-03 §4.3; transliteration AI uses edge ONNX (lower confidence).
- Components:
Form,PhoneInput,BidiText,AiTransliterationCard(edge mode). - Offline: Primary mode.
- AI: Edge ONNX transliteration; provenance shows
local=true; receptionist always confirms. - Errors: Validation per field; phone format coerced.
- Loading: Sub-100 ms.
- A11y: Field-by-field labels; transliteration suggestion announced.
- RTL: Mirror.
- Perf: Form responsive; transliteration <= 200 ms edge.
- Telemetry:
frontend.walkin.guest_info_completed.
4.4 WalkInBookingWizardStep3CashDeposit
- Layout: Cash amount input, currency picker (tenant-supported only), denominations breakdown (helper for till-counting), receipt-print preview, "Take cash" CTA.
- Components:
MoneyInput,DenominationsBreakdown,ReceiptPreview,Button("Take cash"). - Offline: Primary mode; logged to local cash drawer.
- AI: None.
- Errors: Amount < required deposit -> warning; currency mismatch warning.
- Loading: Sub-200 ms.
- A11y: Field-by-field labels; denominations announced.
- RTL: Mirror; numerals localised per tenant policy.
- Perf: <= 200 ms; receipt print <= 5 s.
- Telemetry:
frontend.walkin.cash_taken { amount, currency }.
4.5 WalkInBookingWizardStep4CheckInAndKey
- Layout: Optional ID capture (uses J-05 §4.2 but in offline-primary mode), key issuance via offline-cert, "Done" CTA.
- Components:
IdCameraCapture(edge OCR),LockEncoderPanel(offline-cert path). - Offline: Primary mode; offline-cert signs key payload.
- AI: Edge OCR.
- Errors: Encoder USB disconnect -> retry UX.
- Loading: Encoder cycle <= 8 s.
- A11y: Encoder progress announced; "Done" announces completion.
- RTL: Mirror.
- Perf: Wizard total <= 60 s p95 offline.
- Telemetry:
frontend.walkin.completed { mode: offline }.
4.6 SyncCenterScreen (post-reconnect)
- Layout: List of pending events (reservation, folio open, deposit, key issued) with status: queued / synced / conflict; "Retry all" + per-item retry; conflict diff viewer.
- Components:
SyncCenterTable,ConflictDiffViewer,Button("Retry all"). - Offline: Lists local outbox.
- AI: None on flush. P2 — conflict resolution suggestion.
- Errors: Conflict UX described in §9.
- Loading: Spinner during flush.
- A11y: Each row is a tab stop with status announced; conflict diff is a structured view with
<th>for fields. - RTL: Mirror.
- Perf: Flush total <= 30 s p95 for typical walk-in (5-10 events).
- Telemetry:
frontend.sync.flush_started;frontend.sync.flush_completed { okCount, errorCount };frontend.sync.conflict { entity, strategy }.
5. State Machine
6. Data Requirements
6.1 Local persistence (primary system of record offline)
- SQLite tables:
reservations,folios,folio_charges,payments_local,key_credentials_local,room_status_local,outbox,audit_local. outboxrows have deterministic keys (<entity>:<localId>:<op>); flushed in causal order (reservation -> folio -> deposit -> key).
6.2 Server reconciliation
- On reconnect, outbox flushes via BFF endpoints (same as J-03/J-05/J-06) with
X-Idempotency-Keymatching local IDs. - Server returns canonical IDs; local IDs map to canonical IDs in
id_mapping. - Conflicts (e.g., room overbooked due to concurrent online booking) trigger conflict UX per §9.
6.3 Idempotency
- Mandatory; without it, retries during flush would overbook or double-charge.
7. AI Behavior
- Edge ONNX transliteration in Step 2 (low-confidence; receptionist always confirms).
- Edge ONNX OCR in Step 4 (low-confidence; receptionist always confirms).
- No cloud AI calls during this journey.
8. Offline Behavior
- This is the primary mode of this journey.
- All steps work offline.
- Receipts printed locally.
- Cash drawer logged locally; reconciled to EOD report (J-12).
9. Error States
| Error | Trigger | UX shown | Recovery | Telemetry |
|---|---|---|---|---|
OFFLINE_PRIMER_STALE | Snapshot > 24 h old | Blocking modal: "Reconnect to refresh inventory" | Reconnect required | frontend.walkin.primer_stale |
OFFLINE_CERT_EXPIRED | Day-cert expired | Blocking modal: "Cannot issue keys offline - reconnect" | Reconnect to refresh cert | frontend.walkin.cert_expired |
LOCK_ENCODER_USB_DISCONNECTED | USB unplug | Modal: "Reconnect encoder and retry" | Replug; auto-retry | frontend.walkin.encoder_disconnected |
RESERVATION_CONFLICT_ON_FLUSH | Concurrent online booking took the room | Conflict UX in Sync Center: "Room | Receptionist selects alt; confirms with guest | frontend.sync.conflict { entity: reservation } |
FOLIO_CHARGE_DUPLICATE_ON_FLUSH | Duplicate detected via idempotency | Silent dedupe | n/a | frontend.sync.dedupe { entity: folio_charge } |
OUTBOX_FLUSH_PARTIAL | Some events fail | Sync Center shows errors | Manual retry per item | frontend.sync.partial_failure |
10. E2E Test Gates
- Composite gate
G-DESK-3: walk-in flow offline (full wizard) -> reconnect -> outbox flush success. - Conflict variant: room booked online during outage; conflict UX exercised.
- Outbox flush ordering verified; audit log entries correct.
11. Performance Requirements
| Metric | Target |
|---|---|
| Wizard total (offline) | <= 60 s p95 |
| Edge OCR | <= 1 s p95 |
| Edge transliteration | <= 200 ms |
| Encoder cycle | <= 8 s p95 |
| Receipt print | <= 5 s |
| Outbox flush (typical 5-10 events) | <= 30 s p95 |
12. Accessibility Requirements
- All keyboard-completable.
- Offline pill announced; Sync Center reachable via shortcut and tab.
- Encoder + camera live regions announce status.
- Conflict diff viewer is structured for screen readers.
13. Telemetry
Frontend events
frontend.walkin.step_viewed { step, mode: offline }frontend.walkin.guest_info_completedfrontend.walkin.cash_taken { amount, currency }frontend.walkin.completed { mode: offline }frontend.sync.flush_started/flush_completed { okCount, errorCount }frontend.sync.conflict { entity, strategy }
Domain events emitted (after reconnect, in order)
melmastoon.booking.created.v1(with offline-origin marker)melmastoon.folio.opened.v1melmastoon.payments.cash.received.v1melmastoon.lock_and_key.credential.issued.v1(withmode=offline_cert)melmastoon.audit.recorded.v1(per step)
14. Success Criteria
- Walk-in flow completes <= 60 s p95 fully offline.
- Receipt printed locally; cash drawer logged locally.
- Lock-key issuance succeeds via offline-cert; key works on the door.
- Outbox flushes cleanly on reconnect; conflicts surface with explicit UX (no silent overbooking).
- Audit log entries match local timeline once flushed.
- EOD cash drawer (J-12) reconciles correctly without manual intervention.