Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
5.0 KiB
Recurring Invoicing Hard Cutover Runbook
Purpose
Use this runbook after the hard cutover when recurring invoicing is service-period driven only.
It is the operator and developer reference for:
- understanding the final recurring mental model
- diagnosing missing due recurring work without falling back to billing-cycle compatibility rows
- repairing materialization or linkage gaps through canonical recurring service-period data
- validating that history and reversal still line up with canonical recurring linkage
Final Recurring Mental Model
Recurring invoicing now has one operational model:
- cadence ownership and source rules generate persisted
recurring_service_periods - due recurring work is the set of due
recurring_service_periods - preview, generate, retry, history, reverse, and delete actions all act on canonical execution-window or service-period identity
client_billing_cyclesremain cadence infrastructure, not recurring due-work rows
Compatibility due-work rows are removed from steady-state recurring execution. If a recurring obligation should be invoiceable and no canonical service-period row exists, treat that as a repairable materialization failure.
Hard-Cutover Operator Sequence
- Confirm the affected obligation has persisted
recurring_service_periodsfor the expected invoice window. - If the row is missing, treat that as materialization repair work instead of looking for a compatibility
client_billing_cyclesrow. - Validate the due row identity from canonical fields such as
record_id,schedule_key,period_key,invoice_window_start, andinvoice_window_end. - Run preview or generate flows only from canonical selector input.
- When reversing or deleting a billed recurring invoice, validate the linked recurring service-period rows reopen or remain correctly linked through canonical invoice linkage.
Missing Due-Work Diagnosis
1. Inspect persisted recurring service periods
select
record_id,
schedule_key,
period_key,
cadence_owner,
lifecycle_state,
service_period_start,
service_period_end,
invoice_window_start,
invoice_window_end,
invoice_id,
invoice_charge_id,
invoice_charge_detail_id
from recurring_service_periods
where tenant = :tenant
and client_id = :client_id
order by invoice_window_end desc, service_period_start desc;
2. Interpret the result
- matching canonical row exists and is due: recurring UI and API should use that row directly
- matching canonical row exists but is billed or locked: validate whether the invoice history or reversal state already explains the absence from due work
- matching canonical row does not exist: missing recurring service-period materialization is a repair state, not a fallback-ready invoice row
3. Check source-rule inputs
If the row is missing, confirm:
- cadence owner is correct for the obligation
- client cadence rules still exist when
cadence_owner = client - future recurring service periods were regenerated after source-rule changes
Reverse/Delete Repair Notes
Use canonical recurring linkage when repairing recurring invoice state:
- capture
record_id,schedule_key,period_key,invoice_id, and the canonical execution-window identity - perform the reverse or delete action
- confirm
recurring_service_periods.invoice_id,invoice_charge_id, andinvoice_charge_detail_idreflect the repaired state - confirm lifecycle state is reopened or preserved according to the canonical linkage repair flow
- rerun due-work validation to confirm the same execution window reappears only when it should be invoiceable
Do not treat a billing_cycle_id value as the primary repair handle for recurring invoices.
Historical Incomplete Linkage
Some historical invoices may remain readable without complete canonical recurring linkage.
Use this posture:
- if canonical recurring detail rows exist, read and explain the invoice from those canonical periods
- if canonical detail rows are absent, keep the invoice readable as a historical financial document instead of inventing synthetic recurring periods
- if related source invoice lineage cannot be resolved, surface that as missing source context and keep the invoice readable with null recurring period metadata
What not to do:
- do not recreate live recurring due rows from
client_billing_cycles - do not treat
billing_cycle_idas a replacement execution key - do not backfill synthetic recurring service periods onto historical invoices purely for display
Fallback read states such as financial_document_fallback and missing_source_context are acceptable only to preserve historical readability while cleanup or backfill work is handled explicitly.
Validation Checklist
- recurring due-work investigation starts from
recurring_service_periods - no operator step expects a compatibility due-work row from
client_billing_cycles - client cadence is explained as source-rule infrastructure only
- history and reversal checks are driven by canonical recurring linkage
billing_cycle_idis treated as optional historical context only