cleanup phases 6

This commit is contained in:
hamid
2026-08-02 18:48:32 +03:30
parent e2db97392a
commit 51e86a1e5f
239 changed files with 118 additions and 70 deletions
@@ -0,0 +1,46 @@
# Handoff — after backend-phase-11 (Refunds, clawbacks & invoices)
**The reversal leg of the payments arc is live.** Money can now flow *backwards*: an admin reverses a captured
booking payment across both fee legs, the balanced reversal posts to the append-only ledger, and the flow forks
on whether the nurse was already paid (clean `nurse_payable` reversal vs a first-class `nurse_clawbacks`
receivable). The minimal commission **invoice** (VAT on the commission line, sequential number, mocked مودیان)
ships too.
## What the frontend (f10-b11) can now build
- **Admin refund console** — create a refund (`POST admin_refunds`: full/partial by percentage or explicit legs),
the refund worklist (`GET admin_refunds` — channel, decomposed legs, status, ETA, policy snapshot), and the
clawback write-off (`POST admin_clawbacks/{id}/write_off`).
- **Admin invoice issuance** — `POST admin_invoices` (sequential number, VAT on commission, مودیان pending).
- **Customer cancellation/refund-status view** — `GET refunds/{id}/status` (tenancy-scoped; status, channel,
amount, **`expected_customer_refund_eta`** for the BNPL 710-business-day window, masked reference). This is the
**only** customer-visible refund surface — refunds are admin-initiated, there is no self-service initiation.
- **Customer/admin invoice view** — `GET invoices/{booking_id}` (number, gross, commission, VAT, مودیان status,
PDF URL when present).
## Live endpoints / contract
- Contract: **`dev/contracts/domains/refunds-invoices.md`** (enums, DTO shapes, IRR digit-strings, masked
references, failure codes, side-effects). Machine schema: `dev/contracts/openapi/swagger.v1.json` **refreshed**.
- All money is IRR integer, on the wire as a **digit-string**. `expected_customer_refund_eta` is a **date**.
- Admin endpoints are behind the admin policy and **rate-limited** (sensitive). Refunds are **never** customer
self-service.
## What is mocked / waiting
- **سامانه مودیان** is mocked behind **`IMoadianClient`** (pending/no-ref by default; a config switch forces a
registered 22-digit ref). See `reports/mocks-registry.md`.
- **BNPL revert** runs against **`IBnplProvider`** — a **thin local stub** now; **b12 owns the real seam** and its
adapter. The `bnpl_revert` ledger legs are already identical to the card path.
- **"Was the nurse paid?"** is derived from the booking's dispute-window close (`INursePayoutStatus`) until **b13**
ships `nurse_payouts`; **clawback recovery/netting is b13** (this phase only opens the `pending` receivable +
supports write-off).
- **`tickets`** arrives in **b15**: `refunds.ticket_id` is a nullable column (no FK yet); the "ticket required"
rule is config-gated off (`refund_ticket_required = false`).
- **Deferred crons** (thin/manual today): the مودیان reconciliation job (`pending → registered`) and the
BNPL-revert reconciliation job that clears `refund_payable ↔ escrow_held` for a `processing` refund.
## Notes for the next backend phases
- **b12 (BNPL):** owns the real `IBnplProvider`; the refund `bnpl_revert` path already calls it through the seam.
- **b13 (payouts):** implement the real `INursePayoutStatus` (`nurse_payout_booking_links`) and net `pending`
`nurse_clawbacks` out of a payout batch (set `recovered_in_payout_id` / `original_payout_id`, post `DEBIT
nurse_payable / CREDIT nurse_clawback_receivable`).
- **b15 (tickets/partner centers):** wire the real `refunds.ticket_id` FK + flip `refund_ticket_required` on;
wire `invoices.partner_center_id` + `nurse_clawbacks` payout FKs.