6.4 KiB
6.4 KiB
Backend phase 9 report — Bookings, sessions, care instructions & EVV
What was built
- Domain (
Baya.Domain/Entities/Booking/):Booking,BookingSession,BookingCareInstruction,VisitVerification,CancellationPolicy(+CancellationPolicyCodeseed codes), theBookingStatus/BookingSessionStatus/VisitVerificationStatus/CancellationActorcode sets, theBookingTransitions/BookingSessionTransitionsguards, andBookingAmounts(the pure integer-money split/reconciliation). - Persistence: one migration
BookingsSessionsEvvCancellation— the fivebooking-schema tables with thegross = commission + payout(all ≥ 0) DB CHECK, thebooking_request_id/booking_id(care) /booking_session_id(EVV) UNIQUE 1:1 indexes, encryptedaddress_snapshot_json+ care columns, seeded cancellation tiers + 2 newplatform_configsrows.BookingConfig/configs;BookingRepository+CancellationPolicyRepositoryonIUnitOfWork;IBookingRequestRepositorygainedGetTrackedByIdAsync+GetConversionSourceAsync. - Application (
Features/Bookings/):ConvertRequestToBooking,SubmitCareInstructions,CheckInVisit,CheckOutVisit,TransitionBookingStatus,CancelBooking,CancelSession,DetectNoShowSessions,UpsertCancellationPolicy(commands) +GetBookingDetail,ListBookings,ListSessionsForNurse,GetCareInstructions,GetVisitVerification,ListAdminEvv,ListCancellationPolicies(queries), plusBookingMapper,CancellationHelper,GeoDistance. - Seam:
IPaymentCaptureSimulator(ApplicationContracts/Common) +MockPaymentCaptureSimulator(CrossCutting), registered inAddCrossCuttingSeams, configSeams:PaymentCapture. - API:
BookingsController,BookingSessionsController,AdminEvvController,AdminCancellationPoliciesController(convert/cancel + admin EVV/policies are rate-limited).
What is now testable, and exactly how (per §7 of the phase)
- Convert —
POST bookings/convert(as the owning customer) with anaccepted_awaiting_paymentrequest → aconfirmedbooking whose three amounts sum, snapshots are populated (address encrypted at rest), N sessions reconcile (Σ visit_payout = nurse_payout), request →converted. Re-convert → same booking. - Single-visit — a
session_count=1request → exactly one session. - Care disclosure — submit as customer, then
GET bookings/care_instructions/{id}: assigned nurse + admin get the decrypted fields; customer / unassigned nurse / pre-confirmation → 404. Never in list/detail. - EVV —
check_in(in-range GPS) → session + bookingin_progress;check_out→ sessioncompleted, EVVcompleted. - Mismatch —
check_inout-of-range → still succeeds,check_in_address_match=false, aevv_location_mismatchsupport alert + notification; visible inadmin_evv/list?type=mismatch. - Completion — last
check_out→ bookingcompleted,dispute_window_ends_at = completed_at + 72h(config), each completed session'spayout_eligible_atset; not payout-eligible before that. - Cancellation —
bookings/cancel/{id}resolves the tier by lead-time + actor, snapshotscode+refund_percentage, refunds only un-started sessions; a later policy edit leaves the snapshot unchanged. - Transition guard — an illegal/EVV-contradicting transition →
OperationResultfailure, no state change. - No-show —
admin_evv/detect_no_showsfor an overdue scheduled session →missed+no_showalert + family notification.
Automated coverage: 42 booking foundation tests (SQLite host over the real EF model + real handlers) — the
three-amount split + session reconciliation, the transition guards, the two-stage disclosure gate, the
advisory-mismatch-raises-alert-without-blocking path, SetDisputeWindow on completion, and the
policy-snapshot immutability — plus one WebApplicationFactory integration test per controller (happy path /
401 / 400 / disclosure not-found). Full suite green (269 tests). dotnet build zero new code warnings.
What is mocked, and how to make it real
IPaymentCaptureSimulator— seereports/mocks-registry.md. In b10 the real card capture callsConvertRequestToBookingdirectly on apayment_transactions.succeeded; remove the seam + mock. A config switch forces a failed capture today so the "no booking on failure" path is covered.
Decisions recorded (not in the product docs before)
- The
visit_payout_amountsplit places the remainder of integer division on the last session soΣ = nurse_payout_amountexactly. - The EVV-state ↔ booking-state mapping (
checked_in↔ sessionin_progress↔ bookingin_progress; all sessions settled ↔ bookingcompleted). - Seeded cancellation tiers:
standard_24h(customer ≥24h → 100%),standard_inside_24h(customer <24h → 50%, open lower bound so an already-started cancel still resolves),nurse_no_show(nurse → 100% + a modelled penalty whose posting is deferred to b13),admin_cancellation(admin → 100%). no_show_threshold_minutesdefault 60;no_show_scan_cadence_hoursdefault 1.- The cancellation snapshot (
cancellation_policy_code/cancellation_refund_percentage/refundable_amount_irr) lives on thebookingsrow for MVP — the typed per-event/refund record lands in b11;booking_sessions.cancellation_event_idis a nullable column left unset until then.
Contracts produced / consumed
- Produced:
dev/contracts/domains/bookings-evv.md+ refreshedswagger.v1.json. Consumes b8'sbooking_requests, b5'sIVariantSnapshotSerializer, b4'sIGeocoder+ address coords, b1's config /support_alerts/INotificationDispatcher, b0'sIFieldEncryptor/ICurrentUser/OperationResult.
Follow-ups
- b10 — real card capture (
payment_transactions, ledger) → replaces theIPaymentCaptureSimulatortrigger. - b11 — refund execution consumes the frozen policy snapshot +
refundable_amount_irr; adds the cancellation-event/refund records thatbooking_sessions.cancellation_event_idwill reference. - b13 — payout batching consumes
dispute_window_ends_at/payout_eligible_at; posts the nurse penalty. - b14 — reviews on a completed booking. b15 —
partner_centerswirespartner_center_id. - The no-show cron and a recurring dispute-window/close sweep remain DEFERRED (hosted-scheduler pattern).