medOS ultra

Ledger to Invoice Bridge (createFromLedger)

Closes the automated-charge-capture to claim seam from the append-only ledger.

2 min read diagramsUpdated 2026-06-03docs/architecture/createfromledger-bridge.md

Closes the automated-charge-capture → claim seam. The append-only billable_ledger (menu-price-ledger) accumulates charges as care happens, but nothing yet materializes those lines into a SalesOrder / ClaimInvoice. This is the missing path the billing-rcm master doc names as buildable but absent.

Status: design + skeleton. The NestJS endpoint is specified here and the bridging edge function ships as a documented skeleton (close-encounter-billing). The endpoint implementation in services/financial is the remaining work.

The two pieces

1. POST /v2/financial/salesOrders/createFromLedger (NestJS — to build)

Location: services/financial/src/api/financial/modules/salesOrder/ (new action).

Request:  { encounterId, closeReason?, idempotencyKey }
Behaviour:
  1. Read billable_ledger lines for encounterId (canonical pricing already stamped
     by resolve_menu_price() — contract → scheme → payor → standard).
  2. Group into SalesOrder + OrderRequestItem[] (one SO per encounter per close —
     INVARIANT, enforced via idempotencyKey = `${encounterId}:${closeHash}`).
  3. Split patient-responsible vs insurer-claimable from the coverage context
     (insurance_context / china_insurer_gop_policies copay_pct).
  4. Emit manifest.financial.salesOrderCreated → orchestrator → read models.
Response: { salesOrderId, total, patientResponsible, insurerClaimable }

Invariants (from billing-rcm-unified-master): one SO per encounter per close; closed bill is frozen; idempotent (re-run returns the same SO); no silent zero.

2. close-encounter-billing (Deno edge fn — skeleton shipped)

Location: infrastructure/medbase/functions/close-encounter-billing/index.ts. Bridges the read-model trigger (encounter close / cashier action) to the NestJS endpoint above. On success, stamps encounter.billClosed/billClosedAt/billClosedBy (per delivery-room-remaining-backend-work.md §2) and the journey-cache projection.

Flow

encounter close (cashier action)
        │
        ▼
close-encounter-billing (edge fn)
        │  reads billable_ledger lines for the encounter
        ▼
POST /v2/financial/salesOrders/createFromLedger  (NestJS gateway)
        │  builds SalesOrder + items, splits copay/claim
        ▼
manifest.financial.salesOrderCreated → encounter-orchestrator
        │
        ├─▶ encounter_journey_cache.financial_summary (+ billClosed flag)
        └─▶ department_queues (billing-pending → settled) via trg_sync_billing_queue

China specifics

  • The copay/claim split reads china_insurer_gop_policies.copay_pct (medos-china pack).
  • The resulting SO feeds the cn-commercial-direct / cn-shebao-basic connector pack for claim export, and the Fapiao template (fapiao-template.json) for tax invoicing.

Open work

  1. Implement the NestJS createFromLedger action (read-model → SO mapper + idempotency).
  2. Wire close-encounter-billing to the real gateway URL + service auth (TODO in skeleton).
  3. Confirm billable_ledger column contract before building the mapper.
  4. Add manifest.financial.salesOrderCreated to the orchestrator if not already handled.
Ask Anything