backend phase 15 & frontend phase 8

This commit is contained in:
hamid
2026-07-10 03:22:29 +03:30
parent 93cc5ecb98
commit cd6c2591a6
154 changed files with 15335 additions and 37 deletions
+41
View File
@@ -455,6 +455,47 @@ review_tags) / `PatientCareRecordsController`. Load-bearing rules:
config `Seams:ReviewModeration`) is the AI pre-screen; clean text stays pending by default (publish gate),
banned-word → auto-hidden. Decision authority stays with `ModerateReviewCommand` (human override).
**Messaging, partner centers & admin backoffice (backend-phase-15).** The final backend phase adds two schemas
and consolidates the admin surface. A new **`messaging` schema** holds `Tickets` / `TicketParticipants` /
`TicketMessages` (entities in `Domain/Entities/Messaging/` + `TicketStatus`/`TicketCategory`/`TicketParticipantRole`
codes) — the only sanctioned post-booking channel. A new **`partner` schema** holds `PartnerCenters` (entity in
`Domain/Entities/PartnerCenters/`, `IAuditable`; the licensed sponsor / merchant-of-record). Configs in
`Persistence/Configuration/{MessagingConfig|PartnerCentersConfig}/`; per-domain repos `ITicketRepository` +
`IPartnerCenterRepository` on `IUnitOfWork`; features under `Baya.Application/Features/{Messaging|PartnerCenters}/`;
controllers `TicketsController` / `AdminTicketsController` / `AdminPartnerCentersController` / `CentersController`
/ `InternalCentersController`; one migration (`MessagingAndPartnerCenters`, which also adds the
`nurse_profiles.partner_center_id` FK in place). Load-bearing rules:
- **`is_internal` is a HARD visibility boundary enforced at the QUERY layer.** `GetTicketThreadQuery` takes an
`AsAdmin` flag; the user view (`false`) strips every `is_internal` message in the repository projection
(`GetMessagesAsync(includeInternal:false)`), the admin view (`true`, staff only) returns them. A non-staff
caller can never *set* `is_internal` on `PostMessage` nor *read* one. Never enforced only in the UI.
- **No direct nurse↔customer channel.** All post-booking comms are ticket-mediated + admin-readable; participation
(via `TicketParticipant`, `UNIQUE(ticket_id, user_id)`, soft-remove via `removed_at`) plus staff is the auth
boundary. `reference_code` is minted once (collision-checked, UNIQUE) and stable. Both `booking_id`/`refund_id`
links are nullable — handle a ticket with neither. A coordination ticket is auto-created (idempotent, one per
booking) on confirmation via `AutoCreateCoordinationTicketCommand`, dispatched from the card confirm + BNPL
settle handlers. `LogEmergencyTicket` records the aftermath of an out-of-platform emergency call (+ optional
`support_alert`) — it exposes no phone number.
- **Merchant-of-record resolution follows `partner_centers`, not a hardcoded platform.**
`PartnerCenterRepository.ResolveCenterForBookingAsync` (surfaced by `GetCenterForBookingQuery`, endpoint
`GET /internal/bookings/{id}/center`) resolves booking → nurse → `partner_center_id`; the issuer/settlement
target is `partner_center` **only** when that center `is_merchant_of_record`, else `platform`. This is the
single resolver **b11's `IssueInvoice` now calls** to set `invoices.issuing_entity_type` + `partner_center_id`.
- **`partner_centers` ≠ `organizations`.** The launch licensing *sponsor* (`partner_centers`) is distinct from
the future *employer* (`organizations`, DEFERRED). `settlement_iban` is encrypted at rest (converter in
`ApplicationDbContext`, `[AuditRedacted]`) and **masked** (last 4) in every read; `commission_rate` (the
center's cut) is separate from `platform_fee_rate`. The four DEFERRED tables (`organizations`,
`organization_nurses`, `fraud_flags`, `recurring_booking_schedules`) are **not** created.
- **Refund↔ticket link wired.** `CreateRefundCommand` (b11) now auto-opens a `category=refund` ticket via
`OpenTicketCommand` when the caller supplies none, so `refunds.ticket_id` is always non-null.
- **Backoffice consolidation surfaces, doesn't rebuild.** The support-alert worklist (`ISupportAlertService`
List/Assign/Resolve — `SupportAlertsController`) and the audit viewer (`GetAuditTrail` — `AuditController`)
already existed since b1 and are reused as-is; verification/refund/payout/moderation surfaces are their own
phases'. New seam **`ILicenseVerificationService`** (`Contracts/Common`; mock `MockLicenseVerificationService`
in CrossCutting, config `Seams:LicenseVerification`, `AutoApprove` toggle) is the eNamad / MoH permit check —
manual-approve at MVP; `VerifyPartnerCenter` records the human decision. There is **no** telephony/VoIP seam
(the emergency call is an out-of-platform `tel:` link by design). This is the last backend phase.
**Keeping the Project map current.** When a change touches the architecture — adds, removes, or
renames a project/assembly, a Clean-Architecture layer, or a major folder, or changes a cross-layer
dependency — you **must** update this Project map (and the dependency rule above, if affected) in the