Hermes 284313f908
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
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

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