Skip to main content

J-11 — Late Checkout & Folio Dispute

One-liner: Guest disputes a charge at checkout; receptionist can request a manager approval, refund or credit-note.

1. Purpose

A guest at checkout disputes a charge (mini-bar item not consumed; cleaning fee considered unreasonable; late-checkout fee). The receptionist coordinates with a manager (over-the-shoulder approval or remote), removes / refunds / credit-notes the disputed line, and completes the checkout cleanly with a full audit trail. Outcome: dispute resolved, folio reconciles, audit log captures who-did-what, refund processed (cash on hand or card refund).

2. Persona Context

  • Persona: Receptionist (primary) + Manager (approver).
  • Surface: Electron Desktop.
  • Primary BFF: bff-backoffice-service.
  • Backing services: folio-service, payments-service, audit-service, notification-service.
  • Preconditions: Manager logged in (locally or via remote approval flow); folio is open.
  • Trigger: Receptionist clicks "Dispute charge" on a folio line.

3. Entry Points

#EntryNotes
1"Dispute" on folio line during checkoutDefault
2"Dispute" mid-stayPre-checkout
3Manager-initiated reviewP2

4. Screen-by-Screen Flow

4.1 DisputeChargeModal

  • Layout: Charge details, dispute reason picker, free-text note, photo evidence (optional), proposed action (remove / refund / credit-note); approval level shown ("Manager required for amounts > X").
  • Components: DisputeReasonSelect, Form, PhotoUpload, ApprovalRequiredPill.
  • Offline: Editable; submission queued.
  • AI: Phase 2 — dispute-pattern detector (HITL alert to manager).
  • Errors: Validation per field.
  • Loading: Sub-200 ms.
  • A11y: Field labels; pill announces approval requirement.
  • RTL: Mirror.
  • Perf: <= 200 ms.
  • Telemetry: frontend.dispute.opened { lineId, reason }.

4.2 ManagerApprovalPanel

  • Layout: Manager PIN / fingerprint pad (if configured) OR remote approval pending pill; reason + charge context displayed.
  • Components: ManagerPinPad, RemoteApprovalStatusPill.
  • Offline: PIN works locally; remote approval requires online.
  • AI: None.
  • Errors: Wrong PIN -> rate-limited retry; remote approval timeout -> banner.
  • Loading: Spinner during submit.
  • A11y: PIN pad announces input ("Manager PIN entered"); remote status announced live.
  • RTL: Mirror.
  • Perf: Local approval <= 500 ms; remote <= 30 s p95.
  • Telemetry: frontend.dispute.approval_submitted { method: pin|remote }.

4.3 ResolutionScreen

  • Layout: Confirmation of action taken (line removed / refund issued / credit note created), updated folio total, "Continue checkout" CTA.
  • Components: ResolutionSummaryCard, Button ("Continue").
  • Offline: Refund via cash local; card refund queued.
  • AI: None.
  • Errors: Refund failed -> retry / alternate.
  • Loading: Sub-second.
  • A11y: Resolution announced via aria-live.
  • RTL: Mirror.
  • Perf: <= 1 s p95.
  • Telemetry: frontend.dispute.resolved { action }.

5. State Machine

6. Data Requirements

6.1 Server state

  • POST /api/v1/folios/:id/disputes (idempotent)
  • POST /api/v1/folios/:id/disputes/:disputeId/approve (idempotent; carries manager auth)
  • POST /api/v1/folios/:id/disputes/:disputeId/resolve (idempotent; with action: remove|refund|credit-note)

6.2 Local persistence

  • SQLite disputes_local, outbox, audit_local.

6.3 Idempotency

  • All mutations carry X-Idempotency-Key.

7. AI Behavior

n/a in P1; P2 — dispute-pattern detector (HITL alert to manager when patterns recur).

8. Offline Behavior

  • Local PIN approval works.
  • Remote approval requires online.
  • Cash refunds local; card refunds queued.
  • Credit notes local; reconciled to authority report on next online window.

9. Error States

ErrorTriggerUX shownRecoveryTelemetry
MANAGER_PIN_INVALIDWrong PINInline error; rate-limited retriesManager re-entersfrontend.dispute.pin_invalid
REMOTE_APPROVAL_TIMEOUTNo response in 30 sBanner; offer fallback (local PIN)User switches methodfrontend.dispute.remote_timeout
REFUND_FAILEDProvider 5xxBanner; retry / alternateRetry or switch to credit noteerror.surfaced { code }
DISPUTE_OVER_CAP_FOR_ROLEReceptionist tries to auto-approve over their capModal: "Manager required"Manager enters PINfrontend.dispute.cap_exceeded

10. E2E Test Gates

  • Composite gate G-DESK-OPS-3: dispute -> manager approval -> resolve (remove / refund / credit-note) -> checkout continues.
  • Remote approval timeout fallback.
  • Refund failure -> credit note path.

11. Performance Requirements

MetricTarget
Dispute open<= 200 ms
Local approval<= 500 ms
Remote approval<= 30 s p95
Resolve action<= 1 s p95

12. Accessibility Requirements

  • PIN pad accessible; alternative manager-auth method (smartcard) supported.
  • Resolution announcement via aria-live.
  • All keyboard-completable.

13. Telemetry

Frontend events

  • frontend.dispute.opened { lineId, reason }
  • frontend.dispute.approval_submitted { method }
  • frontend.dispute.resolved { action }

Domain events emitted

  • melmastoon.folio.dispute.opened.v1
  • melmastoon.folio.dispute.approved.v1
  • melmastoon.folio.dispute.resolved.v1 (with action)
  • melmastoon.payments.refund.issued.v1 (if refund)
  • melmastoon.audit.recorded.v1

14. Success Criteria

  • Dispute resolved before checkout continues; folio reconciles.
  • Manager approval recorded with method (PIN / remote / smartcard).
  • Refund / credit note tied to original line; visible in folio history.
  • Audit log entry for opened, approved, resolved.

References