blocker phase end

This commit is contained in:
hamid
2026-08-02 23:42:12 +03:30
parent 184b202f00
commit 10d160358f
8 changed files with 95 additions and 30 deletions
+35 -19
View File
@@ -28,7 +28,7 @@ whatever order you prefer.
| 10 | [search-dedup-and-trust](blocker-phases/10-search-dedup-and-trust.md) | Search isn't de-duplicated; trust info hardcoded | pairs with 09 | ✅ Done |
| 11 | [nurse-payouts](blocker-phases/11-nurse-payouts.md) | Nurse pay/payouts are fake, no "process" action | benefits from 01 | — |
| 12 | [patient-records](blocker-phases/12-patient-records.md) | Patient records & visit notes are fake demo data | needs a product decision first | 🟡 Partial (see follow-up below) |
| 13 | [booking-lifecycle](blocker-phases/13-booking-lifecycle.md) | Stuck bookings; "today's visits" unfiltered | pairs with 04 | |
| 13 | [booking-lifecycle](blocker-phases/13-booking-lifecycle.md) | Stuck bookings; "today's visits" unfiltered | pairs with 04 | 🟡 Partial (see follow-up below) |
| 14 | [partner-center](blocker-phases/14-partner-center.md) | Partner/business-center accounts are fake | benefits from 01 | — |
| 15 | [debug-mode-production](blocker-phases/15-debug-mode-production.md) | Turn off dev mode on the live site (§B.2) | do last, deliberately | — |
@@ -47,23 +47,38 @@ whatever order you prefer.
Building the missing preview/retry/reject endpoints needs real design (retry semantics re-executing a
channel call, what "reject" reverses) that isn't specified anywhere — filed as its own future phase, not
guessed here.
- **Same timezone bug as 04, lower severity, not fixed.** Phase 04 fixed `BookingRequest.PaymentDeadlineAt`/
`NurseResponseDeadlineAt` — a `DateTime` (not `DateTimeOffset`) read back from SQL Server's `datetime2`
loses its `Kind` tag (comes back `Unspecified`), so JSON serialization drops the trailing `Z` and a client
`Date.parse()` misreads it as local time. A grep for every other bare `DateTime`/`DateTime?` entity property
found four more real instances, all on `Booking` (`server/src/Core/Baya.Domain/Entities/Booking/Booking.cs`):
`DisputeWindowEndsAt` (:95, rendered via `formatShamsiDate` in
`client/src/components/booking/BookingDetailView/BookingDetailView.tsx:444`), and `ConfirmedAt`/
`CancelledAt`/`CompletedAt` (:73,74,91), booking timeline timestamps shown to the client. Lower severity than
04 — nothing here drives a live countdown, so the failure mode is "can show the wrong calendar day near a
Tehran (UTC+3:30) midnight boundary," not "actively expires while still showing time left." (Everything
else with a bare `DateTime` — payout batches, webhook events, ASP.NET Identity tables — is internal/audit-only
and never reaches a client, so it's excluded.) Fix is the same pattern phase 04 used: apply the existing
`UtcDateTimeConverter` (`server/src/Infrastructure/Baya.Infrastructure.Persistence/ValueConversion/
UtcDateTimeConverter.cs`) to these four properties in `BookingConfig.cs`. No migration needed. Deliberately
left undone — pick up as its own small phase, or fold into whatever eventually addresses 13b's timezone
decision (booking-lifecycle's "today's visits" fix), since both are the same missing
`Asia/Tehran`/UTC-boundary discipline.
- **Same timezone bug as 04, lower severity now fixed, folded into phase 13.** Phase 04 fixed
`BookingRequest.PaymentDeadlineAt`/`NurseResponseDeadlineAt` — a `DateTime` (not `DateTimeOffset`) read back
from SQL Server's `datetime2` loses its `Kind` tag (comes back `Unspecified`), so JSON serialization drops
the trailing `Z` and a client `Date.parse()` misreads it as local time. The same grep found four more real
instances, all on `Booking`: `DisputeWindowEndsAt`, `ConfirmedAt`, `CancelledAt`, `CompletedAt`
(`server/src/Core/Baya.Domain/Entities/Booking/Booking.cs:73,74,91,95`). Applied the existing
`UtcDateTimeConverter` to all four in `BookingConfig.cs`. No migration needed (same column type, only the
in-memory `Kind` tag changes on read).
- **Phase 13 closed both items except one deliberately-deferred edge case.**
- **13a (stuck partial-missed bookings) fixed.** Extracted the `allSettled` check `CheckOutVisitCommand`
already ran after a real check-out into `Booking.IsAllSessionsSettled()`
(`server/src/Core/Baya.Domain/Entities/Booking/Booking.cs`) and called it from
`DetectNoShowSessionsCommand.Handler.cs` after the no-show sweep marks sessions `Missed`, re-checking every
booking touched in that batch. A booking with one completed session and the rest auto-missed now correctly
reaches `Completed` and opens its dispute window (so the nurse's completed-session payout becomes eligible)
instead of staying stuck at `InProgress` until an admin manually rescues it.
**Still open, on purpose — the zero-completed-sessions case (every session auto-missed straight from
`Confirmed`/`InProgress`).** Asked whether that should still reach `Completed` with the nurse paid in full
(today's payout query is booking-level only — `PayoutRepository.EligibleBookingsQuery` pays the whole
`NursePayoutAmount` off `Status`+`DisputeWindowEndsAt`, with no per-session proration, so this would pay a
nurse who did zero visits), reach `Completed` with the payout fields zeroed (a bigger deviation from the
"money snapshot, never mutate" convention), or use a new terminal status with no payout path at all. The
answer was to leave it deferred rather than pick one now — it stays stuck exactly as before, rescuable only
via the admin `TransitionBookingStatusCommand` (`InProgress → Completed`). Revisit once the payout query
itself is made session-aware, or once there's a concrete need to close these out.
- **13b ("today's visits" unfiltered) fixed.** `ListSessionsForNurseQueryHandler` now defaults
`request.Date` to "today" when null, mirroring the mock (`mockApi.ts:438`). "Today" needed a timezone
decision the codebase had never made (no `Asia/Tehran`-aware date logic existed anywhere): added
`TehranClock` (`server/src/Core/Baya.Application/Contracts/Common/TehranClock.cs`), a fixed UTC+03:30
offset (Iran abolished DST in 2022, so no tzdata/`TimeZoneInfo` lookup is needed) — a technical default,
not a business-rule guess, so it wasn't flagged for a decision the way 13a was.
- **Phase 12 closed everything except the medication/routine schema decision, which stays deliberately
unresolved.** Fixed: the silent data-wipe (`patients/[id]/record/page.tsx`'s `EditableTabs.save` now always
@@ -100,6 +115,7 @@ whatever order you prefer.
`super_admin`/`finance` accounts.
2. **Small, contained, no dependencies:** 03, 04, 05, 06.
3. **De-mock passes, each roughly self-contained:** 08, 09+10 together, 11.
4. **Needs a product decision before coding:** 12 (medication/routine schema), 13a (all-missed edge case).
4. **Needs a product decision before coding:** 12 (medication/routine schema), 13a's all-missed edge case
(deferred on purpose — see follow-up above).
5. **14 (partner center)** — the largest single phase, mostly new server surface.
6. **15 (debug mode)** — on its own, right before any real user is let near the site.