2.8 KiB
Phase 13 — Booking lifecycle
Blocker: blockers.md § "Booking lifecycle." Depends on: 13a's edge case needs a product decision (see below); 13b should use whatever timezone approach you land on in 04-payment-timezone.md.
13a. Stuck partial-missed bookings
The domain's own comment
(BookingStatus.cs:19) states Completed means "every session is completed/cancelled/missed" — implying a
mixed completed+missed booking should reach Completed. The only place that actually checks this is
CheckOutVisitCommand.Handler.cs:64-70 (allSettled check, runs after a real check-out). The no-show sweep
(DetectNoShowSessionsCommand.Handler.cs:25-76, run by NoShowSweepJob) marks individual sessions Missed
but never re-checks allSettled afterward — so a booking with one completed session and the rest
auto-missed later gets permanently stuck at InProgress, and PayoutRepository's eligibility query only
considers Status == Completed, so the nurse's completed-session payout never becomes eligible. An admin can
manually rescue this specific case (TransitionBookingStatusCommand.Handler.cs:43-55 allows InProgress → Completed), but nothing does it automatically.
Fix: extract the allSettled check from CheckOutVisitCommand.Handler.cs:64-70 into a shared helper, call
it from DetectNoShowSessionsCommand.Handler.cs after marking sessions Missed, per affected booking in that
sweep batch.
Flag, don't guess: what should happen to a booking where zero sessions were ever completed (all missed
straight from Confirmed)? BookingTransitions.cs has no Confirmed → Completed edge today, and Cancel
is explicitly refused as a substitute (TransitionBookingStatusCommand.Handler.cs:28-29). Nothing in
mvp//archive/product/ answers this — it needs a product decision (does it still reach Completed with a
zero payout, or some other terminal state?) before writing the edge-case code.
13b. "Today's visits" is unfiltered by default
ListSessionsForNurseQuery documents itself as
"today's visits by default," but BookingRepository.cs:185-192 only applies the date filter when date is
explicitly passed — both real client call sites (nurse/visits/page.tsx:26,
NurseDashboardScreen.tsx:85) omit it, so the entire history returns. The mock already implements the
intended default correctly (mockApi.ts:438: date ?? isoDate(0)).
Fix: default request.Date to "today" via IDateTimeProvider inside the handler/repository when null,
mirroring the mock.
Flag: "today" needs a timezone decision (Iran local date vs. UTC day boundary) — there's no
Asia/Tehran-aware date logic anywhere in the codebase yet (same class of gap as
04-payment-timezone.md). Resolve consistently with that fix, not ad hoc here.