Files
baya-monorepo/docs/flows/index.md
T
2026-08-02 17:18:36 +03:30

14 KiB

Flows — what is implemented, and how to test it

One file per user-meaningful journey. Each answers three questions and no others: what it does, what is real versus mocked, and the exact steps to walk it against a running stack.

Start here → testing-setup.md for bring-up, the demo accounts and the OTP. Then pick a row below.

Last verified: 2026-08-02 against commit c841bde, against a booted stack — API on http://localhost:5002 against the shared remote SQL Server, client on http://localhost:3000 (and a production build on :3001), all 8 demo accounts logged in over the real phone-OTP round-trip.


Status table

Client / Server are independent on purpose. A real UI on a mocked service is the trap this atlas exists to expose — six flows below are exactly that.

Flow Actor Status Client Server Gaps File
auth-login-otp all built real real 15
public-front-door guest partial real real 10
onboarding-customer customer partial real real 9
care-circle-patients customer partial partial real 15
addresses-and-map customer partial real real 10
onboarding-nurse nurse partial partial real 11
nurse-service-areas nurse partial real real 8
nurse-verification nurse + admin mocked mock partial 12
nurse-catalog-and-pricing nurse partial real real 9
search-and-discovery customer partial partial real 14
booking-request customer + nurse partial real real 12
checkout-and-payment customer partial partial partial 11
bnpl-installments customer mocked mock partial 12
booking-lifecycle-evv nurse + customer partial real real 13
cancellation-and-refunds customer + admin mocked mock partial 17
reviews customer + admin partial real real 11
patient-care-records nurse + customer mocked mock real 12
nurse-earnings-and-payouts nurse + admin mocked mock partial 18
messaging-tickets all partial partial real 14
notifications all partial real real 13
admin-backoffice admin mocked partial partial 13
partner-center partner mocked mock partial 12
account-and-settings all partial partial real 12

1 built · 15 partial · 7 mocked · 0 not started · 0 UNVERIFIED. 283 gaps recorded — Phase 4's input.

built = end-to-end real and observed working · partial = real end to end with named gaps · mocked = the UI is real, the data is fake · not started = no implementation. Every row is backed by a code trace, and every money flow plus auth was additionally walked against the running API.


The six things that surprise everyone

  1. No seeded admin can reach any admin endpoint. All 16 admin GET operations return 403 for both 09120000020 (super_admin) and 09120000021 (finance). DynamicPermissionService.CanAccess grants on the literal role "admin" or a per-controller DynamicPermission claim; the demo admins hold super_admin/finance, and no code anywhere writes that claim. The client hides it completely because USE_ADMIN_MOCK = true. → admin-backoffice.md
  2. A false mock flag does not mean the domain is honest. payment is flag-real with only 2 of 6 operations working; search hardcodes isVerified: true and nurseGender: 'female'; tickets.getUnreadTotal is literally async () => null.
  3. A true mock flag does not mean the real half is missing. verification has 10 of 14 operations live and probed, admin 9 of 14, payouts 5 of 10 — all suppressed by one shared flag. The nurse's verification status answers correctly to curl right now; nothing in the browser calls it.
  4. The seeded demo world is dated 2026-07-26 and has aged out. No pending or accepted booking request survived the 60-second expiry job, so the nurse inbox and checkout have nothing seeded to act on. → testing-setup.md
  5. No card payment can complete from a browser. MockPaymentProvider redirects to https://mock-psp.local/pay/…, a host that does not exist. The server money path is correct and idempotent; only the last hop is unreachable. → checkout-and-payment.md
  6. The real BNPL rail is dead on the live stack. SeedPaymentGatewaysAsync seeds only a Standard gateway, never a Bnpl one, so POST /checkout_bnpl/initiate returns 400 "No active BNPL gateway is configured." for every request — verified live. The UI never notices because it runs on a mock. → bnpl-installments.md

Mock-vs-real map — client

Derived from code across all 22 client/src/services/ domains, 153 seam operations. Real-half verdict is about the clientApi.ts implementation, independent of the flag: COMPLETE = every op maps a published route with no fabrication · PARTIAL = ≥1 op fabricates or derives a value · GAPPED = ≥1 op targets a route that does not exist server-side.

Domain Flag Ops Real-half verdict live / proposed / fabricated
addresses real 5 COMPLETE 5 / 0 / 0
admin mock 14 GAPPED 9 / 5 / 0
auth real 6 COMPLETE 6 / 0 / 0
bnpl mock 7 GAPPED 2 / 4 / 1
bookingRequests real 6 COMPLETE 6 / 0 / 0
bookings real 7 COMPLETE 7 / 0 / 0
catalog real 7 COMPLETE 7 / 0 / 0
geography real 3 COMPLETE 3 / 0 / 0
notifications real 4 COMPLETE 4 / 0 / 0
nurse (bank) real 4 COMPLETE 4 / 0 / 0
partnerCenter mock 13 GAPPED 6 / 7 / 0
patientRecords mock 5 GAPPED 2 / 3 / 0
patients real 5 COMPLETE 5 / 0 / 0
payment real 6 GAPPED + PARTIAL — worst real domain 2 / 2 / 2
payouts mock 10 GAPPED + PARTIAL 5 / 4 / 1
profiles real 6 COMPLETE 6 / 0 / 0
refunds mock 9 GAPPED 2 / 7 / 0
reviews real 6 COMPLETE 6 / 0 / 0
search real 2 PARTIAL 2 / 0 / 3 fields
serviceAreas real 3 COMPLETE 3 / 0 / 0
tickets real 11 PARTIAL + GAPPED (gated) 7 / 3 / 1
verification mock 14 GAPPED + PARTIAL 10 / 3 / 1

Totals: 15 real / 7 mock · ~107 ops map published routes · 34 would 404 if the flag were flipped today · 12 fabricate or client-derive a value.

Three further facts a reader needs:

  • 20 of 22 mocks hold mutable module state — a mocked demo that crosses a full page navigation restarts from its seed.
  • Two cross-domain mock edges break live paths: the refunds and bnpl mocks read the bookings mock store, which is seeded only with ids 5001-5005, and findBooking throws 404 for anything else. Real booking ids therefore 404 inside those wizards.
  • One production seam breach: nurse/verification/page.tsx:12 imports __mockApproveAll/__mockRejectStep from apis/mockApi unconditionally. The render is gated; the module edge is not, so the mock ships in every build.

Mock-vs-real map — server seams

Exactly one Seams:*:Provider is set anywhere in the repo — Seams:Sms:Provider = telegram. Every other rail runs on its mock, in development and in the deployed stack: docker-compose.yml overrides only the relay's BaseUrl and the object-storage RootPath, never a selector.

Rail Mock Real adapter available Selected today
SMS / OTP LoggingSmsSender KavenegarSmsSender · TelegramSmsSender telegram
Card PSP MockPaymentProvider ZarinPalPaymentProvider mock
Settlement split (تسهیم) MockSettlementSplitProvider ProviderSettlementSplitProvider mock
BNPL MockBnplProvider SnappPayBnplProvider · DigipayBnplProvider mock
Object storage LocalDiskObjectStorage S3ObjectStorage mock
Geocoder MockGeocoder NeshanGeocoder mock
Bank transfer (payouts) MockBankTransferProvider none mock — moves no money
e-invoicing (مودیان) MockMoadianClient MoadianClient mock
Shahkar · e-KYC · IBAN ownership mocks Finnotech adapters mock
Credential (MoH/INO) · eNamad · review moderation mocks none — deliberate mock, always
Search (no mock) SqlNurseSearch real

Two rails throw at startup instead of falling back to the mock: Seams:Sms:Provider = smsir|ghasedak, and Search:Backend set to anything but sql. And the PSP selector is not a token match — ServiceCollectionExtension.cs:174 treats any non-mock string as ZarinPal, so a typo silently selects a real gateway.

Corrections to mocks-registry.md

dev/shared-working-context/reports/mocks-registry.md was cross-checked row by row against the code: the whole frontend section — 26 rows plus its prose header — and 17 disagreements were found. The registry now carries a banner pointing here.

The systemic cause: its "Config flag … default true" column was never updated after the refinement-phase-4 de-mock, although the same file's prose header and Status column were.

Kind Count Detail
Stale flag default 8 patients, profiles, nurse (bank), geography, addresses, serviceAreas, catalog, reviews, notifications rows all still say "default true"; the code says false
Actively wrong about behaviour 3 profilesClientApi.uploadAvatar "throws 501" — it does a real multipart upload · AddressMapPicker "not a real map, no Neshan tiles, no network" — it renders real Leaflet Neshan tiles when the key is set · the payment row records the flip as clean and never records the 4 broken ops it produced
Wrong in both directions at once 1 One row's flag column says all-mock and its own Status column says all-real; reality is 2 real / 3 mock
Missing entirely 3 SearchApi, BookingRequestsApi (the exact store behind the BNPL breakage), and geography/neshan.ts's direct third-party fetch calls
Internal contradiction 1 The registry says the REQ-027 endpoints exist; patientRecords/apis/clientApi.ts says none exist. Needs a server-side ruling
Verified and holding 15+ Including all 7 remaining flag values, the EVV GPS default, and the deleted card mock-gateway harness

Backend seam rows were not re-audited row-by-row; the current server picture is the seam table above.


Coverage accounting

All 14 business areas are covered. Each maps to a primary flow; none is orphaned.

Area Primary flow Area Primary flow
01 Actors & Onboarding auth-login-otp, onboarding-* 08 Payments & Escrow checkout-and-payment
02 Nurse Verification nurse-verification 09 Installments / BNPL bnpl-installments
03 Catalog & Pricing nurse-catalog-and-pricing 10 Payouts nurse-earnings-and-payouts
04 Search & Matching search-and-discovery 11 Reviews, Trust & Safety reviews
05 Booking & Scheduling booking-request, booking-lifecycle-evv 12 Messaging & Emergencies messaging-tickets
06 EVV / Service Delivery booking-lifecycle-evv 13 Tax, Invoicing & Legal partner-centerweakest
07 Cancellation & Refunds cancellation-and-refunds 14 Notifications & Admin notifications, admin-backoffice

Area 13 is the thin one. Invoicing and VAT are split across checkout-and-payment (the per-booking invoice) and admin-backoffice (the مودیان reconciliation); no flow owns the legal/tax story end to end. Phase 4 backlog item.

All 22 client service domains are covered by at least one flow (see the map above). All 83 client routes are reachable from a flow file's Screens table; the 9 routes with no service domain are static pages, and the orphans (nothing links to them) are recorded in the flow that owns their area.

One flow has no product source: account-and-settings is a UI-phase-9 decision with no product/business/ file behind it. public-front-door's depth decision lives in product/notes/open-questions.md rather than a business area.


Conventions these files follow

  1. Every file carries > Last verified: <date> against <commit>. A file without one is a claim, not a fact.
  2. Every status is backed by a code trace (route → hook → seam → endpoint → controller → handler) with file:line evidence. The money flows and auth were additionally walked against the running API.
  3. What could not be checked is written with an explicit UNVERIFIED: prefix and a reason.
  4. API shapes are not restated heredocs/integration/ owns them, and every flow links to its domain file. Business rules are not restatedproduct/ owns them; a flow states the number and links to its source.
  5. Each file stays under 200 lines. testing-setup.md is a deliberate exception: it is the single page you hand someone, and splitting it would defeat that.