Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
108 lines
5.0 KiB
Markdown
108 lines
5.0 KiB
Markdown
# 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:
|
|
|
|
1. cadence ownership and source rules generate persisted `recurring_service_periods`
|
|
2. due recurring work is the set of due `recurring_service_periods`
|
|
3. preview, generate, retry, history, reverse, and delete actions all act on canonical execution-window or service-period identity
|
|
4. `client_billing_cycles` remain 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
|
|
|
|
1. Confirm the affected obligation has persisted `recurring_service_periods` for the expected invoice window.
|
|
2. If the row is missing, treat that as materialization repair work instead of looking for a compatibility `client_billing_cycles` row.
|
|
3. Validate the due row identity from canonical fields such as `record_id`, `schedule_key`, `period_key`, `invoice_window_start`, and `invoice_window_end`.
|
|
4. Run preview or generate flows only from canonical selector input.
|
|
5. 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
|
|
|
|
```sql
|
|
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:
|
|
|
|
1. capture `record_id`, `schedule_key`, `period_key`, `invoice_id`, and the canonical execution-window identity
|
|
2. perform the reverse or delete action
|
|
3. confirm `recurring_service_periods.invoice_id`, `invoice_charge_id`, and `invoice_charge_detail_id` reflect the repaired state
|
|
4. confirm lifecycle state is reopened or preserved according to the canonical linkage repair flow
|
|
5. 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:
|
|
|
|
1. if canonical recurring detail rows exist, read and explain the invoice from those canonical periods
|
|
2. if canonical detail rows are absent, keep the invoice readable as a historical financial document instead of inventing synthetic recurring periods
|
|
3. 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_id` as 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_id` is treated as optional historical context only
|