Domain 5 — Booking & Scheduling

← Database Model

Related: business requirements — Booking & scheduling.

Two distinct phases: the request phase (pre-payment intent) and the booking phase (post-payment commitment). The previous model's biggest domain gap — single-visit-only bookings — is fixed here with booking_sessions, because elder care is dominantly multi-day / live-in.

booking_requests [CORE]

Role: A customer's pre-payment intent for a nurse, service, date, and time. No money involved. Why separate from bookings: a request may be rejected, expire, or have its payment window lapse without a booking ever existing — merging would mean many nullable fields and tangled status logic. The deadlines are computed once and stored so they're immune to later config changes.

FieldTypeNotes
id, customer_id, nurse_id, patient_id, variant_id, customer_address_idBaseline FKs. Tenancy invariant: patient & address belong to customer_id; variant belongs to nurse_id.
required_caregiver_genderNVARCHAR(10) NULLNEWmale/female/any. Same-gender care is decisive in Iranian bodily-care; surfaced as a first-class filter and matched against nurse gender.
requested_date, requested_time_start, requested_time_endFor multi-day engagements these are the engagement start; sessions carry the per-visit schedule.
customer_notesNVARCHAR(1000) NULLUnencrypted, request-stage only — the only clinical context the nurse sees before accepting (Principle 6).
statusNVARCHAR(50)pending_nurse_responseaccepted_awaiting_paymentconverted / rejected_by_nurse / expired_no_response / payment_deadline_expired / cancelled_by_customer
nurse_response_deadline_atDATETIME2From config at creation; auto-expire after.
payment_deadline_atDATETIME2 NULLSet on accept; 30-min window.
nurse_rejection_reasonNVARCHAR(500) NULL
timestamps

Relations: N:1 → customer_profiles, nurse_profiles, patients, nurse_service_variants, customer_addresses; 1:1 → bookings (on conversion).

bookings [CORE]

Role: The confirmed engagement — exists only when the nurse accepted and payment was captured. Source of truth for the service event and its money split. Why snapshots: variant_snapshot_json and address_snapshot_json freeze the service and address at booking time, so later edits/deletes can't corrupt history or disputes.

FieldTypeNotes
idBIGINT PK
booking_request_idBIGINT FK UNIQUE1:1 — the request that created it
customer_id, nurse_id, patient_id, variant_id, customer_address_idDenormalized for query performance
variant_snapshot_jsonNVARCHAR(MAX)Variant + option labels at booking time
address_snapshot_jsonNVARCHAR(MAX) (enc)Full address at booking time
partner_center_idBIGINT FK → partner_centers NULLNEW — the licensed center legally covering this visit / merchant-of-record
gross_price_irrBIGINTCHANGED (renamed) — total charged to customer
balinyaar_commission_irrBIGINTCHANGED (renamed from platform_fee_amount) — platform's own cut
platform_fee_rateDECIMAL(5,4)Rate snapshot for audit
nurse_payout_amountBIGINT= gross_price_irr − balinyaar_commission_irr. CHECK enforced.
psp_fee_amountBIGINT NULLNEW — gateway cost on this payment, for true margin/reconciliation
session_countSMALLINT NOT NULL DEFAULT 1NEW — 1 = single visit; >1 = multi-session engagement
scheduled_date, scheduled_time_start, scheduled_time_endEngagement-level; per-visit lives in booking_sessions
statusNVARCHAR(30)pending_paymentconfirmedin_progresscompleted → (disputedclosed) / cancelled. Now guarded by an allowed-transition table/CHECK so it can't contradict EVV.
confirmed_at, cancelled_at, cancellation_reason, cancelled_by, completed_at
dispute_window_ends_atDATETIME2 NULLNEWcompleted_at + config(dispute_window_hours, default 72). Payout eligible only after this passes with no open dispute.
payout_releasedCUT — second source of truth for "paid"; now derived from a nurse_payout_booking_links row + ledger.
timestamps

CHECK: gross_price_irr = balinyaar_commission_irr + nurse_payout_amount; all amounts ≥ 0. Relations: 1:1 ← booking_requests; 1:N → booking_sessions, payment_transactions, ledger_entries; 1:1 → booking_care_instructions, visit_verifications (see note), reviews, invoices; referenced by nurse_payout_booking_links, refunds, nurse_clawbacks.

booking_sessions [MVP] — NEW

Role: One row per visit within a booking. A 10-day post-op package or a month of nightly شبانه‌روزی care is one booking with N sessions, each with its own schedule, EVV, and payout eligibility. Why: the single-visit model literally cannot represent the dominant elder-care engagement; and money for a long engagement must release per completed session, not as one whole-month escrow held for weeks. Mid-engagement cancellation then cleanly refunds only the un-started sessions.

FieldTypeNotes
idBIGINT PK
booking_idBIGINT FK → bookings
session_indexINT1-based ordinal
scheduled_date, scheduled_time_start, scheduled_time_endPer-visit
visit_payout_amountBIGINTPortion of nurse_payout_amount for this session
statusNVARCHAR(20)scheduled / in_progress / completed / missed / cancelled
payout_eligible_atDATETIME2 NULLPer-session dispute-window close
cancellation_event_idBIGINT NULLIf this session was cancelled
timestamps

Relations: N:1 → bookings; 1:1 → visit_verifications. Note: for a single-visit booking, exactly one session is created so the EVV/payout path is uniform.

booking_care_instructions [CORE]

Role: Encrypted clinical/logistical context provided at booking time, visible only post-confirmation to the assigned nurse and admin. Why separate + encrypted: keeps the financial/scheduling table clean and enforces the two-stage disclosure boundary (Principle 6) with stricter access controls. Fields unchanged (current conditions, medications, allergies, special instructions, emergency contact — all enc). Relations: 1:1 → bookings.

visit_verifications [CORE]

Role: Electronic Visit Verification — the authoritative record that a visit happened and for how long; required for payout. Why: in-home care is unobserved; GPS + timestamped check-in/out is the proof that safely releases escrow. check_in_address_match is advisory (a mismatch triggers admin review, not auto-cancel). CHANGED: the FK moves to booking_session_id so each visit in a multi-session engagement is verified independently; the mapping between visit_verifications.status and the parent bookings.status is documented so the two state machines cannot silently diverge. Fields otherwise unchanged. Relations: 1:1 → booking_sessions.

cancellation_policies [MVP] — NEW

Role: Config-driven, snapshot-able cancellation/refund tiers by lead time and initiating actor. Why: "default 100% refund" is naive — a free cancel 24h ahead, a 50% charge inside 24h, and a nurse-no-show full refund + nurse penalty are different money flows, and the applicable rule must be frozen onto the booking at cancel time (the same snapshot discipline used for platform_fee_rate).

FieldTypeNotes
idBIGINT PK
codeNVARCHAR(50) UNIQUEe.g. standard_24h
applies_toNVARCHAR(20)customer / nurse / admin
hours_before_start_min, hours_before_start_maxINT NULLTier bounds
refund_percentageDECIMAL(5,2)0–100
fee_amount_or_rateCancellation fee / nurse penalty
is_active, timestamps

Relations: referenced (snapshot) by refunds and the cancellation_event recorded on a session/booking.

↑ Back to top