backend phase 4: geography, addresses & nurse service areas
Adds the province -> city -> district reference hierarchy (geo schema, seeded with 31 provinces + capital cities + Tehran's 22 districts), nurse service areas (district_id NULL = whole city, filtered-index-pair uniqueness -> 409), and encrypted, geocoded customer addresses with a single-primary invariant. Introduces the IGeocoder seam (mocked) and 409 Conflict on the result envelope. Public cascading lookups are cached behind a generation-token scheme with invalidate-on-admin-write. One EF migration (GeographyAddressesServiceAreas, applied). Contract + swagger snapshot + handoff/report/registry updated. 103 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,8 @@
|
||||
<p><strong>Role:</strong> The person receiving care, <strong>separate from the payer</strong>. <strong>Why:</strong> the payer (adult child, spouse) is usually not the patient (elderly parent, newborn, post-surgical adult); one customer registers many patients, each with its own clinical baseline and longitudinal record. Unchanged: <code>id</code>, <code>customer_id</code>, <code>display_name</code>, <code>first_name</code>, <code>last_name</code>, <code>birth_date</code>, <code>gender</code>, <code>blood_type</code>, <code>initial_medical_notes</code> (enc), <code>is_active</code>, timestamps. <strong>Relations:</strong> N:1 → <code>customer_profiles</code>; 1:N → <code>booking_requests</code>, <code>patient_care_records</code>. <strong>Tenancy invariant:</strong> a <code>booking_request.patient_id</code> must belong to the same <code>customer_id</code>.</p>
|
||||
<h3 id="customer_addresses-core"><code>customer_addresses</code> [CORE] <a class="anchor" href="#customer_addresses-core" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> Saved service locations; the encrypted address + coordinates for EVV distance checks. <strong>Why coordinates:</strong> EVV check-in compares the nurse's GPS against the booking address within tolerance. Unchanged fields, plus: <strong>filtered <code>UNIQUE(customer_id) WHERE is_primary=1</code></strong> so exactly one primary exists (prevents ambiguous default). <strong>Relations:</strong> N:1 → <code>customer_profiles</code>, <code>cities</code>, <code>districts</code>; referenced by <code>booking_requests</code>/<code>bookings</code>.</p>
|
||||
<blockquote><p><strong>As-built (backend-phase-4):</strong> <code>usr.CustomerAddresses</code> — <code>address_line</code>, <code>postal_code</code>, <code>recipient_name</code> and <code>recipient_phone</code> are <strong>encrypted at rest</strong> via <code>IFieldEncryptor</code> and returned only in the owning customer's own read (decrypted); <code>title</code> and coordinates (<code>decimal(9,6)</code>, nullable until geocoded) stay plaintext. The <strong>first</strong> address is primary by default; <code>set_primary</code> thereafter clears the prior primary and sets the new one in one transaction, with the filtered <code>UNIQUE(customer_id) WHERE is_primary=1 AND deleted_at IS NULL</code> index as the DB backstop. Coordinates are produced by the mocked <strong><code>IGeocoder</code></strong> seam on create/update (nullable when the address can't be resolved) and are consumed by the EVV distance check only later (b9), never for coverage matching.</p>
|
||||
</blockquote>
|
||||
<h3 id="nurse_bank_accounts-core"><code>nurse_bank_accounts</code> [CORE] <a class="anchor" href="#nurse_bank_accounts-core" aria-hidden="true">#</a></h3>
|
||||
<p><strong>Role:</strong> Payout destination (IBAN/Sheba). <strong>Why hardened:</strong> the IBAN is the single place real money leaves the platform — the original "admin eyeballs the IBAN" check is exactly the forgeable, money-mule-risk link the research warns about.</p>
|
||||
<div class="table-wrap"><table><thead><tr><th>Field</th><th>Type</th><th>Notes</th></tr></thead><tbody>
|
||||
|
||||
Reference in New Issue
Block a user