Files
baya-monorepo/product/data-model/12-audit-config-and-reference.html
T
hamid 2f2aec61a2 backend phase 1: config, reference & platform signals
Lay the cross-cutting platform backbone every later phase reads from. Adds
the first marketplace EF migration baseline (new `ops` schema) and the
mechanisms b2..b15 reuse: typed runtime config, an append-only audit trail,
an analytics event log, the holiday/bank-closure calendar, in-app
notifications, and the internal support-alert worklist.

Schema & migration
- New `ops` schema + migration InitialMarketplaceBaseline with 6 tables:
  PlatformConfigs (IAuditable), AuditLogs (append-only), SystemEvents,
  IranianHolidays, Notifications, SupportAlerts — with indexes/uniques and
  FKs to usr.Users. Seeded 12 config keys + 7 sample holidays via HasData.

Domain / Application
- IAuditable marker + [AuditRedacted] attribute; entities + string-code
  constant holders (config data_type, holiday type, alert type/severity/status).
- Facade contracts: IPlatformConfig, IHolidayCalendar, IAnalyticsSink,
  IAuditLogger, INotificationService, ISupportAlertService; DTOs +
  PagedResult<T>; evolved the INotificationDispatcher.Notification record to
  carry Type + DataJson; Pagination helper.
- 14 CQRS commands/queries (+ validators) wiring the endpoints to the facades.

Infrastructure
- DB-backed facade implementations in Persistence/Services/; real in-app
  INotificationDispatcher (removes the b0 log stub); notification-retention
  hosted service (purge is_read=1 AND age>90d).
- Extended AuditFieldInterceptor to also append an old/new-diff audit_logs row
  for every IAuditable change in the same transaction (PII redacted).
- Registered all facades + hosted service in AddPersistenceServices; removed
  the dispatcher registration from AddCrossCuttingSeams.

API
- 5 controllers: admin PlatformConfig/Holidays/Audit/SupportAlerts
  ([Authorize(DynamicPermission)]) + current-user Notifications ([Authorize]),
  all tenant-scoped and paginated. 16 Swagger paths total.

Money-correctness & safety rules honoured
- Config read at compute time (cached, parsed by data_type), never hardcoded;
  every config change is audited in the same transaction; audit_logs is
  append-only (no update/delete path); support alerts are admin-only;
  notifications are tenant-scoped; analytics is fire-and-forget.

Tests & docs
- 18 new foundation tests over in-memory SQLite (config typing + audit,
  holidays, notifications + tenancy + retention, support alerts, analytics);
  build clean (0 new code warnings), 22 tests green; migration applied to the
  dev DB and swagger.v1.json refreshed.
- Updated server Project map + CONVENTIONS, product data-model doc 12 (seeded
  config defaults), config-reference contract, mock registry, backend handoff/
  STATUS/report.

Follow-ups: add FK constraints for SupportAlerts.BookingId (b9) and ReviewId
(b14) when those tables land.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 01:18:00 +03:30

65 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Domain 12 — Audit, Config &amp; Reference — 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 &amp; ground truths</a></li></ul></div><div class="group"><div class="label">Business requirements</div><ul><li><a href="../business/index.html">Overview &amp; MVP scope</a></li><li><a href="../business/01-actors-and-onboarding.html">1. Actors &amp; 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 &amp; pricing</a></li><li><a href="../business/04-search-and-matching.html">4. Search &amp; matching</a></li><li><a href="../business/05-booking-and-scheduling.html">5. Booking &amp; 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 &amp; refunds</a></li><li><a href="../business/08-payments-and-escrow.html">8. Payments &amp; 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 &amp; safety</a></li><li><a href="../business/12-messaging-and-emergencies.html">12. Messaging &amp; emergencies</a></li><li><a href="../business/13-tax-invoicing-and-legal.html">13. Tax, invoicing &amp; legal</a></li><li><a href="../business/14-notifications-and-admin.html">14. Notifications &amp; admin</a></li></ul></div><div class="group"><div class="label">Database model</div><ul><li><a href="index.html">Overview &amp; decisions</a></li><li><a href="diagrams.html">Diagrams</a></li><li><a href="01-identity-and-access.html">1. Identity &amp; access</a></li><li><a href="02-geography.html">2. Geography</a></li><li><a href="03-services-and-pricing.html">3. Services &amp; pricing</a></li><li><a href="04-verification-and-credentials.html">4. Verification &amp; credentials</a></li><li><a href="05-booking-and-scheduling.html">5. Booking &amp; scheduling</a></li><li><a href="06-payments-ledger-and-refunds.html">6. Payments, ledger &amp; 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 &amp; records</a></li><li><a href="11-notifications.html">11. Notifications</a></li><li><a class="active" href="12-audit-config-and-reference.html">12. Audit, config &amp; reference</a></li><li><a href="13-partner-centers-and-future.html">13. Partner centers &amp; future</a></li></ul></div><div class="group"><div class="label">Payments deep-dive</div><ul><li><a href="../payments/index.html">Overview &amp; 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 &amp; finding</a></li><li><a href="../payments/cancellation-and-payout.html">Cancellation &amp; nurse payout</a></li><li><a href="../payments/integration-notes.html">Integration &amp; schema touchpoints</a></li><li><a href="../payments/sources.html">Recommendations &amp; sources</a></li></ul></div><div class="group"><div class="label">Research &amp; strategy</div><ul><li><a href="../research/index.html">Overview &amp; exec summary</a></li><li><a href="../research/market-and-competitors.html">Market &amp; competitors</a></li><li><a href="../research/problems-and-risks.html">Problems &amp; 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 &amp; sources</a></li></ul></div><div class="group"><div class="label">Notes &amp; 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-12-audit-config-reference">Domain 12 — Audit, Config &amp; Reference</h1>
<p><a href="index.html">← Database Model</a></p>
<h3 id="audit_logs-core"><code>audit_logs</code> [CORE] <a class="anchor" href="#audit_logs-core" aria-hidden="true">#</a></h3>
<p><strong>Role:</strong> Immutable, append-only record of every state change on sensitive entities — now explicitly <strong>including <code>platform_configs</code></strong> (so finance can prove the commission rate at any moment). <strong>Why:</strong> compliance and accountability; <code>changed_fields_json</code> enables fast filtering. Plan month-partitioning + 23yr cold-storage archival before launch. Fields unchanged. <strong>Relations:</strong> polymorphic, append-only.</p>
<h3 id="system_events-mvp"><code>system_events</code> [MVP] <a class="anchor" href="#system_events-mvp" aria-hidden="true">#</a></h3>
<p><strong>Role:</strong> High-volume behavioral/analytics event log. <strong>Why kept but de-emphasized:</strong> product analytics, not compliance. It grows unbounded — at scale, pipe it to an analytics sink/warehouse rather than the transactional DB. Fields unchanged.</p>
<h3 id="platform_configs-core"><code>platform_configs</code> [CORE] <a class="anchor" href="#platform_configs-core" aria-hidden="true">#</a></h3>
<p><strong>Role:</strong> Key-value runtime business parameters — change without a deploy. <strong>Why typed values:</strong> <code>data_type</code> tells the app how to parse. <strong>New keys</strong> this revision: <code>dispute_window_hours</code> (default 72), <code>vat_rate</code> (0.10), <code>bnpl_merchant_of_record</code>, <code>bnpl_provider_commission_rate</code>, <code>bnpl_settlement_timing</code>, cancellation-tier defaults — alongside the existing <code>platform_fee_rate</code>, <code>booking_payment_deadline_minutes</code>, <code>nurse_response_deadline_hours</code>, <code>nurse_payout_interval_days</code>, <code>evv_location_tolerance_meters</code>, <code>min_rating_for_support_alert</code>. <strong>Relations:</strong> referenced everywhere; changes audited.</p>
<p><strong>Seeded defaults (as built, backend-phase-1).</strong> The baseline migration seeds every key below. Values marked _provisional_ were chosen as safe defaults where the product docs did not pin a number — confirm before launch; each is config-driven so it changes without a deploy.</p>
<div class="table-wrap"><table><thead><tr><th>Key</th><th><code>data_type</code></th><th>Seeded value</th><th>Source</th></tr></thead><tbody>
<tr><td><code>platform_fee_rate</code></td><td>decimal</td><td><code>0.15</code></td><td>_provisional_</td></tr>
<tr><td><code>vat_rate</code></td><td>decimal</td><td><code>0.10</code></td><td>doc (10%, commission line only)</td></tr>
<tr><td><code>dispute_window_hours</code></td><td>int</td><td><code>72</code></td><td>doc</td></tr>
<tr><td><code>booking_payment_deadline_minutes</code></td><td>int</td><td><code>30</code></td><td>doc</td></tr>
<tr><td><code>nurse_response_deadline_hours</code></td><td>int</td><td><code>24</code></td><td>_provisional_</td></tr>
<tr><td><code>nurse_payout_interval_days</code></td><td>int</td><td><code>7</code></td><td>doc (weekly)</td></tr>
<tr><td><code>evv_location_tolerance_meters</code></td><td>int</td><td><code>200</code></td><td>_provisional_</td></tr>
<tr><td><code>min_rating_for_support_alert</code></td><td>decimal</td><td><code>2</code></td><td>_provisional_ (review ≤ 2 raises an alert)</td></tr>
<tr><td><code>bnpl_merchant_of_record</code></td><td>string</td><td><code>platform</code></td><td>doc (Balinyaar is MoR)</td></tr>
<tr><td><code>bnpl_provider_commission_rate</code></td><td>decimal</td><td><code>0.07</code></td><td>_provisional_</td></tr>
<tr><td><code>bnpl_settlement_timing</code></td><td>string</td><td><code>immediate</code></td><td>_provisional_</td></tr>
<tr><td><code>cancellation_tiers</code></td><td>json</td><td><code>[{"min_hours_before":48,"refund_percent":100},{"min_hours_before":24,"refund_percent":50},{"min_hours_before":0,"refund_percent":0}]</code></td><td>_provisional_</td></tr>
</tbody></table></div>
<p>Rates are <code>DECIMAL</code> fractions (not money); the IRR amounts they later multiply are <code>BIGINT</code>. <strong>Read them at compute time (cached via <code>IPlatformConfig</code>), never hardcode</strong>, and snapshot the rate used onto the priced booking/invoice so a later rate change never re-prices an existing row.</p>
<h3 id="iranian_holidays-mvp-new"><code>iranian_holidays</code> [MVP] — <strong>NEW</strong> <a class="anchor" href="#iranian_holidays-mvp-new" aria-hidden="true">#</a></h3>
<p><strong>Role:</strong> Shared official/religious holiday calendar (movable, partly lunar-Hijri), with a <code>is_bank_closed</code> flag. <strong>Why a real table:</strong> Iran's holidays are numerous and partly movable, and they drive <strong>payout bank-closure scheduling</strong> (PAYA/SATNA closed → a weekly payout shifts to the next business day), optional holiday pricing, and business-hour deadline math — none of which a purely manual per-nurse availability exception can express.</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>holiday_date</code></td><td>DATE</td><td></td></tr>
<tr><td><code>name_fa</code></td><td>NVARCHAR(200)</td><td></td></tr>
<tr><td><code>type</code></td><td>NVARCHAR(20)</td><td><code>official</code> / <code>religious</code> / <code>national</code></td></tr>
<tr><td><code>is_bank_closed</code></td><td>BIT</td><td>Drives payout date shifting</td></tr>
</tbody></table></div>
<p><strong>Relations:</strong> referenced by payout scheduling and (optionally) pricing.</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>