Billing Service — Migration Plan
Status: populated
Owner: TBD
Last updated: 2026-04-17
Companion: Service Template
1. Scope
Migrate legacy "EHR billing module" and any customer-site spreadsheets/ERP exports into the canonical billing-service. Target tenants:
- Afghanistan — mostly self-pay; minimal prior digital billing. Fresh installs dominate.
- UAE — may have prior HIS billing and payer workflows; mixed data shapes.
- Other pilots — variable; treated case-by-case.
2. Legacy sources
| Source | Format | Notes |
|---|
Legacy EHR apps/services/billing (pre-platform) | Postgres tables (charges, invoices, payments, ledger_entries) | Partial implementation; migrate as "baseline" |
| Tenant-provided ERP extract | CSV per facility | Map to ChargeItem + Invoice + PaymentReconciliation |
| Paper / spreadsheet AR | Excel | Convert to charge + payment import via staging table |
3. Phases
Phase 0 — Data assessment (1–2 weeks per tenant)
- Extract schema; run profiling (row counts, null rates, currency mix).
- Identify outliers (negative prices, unknown codes, zombie invoices without lines).
- Agree cut-over date.
Phase 1 — Schema preparation (1 week)
- Run canonical
drizzle migrations.
- Create staging schema
billing_migration for raw imports.
- Provision tenant row in
tenant-service with currency + facility defaults.
Phase 2 — Reference data (1 week)
- Import payer directory via claims-service.
- Publish a "migration" price list (effective_from = historical earliest service date) to allow re-pricing of historical charges.
- Load tax rules for the country/facility combinations.
Phase 3 — Historical load (2 weeks)
- Load historical charges →
billing_migration.charges_staging → normalise → billing.charges (status=posted, ledger_entries written).
- Load historical payments →
billing.payments + allocations → ledger.
- Load adjustments and refunds.
- Use the existing append-only invariant: every historical row writes a new ledger entry.
- Reconcile end-of-migration balance against source balances; publish diff report.
Phase 4 — Consumer wiring (1 week)
- Subscribe to clinical producer events (registration, scheduling, orders, medication, virtual-care, immunizations).
- Wire claims-service to consume
billing.invoice.issued.v1.
- Wire patient-portal-service BFF to read accounts / invoices.
Phase 5 — Cut-over (1 day)
- Freeze legacy writes.
- Run final delta import.
- Flip traffic via Kong route (
/api/v1/billing → new service).
- Smoke tests + reconciliation.
- Un-freeze.
Phase 6 — Stabilisation (4 weeks)
- Dual-read: queries fall back to legacy read-only for audit if needed.
- Nightly reconciliation with legacy snapshot until stable.
- Retire legacy database after 90-day burn-in + compliance sign-off.
4. Data mapping
| Legacy field | New field | Notes |
|---|
charges.amount (decimal) | charges.total_amount_minor | Multiply by 10^currency-precision; enforce bigint |
invoices.status (legacy enum) | invoices.status | CREATED→draft, ISSUED→issued, SETTLED→paid, CANCELLED→voided |
Legacy audit.user | Ledger created_by (via outbox audit event) | — |
Legacy coverage_id | carried forward; claims-service owns | — |
| Paper ledgers | one-shot import into adjustments with reason MIGRATION_OPENING_BALANCE | |
5. Validation & rollback
| Check | Rule |
|---|
| Balance reconciliation | sum(legacy_balance) == sum(new_ledger) per account |
| Invoice count | equal ± 0.1 % (allow rounding on historical) |
| Currency integrity | no cross-currency accounts |
| Issued invoices are immutable | 0 post-migration edits |
Rollback: within first 24 h, route traffic back to legacy and quarantine new service. After 24 h, rollback requires DR plan + approval.
6. Cut-over communications
- 14 days before: tenant admin notification + migration schedule.
- 24 h before: final data freeze notice.
- Day of: status page; hotline to ops channel.
- Post-migration: 7-day hypercare; reconciliation report shared with tenant finance.
7. Risks
| Risk | Mitigation |
|---|
| Historical currency mis-scaling | Per-tenant precision table; property-based tests on import |
| Legacy codes not in canonical terminology | Map to local code system + flag for ops recode |
| Missing facility IDs | Default to tenant's main facility with ops review |
| Tenant over-counted historical payments | Mark differences as adjustments with reason MIGRATION_RECONCILIATION |