PSA/ee/docs/plans/2026-03-16-service-period-first-billing-and-cadence-ownership/RECURRING_SERVICE_PERIOD_REGENERATION_TRIGGERS.md
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

3.8 KiB

Recurring Service-Period Regeneration Triggers

F254 defines when source-side edits must trigger future persisted service-period regeneration.

Trigger Classification Contract

The shared trigger contract now lives in:

  • packages/types/src/interfaces/recurringTiming.interfaces.ts
    • IRecurringServicePeriodRegenerationTriggerInput
    • IRecurringServicePeriodRegenerationDecision
  • shared/billingClients/recurringServicePeriodRegenerationTriggers.ts
    • resolveRecurringServicePeriodRegenerationDecision(...)

The contract answers one narrow question:

  • does this source-side edit require regeneration of future materialized periods?
  • if yes, what reason code and scope should later repositories or jobs use?

Trigger Families

Contract-Line Edits

Contract-line edits trigger regeneration only when they change recurrence-shaping fields such as:

  • billing_frequency
  • billing_timing
  • start_date
  • end_date
  • service_start_date
  • service_end_date

Those changes rebuild future candidate service periods for the affected obligation with:

  • triggerKind = contract_line_edit
  • regenerationReasonCode = source_rule_changed
  • scope = obligation_schedule_only

Pure pricing edits do not regenerate persisted periods. They change future billing amounts, not future service-period boundaries or due-window mapping.

Contract-Assignment Edits

Contract-assignment edits trigger regeneration when they change activity-window clipping fields such as:

  • assignment_start_date
  • assignment_end_date
  • service_start_date
  • service_end_date

Those changes rebuild future candidate coverage with:

  • triggerKind = contract_assignment_edit
  • regenerationReasonCode = activity_window_changed
  • scope = obligation_schedule_only

Cadence-Owner Changes

Cadence-owner changes are a special contract-line trigger.

They do not merely refresh one existing schedule. They replace future schedule identity:

  • triggerKind = cadence_owner_change
  • regenerationReasonCode = cadence_owner_changed
  • scope = replace_schedule_identity

Operationally this means:

  • supersede untouched future rows on the prior schedule key
  • materialize future rows on the new cadence-owner schedule key
  • preserve edited, locked, and billed rows under the existing override and immutability rules

Billing-Schedule Changes

Billing-schedule edits trigger regeneration only for client-cadence obligations that depend on that client billing schedule.

The first v1 trigger fields are:

  • billing_frequency
  • billing_day_of_month
  • billing_month
  • billing_anchor_date
  • billing_cycle_anchor
  • next_billing_date

Those changes use:

  • triggerKind = billing_schedule_change
  • regenerationReasonCode = billing_schedule_changed
  • scope = client_cadence_dependents

Contract-cadence obligations remain out of scope for this trigger because their invoice windows are contract-anniversary-owned rather than client-schedule-owned.

Safety Invariants

Every positive regeneration trigger still carries the same safety posture:

  • preserve user-edited future overrides
  • preserve billed history
  • leave conflict resolution to the explicit later conflict path when regenerated candidates diverge from preserved overrides

This keeps trigger detection aligned with the existing F238-F249 regeneration and conflict rules instead of creating a second mutation path.

Deliberate Boundary

This checkpoint does not yet wire live repositories, jobs, or controllers to invoke regeneration automatically.

It defines:

  • the authoritative reason-code and scope classification
  • the boundary between regeneration-required edits and amount-only edits
  • the special schedule-replacement rule for cadence-owner changes

Live trigger wiring and DB-backed regeneration flow remain sequenced behind F255-F259.