backend phase 15 & frontend phase 8
This commit is contained in:
@@ -12,6 +12,30 @@ One block per completed backend phase. Newest at the top. Backend lane writes he
|
||||
- **Notes for frontend:** <anything load-bearing>
|
||||
-->
|
||||
|
||||
## backend-phase-15 — Messaging (tickets), partner centers & admin backoffice — 2026-07-10
|
||||
- **Shipped (FINAL backend phase):** new `messaging` schema — `Tickets` (`UNIQUE(reference_code)`, status/
|
||||
category, nullable `booking_id`/`refund_id`), `TicketParticipants` (`UNIQUE(ticket_id, user_id)`, soft-remove
|
||||
via `removed_at`), `TicketMessages` (`is_internal` hard boundary) — and new `partner` schema — `PartnerCenters`
|
||||
(`IAuditable`, encrypted+masked `settlement_iban`, `commission_rate` separate from `platform_fee_rate`). Added
|
||||
the `nurse_profiles.partner_center_id` FK in place. One migration (`MessagingAndPartnerCenters`). CQRS:
|
||||
OpenTicket / AutoCreateCoordinationTicket / PostMessage / Add+RemoveParticipant / Close+ReopenTicket /
|
||||
LogEmergencyTicket / GetTicketThread (user vs admin view) / ListMyTickets / ListTicketsForAdmin; CreatePartnerCenter
|
||||
/ UpdatePartnerCenter / VerifyPartnerCenter / SponsorNurse / GetCenterForBooking / ListPartnerCenters /
|
||||
GetPartnerCenterById / GetCenterDashboard. 5 new controllers (Tickets/AdminTickets/AdminPartnerCenters/Centers/
|
||||
InternalCenters). Wired: b11 `IssueInvoice` now resolves issuer/settlement via `GetCenterForBooking`; b11
|
||||
`CreateRefund` auto-opens a `refund` ticket (so `refunds.ticket_id` is always non-null); the card confirm + BNPL
|
||||
settle handlers auto-create the coordination ticket. Support-alert worklist + audit viewer reused from b1 (not
|
||||
rebuilt). The 4 DEFERRED tables were **not** created.
|
||||
- **Contracts:** `dev/contracts/domains/messaging-notifications-admin.md` + openapi snapshot refreshed (yes).
|
||||
- **Mocked:** `ILicenseVerificationService` (eNamad / MoH permit — manual-approve at MVP) → 🟡 (see reports/mocks-registry.md).
|
||||
- **Gate:** build clean (0 new code warnings) / tests green (358 total: 4 identity + 240 foundation + 114 API,
|
||||
incl. 4 new merchant-of-record resolver tests + 8 new ticket/partner-center API tests).
|
||||
- **Handoff:** backend/handoff/after-backend-phase-15.md
|
||||
- **Notes for frontend:** `is_internal` is stripped from the user thread view server-side (never trust the UI);
|
||||
no direct nurse↔customer channel / no phone numbers; ticket↔booking/refund links are optional (nullable);
|
||||
duplicate participant add = 409; `settlement_iban` is only ever returned masked (last 4); merchant-of-record
|
||||
(invoice issuer + settlement) follows `partner_centers`, resolved by `GET /internal/bookings/{id}/center`.
|
||||
|
||||
## backend-phase-14 — Reviews, ratings & patient care records — 2026-07-09
|
||||
- **Shipped:** new `reviews` schema, 4 tables — `Reviews` (`UNIQUE(booking_id)`, `CHECK(rating 1–5)`, guarded
|
||||
`moderation_status`, `IAuditable`), `ReviewTagsMaster` (seeded 5-tag vocab, `UNIQUE(code)`), `ReviewTagLinks`
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# Handoff — after backend phase 15 (Messaging, partner centers & admin backoffice)
|
||||
|
||||
**This is the final backend phase. The backend chain is complete.** Every domain the admin backoffice acts on
|
||||
now exists and is wired together.
|
||||
|
||||
## What is now live (frontend can build against it)
|
||||
|
||||
### Tickets — the post-booking channel (f14 messaging)
|
||||
- `POST /api/v1/tickets` — open a ticket (`category` ∈ `support|coordination|refund|emergency`; optional
|
||||
`bookingId`/`refundId`; body optional). Opener is auto-added as the first participant. Returns
|
||||
`{ ticketId, referenceCode, status, category }`.
|
||||
- `POST /api/v1/tickets/{id}/messages` — post a message. **`isInternal` is staff-only**; a non-staff caller
|
||||
sending `true` → `403`; posting to a closed ticket as non-staff → `403`.
|
||||
- `POST /api/v1/tickets/{id}/participants` (add) / `DELETE …/participants/{userId}` (soft-remove) — staff or
|
||||
ticket owner. A **duplicate add is `409`** (backed by `UNIQUE(ticket_id, user_id)`), never a 500.
|
||||
- `POST /api/v1/tickets/{id}/close` · `/reopen` — participant or staff (idempotent).
|
||||
- `POST /api/v1/tickets/emergency` — assigned nurse (or staff) logs an emergency (+ optional support alert).
|
||||
- `GET /api/v1/tickets` — my tickets (paginated, filter `status`, search `referenceCode`).
|
||||
- `GET /api/v1/tickets/{id}` — **user thread view: internal notes are stripped** in the projection.
|
||||
- `GET /api/v1/admin/tickets` + `GET /api/v1/admin/tickets/{id}` — admin queue + **admin thread view: internal
|
||||
notes included** (`support`/`admin`).
|
||||
|
||||
**The rule f14 must respect:** never build a direct nurse↔customer channel, never surface a phone number, and
|
||||
never rely on the UI to hide internal notes — the backend already strips them from the user payload. The
|
||||
coordination ticket for a booking is auto-created on confirmation (you don't create it).
|
||||
|
||||
### Partner centers + merchant-of-record (f15 admin + partner consoles)
|
||||
- `POST /api/v1/admin/partner-centers` (create, inactive) · `PATCH …/{id}` (update) ·
|
||||
`POST …/{id}/verify` (activate) · `POST …/{id}/sponsor-nurse` · `GET …` (list) · `GET …/{id}` (detail) —
|
||||
`admin`/`super_admin`. **`settlementIbanMasked` (last 4) is the only IBAN ever returned** — never plaintext.
|
||||
`commissionRate ∈ [0,1)`; a merchant-of-record center requires a `settlementIban`.
|
||||
- `GET /api/v1/centers/{id}/dashboard` — the center's own account (or staff): sponsored nurses + booking/invoice
|
||||
counts + masked settlement summary.
|
||||
- `GET /api/v1/internal/bookings/{bookingId}/center` — the issuer/settlement resolver
|
||||
(`platform` | `partner_center`).
|
||||
|
||||
### Admin backoffice (surfaced, not rebuilt)
|
||||
- Support-alert worklist: `GET support_alerts/get_support_alerts`, `POST …/assign_support_alert`,
|
||||
`POST …/resolve_support_alert` (built b1). Audit viewer: `GET audit/get_audit_trail` (built b1). Both
|
||||
`DynamicPermission`. Verification queue / refunds / payout dashboard / moderation queue are their own phases'
|
||||
routes — surface them under the admin console with the right RBAC scope.
|
||||
|
||||
## RBAC the frontend must respect (per route)
|
||||
- Authenticated (own) ticket routes: any logged-in user; participation is enforced server-side.
|
||||
- Admin ticket queue + admin thread: `support`/`admin`. Partner centers: `admin`/`super_admin`. Center
|
||||
dashboard: the center's `adminUserId` (or staff). Support alerts: `support`/`admin`. Audit: `super_admin`/`admin`.
|
||||
- The admin role passes every `DynamicPermission` check; narrower staff scopes (`support`/`finance`/`moderation`)
|
||||
are granted via seeded role claims.
|
||||
|
||||
## What's mocked
|
||||
- **`ILicenseVerificationService`** (eNamad / MoH establishment-permit) — `MockLicenseVerificationService`,
|
||||
manual-approve at MVP (`NeedsManualReview`); `VerifyPartnerCenter` records the human decision. Config
|
||||
`Seams:LicenseVerification:AutoApprove` forces `Valid`. See the mock registry (🟡). There is **no** telephony
|
||||
seam — the emergency call is an out-of-platform `tel:` link by design.
|
||||
|
||||
## Contracts
|
||||
- `dev/contracts/domains/messaging-notifications-admin.md` (this phase). `swagger.v1.json` refreshed (now includes
|
||||
`/tickets`, `/admin/tickets`, `/admin/partner-centers`, `/centers`, `/internal/bookings/{id}/center`).
|
||||
|
||||
## Types / wire notes
|
||||
- Envelope unchanged (camelCase body, snake_case URL tokens where `[action]`-based; the new controllers use
|
||||
explicit REST routes). Pagination `page`/`pageSize` (default 50, max 100). `sentAt`/`closedAt`/`verifiedAt` are
|
||||
UTC ISO-8601; ids are numbers; the settlement IBAN is a masked string (`"••••0001"`).
|
||||
Reference in New Issue
Block a user