Files
baya-monorepo/archive/docs/integration/domains/bookings.md
T
2026-08-02 20:01:31 +03:30

6.4 KiB

bookings — the post-payment engine, sessions and EVV

Client seam client/src/services/bookings/ · USE_BOOKINGS_MOCK = false (real) · 16 server ops Last verified: 2026-07-30 against commit d3ec723 and swagger.v1.json (2026-07-29).

Stage two: a paid booking, its per-visit sessions, and electronic visit verification. A bookings row is created only on payment capture, from an accepted booking request.

Endpoints

Method Path Caller Verdict
GET /api/v1/bookings/list both wired · paginated · role=customer|nurse|all
GET /api/v1/bookings/get/{id} both wired
GET /api/v1/bookings/care_instructions/{id} nurse, admin wired · stage-2 disclosure gate
POST /api/v1/bookings/submit_care_instructions/{id} customer unwired — the client has no care-instructions form
POST /api/v1/bookings/convert unwired — Development-only capture simulator; the PSP webhook supersedes it
POST /api/v1/bookings/transition/{id} admin unwired — a raw state-machine escape hatch, no UI
POST /api/v1/bookings/cancel/{id} unwired, superseded by {id}/cancel
POST /api/v1/bookings/{id}/cancel customer wired — by refunds (cancel and refund, REQ-019)
GET /api/v1/bookings/{id}/cancellation_policy customer wired — by refunds (REQ-020)
GET /api/v1/booking_sessions/today nurse wired · paginated
GET /api/v1/booking_sessions/evv/{id} nurse wired
POST /api/v1/booking_sessions/check_in/{id} nurse wired
POST /api/v1/booking_sessions/check_out/{id} nurse wired · sensitive 20/min — this is what releases the payout clock
POST /api/v1/booking_sessions/cancel/{id} nurse unwired — no per-session cancel in the UI
GET /api/v1/admin_evv/list admin unwired — no console screen; the mock demonstrates it
POST /api/v1/admin_evv/detect_no_shows admin unwired — an ops one-shot; the scheduler runs it

All [Authorize]; the two admin_evv routes are DynamicPermission + sensitive. No phantoms.

Two cancel routes exist on the same controller. POST bookings/cancel/{id} (action-style, b9) and POST bookings/{id}/cancel (REST-style, b11 cancel-and-refund). Only the second is wired. They are not aliases — the second also drives the refund. Treat the first as legacy.

The lifecycle

pending_payment ──capture──▸ confirmed ──first check-in──▸ in_progress
                                 │                              │
                                 │                       all sessions out
                                 ▼                              ▼
                             cancelled  ◂──cancel──         completed ──dispute window──▸ closed
                                                                 └──dispute──▸ disputed

Forward-only, through the transition table. status has a private setter; only cohesive domain methods mutate it and the handler pre-checks, returning a clean 409.

Sessions run their own machine: scheduled → in_progress → completed, or missed / cancelled.

Shape rules the JSON does not express

  • Two-stage clinical disclosure is enforced server-side. care_instructions/{id} decrypts and returns the care plan only post-confirmation and only to the assigned nurse or an admin. It is never projected into a list and never logged. The client's UI gate mirrors this; it does not create it.
  • EVV is advisory, and checkInAddressMatch is a tri-state: true (inside tolerance), false (outside), null (no reading — permission denied or unavailable). Null is not a failure. A mismatch does not block check-in; it raises a support alert (evv_location_mismatch, see admin.md). Tolerance is evv_location_tolerance_meters config. REQ-015 confirmed the tri-state.
  • BookingDetailDto.variantSnapshotJson and addressSnapshotJson are strings containing JSON, not typed objects — the point of a snapshot is that later edits to the variant or address cannot rewrite history. The client parses defensively across multiple key spellings (REQ-045 open).
  • The three-amount split is guaranteed by a DB CHECK: grossPriceIrr = balinyaarCommissionIrr + nursePayoutAmount. platformFeeRate is snapshotted onto the row at compute time, so a later rate change is not retroactive. Never recompute any of these.
  • payoutEligibleAt per session is the payout clock, started by check-out and resolved against the holiday calendar server-side. See payouts.md.
  • disputeWindowEndsAt gates completed → closed; dispute_window_hours is config.
  • BookingListItemDto is deliberately thin: id, status, counterpartyName, scheduledDate, sessionCount, amountIrr, disputeWindowEndsAt, createdAt. No patientId — which is what blocks REQ-057's care teaser.
  • NEXT_PUBLIC_EVV_MOCK_GPS overrides the GPS reading for local testing (off = real capture). See ../config-matrix.md.

Enums

Vocabulary Values
BookingStatus pending_payment confirmed in_progress completed disputed closed cancelled
BookingSessionStatus scheduled in_progress completed missed cancelled
VisitVerificationStatus (evvStatus) pending checked_in completed
BookingListRole (query param) customer nurse all
EvvGpsMode (client test knob) off in_range out_of_range denied

Verified identical to Baya.Domain/Entities/Booking/*.cs.

Open REQs

REQ Status Effect
REQ-045 open variantSnapshot/addressSnapshot are untyped JSON strings. The client keeps a defensive multi-key parse; no user-facing defect
REQ-051 open The nurse view of a confirmed+ booking still masks the address. The nurse sees a quiet fallback note, not a crash
REQ-052 open The today feed carries no service label — it renders patient name + visit index only
REQ-054 deferred No web push for new requests; a 15 s poll remains the only signal
REQ-057 open BookingListItemDto has no patientId and the patient read has no lastVisitAt, so the card renders no care teaser. See patients.md