feat(billing): generate recurring invoices from contract items (PMS-64 AC5) #119
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/pms64-recurring-invoicing"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes PMS-64 AC5: contracts exposed recurring items + billing_cycle/amount, but billing never turned them into invoices. This adds a recurring billing run.
Design
migrations/031_contract_invoice_runs.sqlwithUNIQUE (tenant_id, contract_id, period_start). Per contract+period in one transaction: mint invoice -> insert header ->INSERT ... ON CONFLICT DO NOTHING RETURNING id; if the period was already billed (prior/concurrent run), the whole tx rolls back (invoice + sequence bump). At most one invoice per period, robust to double-runs/restarts.BillingService::generate_due_recurring_invoices(tenant_id, now)finds active, non-one_time contracts whose current period is due and have recurring items, builds lines fromrecurring_service/retaineritems, and creates the invoice via the existingcreate_invoicemachinery (sequence lock, totals, audit) - not a parallel writer.current_billing_period(cycle, start_date, today)tiles fixed periods (1/3/12 months) forward from start_date.RecurringInvoicingWorker(scheduler Job, daily-ish, cross-tenant), registered in main.rs like ContractLifecycleWorker. Service method is independently callable (tests drive it with injectednow).Tests (tests/recurring_invoicing.rs, #[sqlx::test])
Monthly -> one invoice/2 lines/total; rerun same period -> no dupe; one_time + expired -> nothing; no recurring items -> nothing; advance a month -> distinct invoice, rerun idempotent.
Decisions to confirm (human review)
No HTTP trigger added (AC is a scheduled run + callable service method). NOT compiler-checked - needs
just check+ the test (with tests/ mount).