clean and refine product docs structure
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Domain 5 — Booking & Scheduling — Balinyaar docs</title>
|
||||
<link rel="stylesheet" href="../assets/doc.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
<aside class="sidebar">
|
||||
<a class="brand" href="../index.html"><span class="dot"></span> Balinyaar docs</a>
|
||||
<p class="tagline">Trust-first home-nursing marketplace · Iran</p>
|
||||
<nav><div class="group"><div class="label">Start here</div><ul><li><a href="../index.html">Docs home</a></li><li><a href="../overview/platform-summary.html">Platform summary & ground truths</a></li></ul></div><div class="group"><div class="label">Business requirements</div><ul><li><a href="../business/index.html">Overview & MVP scope</a></li><li><a href="../business/01-actors-and-onboarding.html">1. Actors & onboarding</a></li><li><a href="../business/02-nurse-verification.html">2. Nurse verification</a></li><li><a href="../business/03-service-catalog-and-pricing.html">3. Service catalog & pricing</a></li><li><a href="../business/04-search-and-matching.html">4. Search & matching</a></li><li><a href="../business/05-booking-and-scheduling.html">5. Booking & scheduling</a></li><li><a href="../business/06-evv-and-service-delivery.html">6. EVV / service delivery</a></li><li><a href="../business/07-cancellation-and-refunds.html">7. Cancellation & refunds</a></li><li><a href="../business/08-payments-and-escrow.html">8. Payments & escrow</a></li><li><a href="../business/09-installments-bnpl.html">9. Installments / BNPL</a></li><li><a href="../business/10-payouts.html">10. Payouts to nurses</a></li><li><a href="../business/11-reviews-trust-and-safety.html">11. Reviews, trust & safety</a></li><li><a href="../business/12-messaging-and-emergencies.html">12. Messaging & emergencies</a></li><li><a href="../business/13-tax-invoicing-and-legal.html">13. Tax, invoicing & legal</a></li><li><a href="../business/14-notifications-and-admin.html">14. Notifications & admin</a></li></ul></div><div class="group"><div class="label">Database model</div><ul><li><a href="index.html">Overview & decisions</a></li><li><a href="diagrams.html">Diagrams</a></li><li><a href="01-identity-and-access.html">1. Identity & access</a></li><li><a href="02-geography.html">2. Geography</a></li><li><a href="03-services-and-pricing.html">3. Services & pricing</a></li><li><a href="04-verification-and-credentials.html">4. Verification & credentials</a></li><li><a class="active" href="05-booking-and-scheduling.html">5. Booking & scheduling</a></li><li><a href="06-payments-ledger-and-refunds.html">6. Payments, ledger & refunds</a></li><li><a href="07-payouts.html">7. Payouts</a></li><li><a href="08-bnpl.html">8. BNPL / installments</a></li><li><a href="09-messaging.html">9. Messaging</a></li><li><a href="10-reviews-and-records.html">10. Reviews & records</a></li><li><a href="11-notifications.html">11. Notifications</a></li><li><a href="12-audit-config-and-reference.html">12. Audit, config & reference</a></li><li><a href="13-partner-centers-and-future.html">13. Partner centers & future</a></li></ul></div><div class="group"><div class="label">Payments deep-dive</div><ul><li><a href="../payments/index.html">Overview & exec summary</a></li><li><a href="../payments/iranian-payment-reality.html">Iranian payment reality</a></li><li><a href="../payments/escrow-ledger.html">Escrow as a ledger</a></li><li><a href="../payments/bnpl-landscape.html">BNPL landscape & finding</a></li><li><a href="../payments/cancellation-and-payout.html">Cancellation & nurse payout</a></li><li><a href="../payments/integration-notes.html">Integration & schema touchpoints</a></li><li><a href="../payments/sources.html">Recommendations & sources</a></li></ul></div><div class="group"><div class="label">Research & strategy</div><ul><li><a href="../research/index.html">Overview & exec summary</a></li><li><a href="../research/market-and-competitors.html">Market & competitors</a></li><li><a href="../research/problems-and-risks.html">Problems & risks</a></li><li><a href="../research/verification.html">Verification (research)</a></li><li><a href="../research/legal-landscape.html">Legal landscape</a></li><li><a href="../research/go-to-market.html">Go-to-market & sources</a></li></ul></div><div class="group"><div class="label">Notes & more</div><ul><li><a href="../notes/open-questions.html">Open questions</a></li><li><a href="../notes/future-ideas.html">Future ideas</a></li><li><a href="../wireframes/index.html">Wireframes</a></li><li><a href="../fa/index.html">Farsi documents</a></li></ul></div></nav>
|
||||
</aside>
|
||||
<main class="main"><div class="content">
|
||||
<div class="topbar"><button class="theme-toggle" type="button" onclick="__t()">theme</button></div>
|
||||
<h1 id="domain-5-booking-scheduling">Domain 5 — Booking & Scheduling</h1>
|
||||
<p><a href="index.html">← Database Model</a></p>
|
||||
<p><strong>Related:</strong> business requirements — <a href="../business/05-booking-and-scheduling.html">Booking & scheduling</a>.</p>
|
||||
<p>Two distinct phases: the <strong>request phase</strong> (pre-payment intent) and the <strong>booking phase</strong> (post-payment commitment). The previous model's biggest domain gap — <strong>single-visit-only bookings</strong> — is fixed here with <code>booking_sessions</code>, because elder care is dominantly multi-day / live-in.</p>
|
||||
<h3 id="booking_requests-core"><code>booking_requests</code> [CORE] <a class="anchor" href="#booking_requests-core" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> A customer's pre-payment intent for a nurse, service, date, and time. No money involved. <strong>Why separate from <code>bookings</code>:</strong> 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 <strong>computed once and stored</strong> so they're immune to later config changes.</p>
|
||||
<div class="table-wrap"><table><thead><tr><th>Field</th><th>Type</th><th>Notes</th></tr></thead><tbody>
|
||||
<tr><td><code>id</code>, <code>customer_id</code>, <code>nurse_id</code>, <code>patient_id</code>, <code>variant_id</code>, <code>customer_address_id</code></td><td>…</td><td>Baseline FKs. <strong>Tenancy invariant:</strong> patient & address belong to <code>customer_id</code>; variant belongs to <code>nurse_id</code>.</td></tr>
|
||||
<tr><td><code>required_caregiver_gender</code></td><td>NVARCHAR(10) NULL</td><td><strong>NEW</strong> — <code>male</code>/<code>female</code>/<code>any</code>. Same-gender care is decisive in Iranian bodily-care; surfaced as a first-class filter and matched against nurse gender.</td></tr>
|
||||
<tr><td><code>requested_date</code>, <code>requested_time_start</code>, <code>requested_time_end</code></td><td>…</td><td>For multi-day engagements these are the engagement start; sessions carry the per-visit schedule.</td></tr>
|
||||
<tr><td><code>customer_notes</code></td><td>NVARCHAR(1000) NULL</td><td><strong>Unencrypted, request-stage only</strong> — the <em>only</em> clinical context the nurse sees before accepting (Principle 6).</td></tr>
|
||||
<tr><td><code>status</code></td><td>NVARCHAR(50)</td><td><code>pending_nurse_response</code> → <code>accepted_awaiting_payment</code> → <code>converted</code> / <code>rejected_by_nurse</code> / <code>expired_no_response</code> / <code>payment_deadline_expired</code> / <code>cancelled_by_customer</code></td></tr>
|
||||
<tr><td><code>nurse_response_deadline_at</code></td><td>DATETIME2</td><td>From config at creation; auto-expire after.</td></tr>
|
||||
<tr><td><code>payment_deadline_at</code></td><td>DATETIME2 NULL</td><td>Set on accept; 30-min window.</td></tr>
|
||||
<tr><td><code>nurse_rejection_reason</code></td><td>NVARCHAR(500) NULL</td><td></td></tr>
|
||||
<tr><td>timestamps</td><td>…</td><td></td></tr>
|
||||
</tbody></table></div>
|
||||
<p><strong>Relations:</strong> N:1 → <code>customer_profiles</code>, <code>nurse_profiles</code>, <code>patients</code>, <code>nurse_service_variants</code>, <code>customer_addresses</code>; 1:1 → <code>bookings</code> (on conversion).</p>
|
||||
<h3 id="bookings-core"><code>bookings</code> [CORE] <a class="anchor" href="#bookings-core" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> The confirmed engagement — exists <strong>only</strong> when the nurse accepted <strong>and</strong> payment was captured. Source of truth for the service event and its money split. <strong>Why snapshots:</strong> <code>variant_snapshot_json</code> and <code>address_snapshot_json</code> freeze the service and address at booking time, so later edits/deletes can't corrupt history or disputes.</p>
|
||||
<div class="table-wrap"><table><thead><tr><th>Field</th><th>Type</th><th>Notes</th></tr></thead><tbody>
|
||||
<tr><td><code>id</code></td><td>BIGINT PK</td><td></td></tr>
|
||||
<tr><td><code>booking_request_id</code></td><td>BIGINT FK UNIQUE</td><td>1:1 — the request that created it</td></tr>
|
||||
<tr><td><code>customer_id</code>, <code>nurse_id</code>, <code>patient_id</code>, <code>variant_id</code>, <code>customer_address_id</code></td><td>…</td><td>Denormalized for query performance</td></tr>
|
||||
<tr><td><code>variant_snapshot_json</code></td><td>NVARCHAR(MAX)</td><td>Variant + option labels at booking time</td></tr>
|
||||
<tr><td><code>address_snapshot_json</code></td><td>NVARCHAR(MAX) (enc)</td><td>Full address at booking time</td></tr>
|
||||
<tr><td><code>partner_center_id</code></td><td>BIGINT FK → partner_centers NULL</td><td><strong>NEW</strong> — the licensed center legally covering this visit / merchant-of-record</td></tr>
|
||||
<tr><td><code>gross_price_irr</code></td><td>BIGINT</td><td><strong>CHANGED (renamed)</strong> — total charged to customer</td></tr>
|
||||
<tr><td><code>balinyaar_commission_irr</code></td><td>BIGINT</td><td><strong>CHANGED (renamed from <code>platform_fee_amount</code>)</strong> — platform's own cut</td></tr>
|
||||
<tr><td><code>platform_fee_rate</code></td><td>DECIMAL(5,4)</td><td>Rate snapshot for audit</td></tr>
|
||||
<tr><td><code>nurse_payout_amount</code></td><td>BIGINT</td><td><code>= gross_price_irr − balinyaar_commission_irr</code>. <strong>CHECK enforced.</strong></td></tr>
|
||||
<tr><td><code>psp_fee_amount</code></td><td>BIGINT NULL</td><td><strong>NEW</strong> — gateway cost on this payment, for true margin/reconciliation</td></tr>
|
||||
<tr><td><code>session_count</code></td><td>SMALLINT NOT NULL DEFAULT 1</td><td><strong>NEW</strong> — 1 = single visit; >1 = multi-session engagement</td></tr>
|
||||
<tr><td><code>scheduled_date</code>, <code>scheduled_time_start</code>, <code>scheduled_time_end</code></td><td>…</td><td>Engagement-level; per-visit lives in <code>booking_sessions</code></td></tr>
|
||||
<tr><td><code>status</code></td><td>NVARCHAR(30)</td><td><code>pending_payment</code> → <code>confirmed</code> → <code>in_progress</code> → <code>completed</code> → (<code>disputed</code> → <code>closed</code>) / <code>cancelled</code>. <strong>Now guarded by an allowed-transition table/CHECK</strong> so it can't contradict EVV.</td></tr>
|
||||
<tr><td><code>confirmed_at</code>, <code>cancelled_at</code>, <code>cancellation_reason</code>, <code>cancelled_by</code>, <code>completed_at</code></td><td>…</td><td></td></tr>
|
||||
<tr><td><code>dispute_window_ends_at</code></td><td>DATETIME2 NULL</td><td><strong>NEW</strong> — <code>completed_at + config(dispute_window_hours, default 72)</code>. Payout eligible only after this passes with no open dispute.</td></tr>
|
||||
<tr><td><del><code>payout_released</code></del></td><td>—</td><td><strong>CUT</strong> — second source of truth for "paid"; now derived from a <code>nurse_payout_booking_links</code> row + ledger.</td></tr>
|
||||
<tr><td>timestamps</td><td>…</td><td></td></tr>
|
||||
</tbody></table></div>
|
||||
<p>CHECK: <code>gross_price_irr = balinyaar_commission_irr + nurse_payout_amount</code>; all amounts ≥ 0. <strong>Relations:</strong> 1:1 ← <code>booking_requests</code>; 1:N → <code>booking_sessions</code>, <code>payment_transactions</code>, <code>ledger_entries</code>; 1:1 → <code>booking_care_instructions</code>, <code>visit_verifications</code> <em>(see note)</em>, <code>reviews</code>, <code>invoices</code>; referenced by <code>nurse_payout_booking_links</code>, <code>refunds</code>, <code>nurse_clawbacks</code>.</p>
|
||||
<h3 id="booking_sessions-mvp-new"><code>booking_sessions</code> [MVP] — <strong>NEW</strong> <a class="anchor" href="#booking_sessions-mvp-new" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> One row per <strong>visit</strong> within a booking. A 10-day post-op package or a month of nightly شبانهروزی care is <strong>one booking with N sessions</strong>, each with its own schedule, EVV, and payout eligibility. <strong>Why:</strong> the single-visit model literally cannot represent the dominant elder-care engagement; and money for a long engagement must release <strong>per completed session</strong>, not as one whole-month escrow held for weeks. Mid-engagement cancellation then cleanly refunds only the un-started sessions.</p>
|
||||
<div class="table-wrap"><table><thead><tr><th>Field</th><th>Type</th><th>Notes</th></tr></thead><tbody>
|
||||
<tr><td><code>id</code></td><td>BIGINT PK</td><td></td></tr>
|
||||
<tr><td><code>booking_id</code></td><td>BIGINT FK → bookings</td><td></td></tr>
|
||||
<tr><td><code>session_index</code></td><td>INT</td><td>1-based ordinal</td></tr>
|
||||
<tr><td><code>scheduled_date</code>, <code>scheduled_time_start</code>, <code>scheduled_time_end</code></td><td>…</td><td>Per-visit</td></tr>
|
||||
<tr><td><code>visit_payout_amount</code></td><td>BIGINT</td><td>Portion of <code>nurse_payout_amount</code> for this session</td></tr>
|
||||
<tr><td><code>status</code></td><td>NVARCHAR(20)</td><td><code>scheduled</code> / <code>in_progress</code> / <code>completed</code> / <code>missed</code> / <code>cancelled</code></td></tr>
|
||||
<tr><td><code>payout_eligible_at</code></td><td>DATETIME2 NULL</td><td>Per-session dispute-window close</td></tr>
|
||||
<tr><td><code>cancellation_event_id</code></td><td>BIGINT NULL</td><td>If this session was cancelled</td></tr>
|
||||
<tr><td>timestamps</td><td>…</td><td></td></tr>
|
||||
</tbody></table></div>
|
||||
<p><strong>Relations:</strong> N:1 → <code>bookings</code>; 1:1 → <code>visit_verifications</code>. <strong>Note:</strong> for a single-visit booking, exactly one session is created so the EVV/payout path is uniform.</p>
|
||||
<h3 id="booking_care_instructions-core"><code>booking_care_instructions</code> [CORE] <a class="anchor" href="#booking_care_instructions-core" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> Encrypted clinical/logistical context provided at booking time, visible <strong>only post-confirmation</strong> to the assigned nurse and admin. <strong>Why separate + encrypted:</strong> 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). <strong>Relations:</strong> 1:1 → <code>bookings</code>.</p>
|
||||
<h3 id="visit_verifications-core"><code>visit_verifications</code> [CORE] <a class="anchor" href="#visit_verifications-core" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> Electronic Visit Verification — the authoritative record that a visit happened and for how long; <strong>required for payout</strong>. <strong>Why:</strong> in-home care is unobserved; GPS + timestamped check-in/out is the proof that safely releases escrow. <code>check_in_address_match</code> is advisory (a mismatch triggers admin review, not auto-cancel). <strong>CHANGED:</strong> the FK moves to <strong><code>booking_session_id</code></strong> so each visit in a multi-session engagement is verified independently; the mapping between <code>visit_verifications.status</code> and the parent <code>bookings.status</code> is documented so the two state machines cannot silently diverge. Fields otherwise unchanged. <strong>Relations:</strong> 1:1 → <code>booking_sessions</code>.</p>
|
||||
<h3 id="cancellation_policies-mvp-new"><code>cancellation_policies</code> [MVP] — <strong>NEW</strong> <a class="anchor" href="#cancellation_policies-mvp-new" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> Config-driven, snapshot-able cancellation/refund tiers by lead time and initiating actor. <strong>Why:</strong> "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 <strong>frozen onto the booking</strong> at cancel time (the same snapshot discipline used for <code>platform_fee_rate</code>).</p>
|
||||
<div class="table-wrap"><table><thead><tr><th>Field</th><th>Type</th><th>Notes</th></tr></thead><tbody>
|
||||
<tr><td><code>id</code></td><td>BIGINT PK</td><td></td></tr>
|
||||
<tr><td><code>code</code></td><td>NVARCHAR(50) UNIQUE</td><td>e.g. <code>standard_24h</code></td></tr>
|
||||
<tr><td><code>applies_to</code></td><td>NVARCHAR(20)</td><td><code>customer</code> / <code>nurse</code> / <code>admin</code></td></tr>
|
||||
<tr><td><code>hours_before_start_min</code>, <code>hours_before_start_max</code></td><td>INT NULL</td><td>Tier bounds</td></tr>
|
||||
<tr><td><code>refund_percentage</code></td><td>DECIMAL(5,2)</td><td>0–100</td></tr>
|
||||
<tr><td><code>fee_amount_or_rate</code></td><td>…</td><td>Cancellation fee / nurse penalty</td></tr>
|
||||
<tr><td><code>is_active</code>, timestamps</td><td>…</td><td></td></tr>
|
||||
</tbody></table></div>
|
||||
<p><strong>Relations:</strong> referenced (snapshot) by <code>refunds</code> and the <code>cancellation_event</code> recorded on a session/booking.</p>
|
||||
<a class="back-to-top" href="#">↑ Back to top</a>
|
||||
</div></main>
|
||||
</div>
|
||||
<script>
|
||||
(function(){var k='balinyaar-docs-theme';var s=localStorage.getItem(k);
|
||||
if(s)document.documentElement.setAttribute('data-theme',s);
|
||||
else if(matchMedia('(prefers-color-scheme: dark)').matches)document.documentElement.setAttribute('data-theme','dark');})();
|
||||
function __t(){var d=document.documentElement;var n=d.getAttribute('data-theme')==='dark'?'light':'dark';
|
||||
d.setAttribute('data-theme',n);localStorage.setItem('balinyaar-docs-theme',n);}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user