# 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](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](04-payment-timezone.md)). Resolve consistently with that fix, not ad hoc here.
