Files
2026-08-02 23:42:12 +03:30

122 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Fix plan — how to close every item in blockers.md
An index into [`blocker-phases/`](blocker-phases/), an engineering-facing companion to
[blockers.md](blockers.md): one numbered file per fix, in a suggested working order. Each file has the exact
root cause (file:line), the proposed fix, and anywhere the fix depends on a business-rule decision that
shouldn't be guessed. Written from a full read of the current code (commit `fb58ca5`, 2026-08-02). Phases 01
and 02 are applied (see Status column); the rest are unapplied — work through them at your own pace, in
whatever order you prefer.
> **Two blockers already got a scope decision, recorded so it isn't re-litigated:** §B.4 (مودیان
> e-invoicing) gets a note in [forgotten-features.md](forgotten-features.md) instead of a build — no chosen
> provider to integrate against. §B.6 (bank-transfer payout rail) stays mocked — the seam already exists, see
> phase 11.
## Phases
| # | File | Blocker | Depends on | Status |
| --- | --- | --- | --- | --- |
| 01 | [admin-rbac](blocker-phases/01-admin-rbac.md) | Admin can't do anything (the #1 leverage item) | — | ✅ Done |
| 02 | [reviews-moderation](blocker-phases/02-reviews-moderation.md) | Reviews can never go live | 01 | ✅ Done (no separate fix needed — unblocked by 01) |
| 03 | [address-edit-bug](blocker-phases/03-address-edit-bug.md) | Editing an address wipes recipient name/phone | — | ✅ Done |
| 04 | [payment-timezone](blocker-phases/04-payment-timezone.md) | The 30-minute payment countdown can lie | — | ✅ Done (follow-up filed below) |
| 05 | [bnpl-setup](blocker-phases/05-bnpl-setup.md) | Installments (BNPL) don't work at all | — | — |
| 06 | [catalog-admin-page](blocker-phases/06-catalog-admin-page.md) | No admin page for service categories/pricing | — | — |
| 07 | [card-payment-redirect](blocker-phases/07-card-payment-redirect.md) | Card payment can never complete | — | ✅ Done (follow-up filed below) |
| 08 | [refunds-demock](blocker-phases/08-refunds-demock.md) | Refunds are demo-only, off by 100× | — | ✅ Done (customer surface only, follow-up filed below) |
| 09 | [nurse-verification-badge](blocker-phases/09-nurse-verification-badge.md) | Verification badge doesn't reflect reality | pairs with 10 | ✅ Done |
| 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 | 🟡 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 | — |
## Follow-ups filed (not yet phases of their own)
- **Phase 08 closed the customer-facing refund surface only — the admin refund console stays mocked.**
`AdminRefundsController` only implements create-and-execute (`POST admin_refunds`, matching
`initiateRefund`); there is no real read-only preview, no retry/approve, and no reject route. The phase
doc's own read ("real server side is live and correct") only checked the customer half
(`RefundsController`, `CreateRefundCommand`, `GetCancellationPolicyPreviewQuery`) — the admin gaps weren't
called out and would have 404'd the console had the single mock flag been flipped wholesale. Split into
two independently-selected flags instead (`client/src/services/refunds/constants.ts`):
`USE_CUSTOMER_REFUNDS_MOCK = false` (real — closes the actual ×100 money-safety bug) and
`USE_ADMIN_REFUNDS_MOCK = true` (stays mocked; the four admin methods are kept together rather than mixed,
since a real `initiateRefund` executing against a mocked preview's numbers would be actively dangerous).
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 — 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
resends the full `medications`/`routine`/`tasks` triple, not just the tab being edited — the real
`UpsertCarePlanCommand` fully replaces all three from the request, no server-side merge); the
`RecordAccess.deniedReason` enum (client now matches the server's real `'not_authorized' | 'not_found'`,
was `'no_access' | 'not_found'`); and the visit-note `taskResults` mapper bug (`clientApi.ts` was hardcoding
`taskResults: []` on read and folding the checklist into free text on write, even though `CareRecordDto`
already serves a structured `TaskResults` array and `WriteCareRecordBody` already accepts one — both sides
now pass the array through directly). Also corrected the stale "no backend at all" comments in
`constants.ts`/`types.ts`/`clientApi.ts`/`mockApi.ts``PatientCareRecordsController` implements every
route this domain needs.
**Still open, on purpose:** the client's medication/routine shape (structured dose amount/unit, frequency
preset codes, a multi-select `timeOfDay`) doesn't match the server's (one free-text dose, one required
frequency string, no `timeOfDay` on either medications or routine items) — see phase 12's items #3/#4. Asked
which side should change; the answer was to leave both as they are for now and just keep this note so it
isn't lost. `USE_PATIENT_RECORDS_MOCK` therefore stays `true`, and the id-type mismatch (client `string` ids
vs. the wire's `long`, phase 12 item #2) is left unfixed too — it can't be finished without first knowing
the target field shape, since `getFamilyRecord`/`updateFamilyRecord` would need a real mapping function
between the two, not just an id conversion.
- **ZarinPal-callback translator, latent until a real gateway is switched on.** Phase 07 restored the
mock-gateway harness and pointed `MockPaymentProvider.InitPaymentAsync`'s `redirectUrl` at it (relative
URL, no more dead `mock-psp.local` host) — the card-payment dead end (blockers.md § "Payments") is closed
for the mock path, which is the only path reachable pre-e-namad (§B.5). Once a real acquirer is ever
switched on, ZarinPal's callback redirects the browser with its **own** query params (`Authority`,
`Status`) — nothing under `Controllers/V1` today translates that into what `checkout/return/page.tsx`
expects (`request_id`/`transaction_id`/`outcome`). Needs a translating endpoint/route before the real path
can work; deliberately out of scope of the mock fix, and moot until §B.5 clears.
## Suggested order
1. **01 (admin RBAC)** — unlocks 02, and makes 09/10/11/14 independently testable via the seeded
`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'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.