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
11 KiB
11 KiB
Contract Template Hard Cutover Plan
Context & Rationale
- The current schema stores template and instance records together (e.g.,
contracts.is_template, template rows insidecontract_lines), which forces brittle conditionals throughout our codebase and prevents enforcing stricter rules on live contracts. - Separating template data into purpose-built tables will clarify ownership, improve performance of contract-instance queries, and let us evolve template authoring without risking client contract integrity.
- A decisive cutover—preceded by rehearsal and feature-flagged code—lets us avoid months of dual-write complexity while still giving us checkpoints to compare legacy and new structures before flipping traffic.
Phase Overview
- Preparation — Requirements capture, dependency analysis, change-freeze planning.
- Schema Introduction — Add the dedicated template tables and helper views without touching production data.
- Data Migration Dry Run — Populate the new tables in staging, validate parity, and refine tooling.
- Application Readiness — Update backend/frontend code to target the new schema behind a feature flag.
- Hard Cutover Window — Execute the live migration, drop legacy columns, and enable the new code paths.
- Post-Cutover Validation — Run regressions, monitor telemetry, and plan clean-up tasks.
Detailed Steps
Phase 1 — Preparation
- Audit all surfaces using template flags or metadata (
ContractTemplateDetail, wizard flows, reporting exports, analytics events). - Confirm that downstream consumers (BI pipelines, integrations) can tolerate the schema changes or have parallel updates planned.
- Draft communication for internal stakeholders and any customers affected by the maintenance window; align on roles (migration owner, QA lead, comms owner).
- Pick the maintenance window (target off-peak), document rollback strategy (snapshot + restore steps), and freeze competing schema work during that period.
Phase 1 Tracker (in progress)
- Feature inventory (UI + services)
server/src/components/billing-dashboard/contracts/ContractTemplateDetail.tsx– parsestemplate_metadata, drives inline edit + services editor, and depends ongetContractAssignments.server/src/components/billing-dashboard/contracts/Contracts.tsx:300andContractDetailSwitcher.tsx:63– filter/switch views withcontract.is_template.server/src/components/billing-dashboard/contracts/ContractHeader.tsx:106– badge logic toggled bycontract.is_template.- Server actions:
contractActions.ts(getContracts,updateContract, etc.),contractWizardActions.ts:805-831,contractLineAction.ts, and the newcontractLineRepository.tslogic all branch onis_templateor expect template rows inside shared tables. - Billing utilities:
server/src/lib/billing/utils/templateClone.tsreads fromcontract_lines/contract_line_servicesassuming template rows live alongside instance data.
- Downstream / external consumers
- Reporting joins:
server/src/lib/billing/billingEngine.ts:396usescoalesce(cc.template_contract_id, cc.contract_id)to tie invoices back to templates. - Data scripts:
server/scripts/contract-template-decoupling.tspre-populatestemplate_contract_id&template_contract_line_id; must migrate to the new tables. - API services:
server/src/lib/api/services/ContractLineService.ts:811-823and related schemas reference template IDs for cloning and validation. - Interfaces:
server/src/interfaces/contract.interfaces.tsexpose optionalis_templateandtemplate_metadata, impacting consumers (e.g., analytics payloads) once removed.
- Reporting joins:
- Maintenance window & communication prep
- Proposed duration: 60-minute off-peak window (needs coordination with Customer Success + Support).
- Owners: Migration (Billing Platform), QA (Web), Comms (Customer Ops) – confirm availability one week prior.
- Rollback assets: capture full database snapshot immediately before the copy; keep previous application build (feature flag off) scripted for quick redeploy.
- Draft announcements: internal Slack update, statuspage maintenance notice, customer email for managed tenants (templates authoring heavy users).
Phase 2 — Schema Introduction
- Create new tables:
contract_templates,contract_template_lines,contract_template_line_services,contract_template_line_service_configuration,contract_template_line_defaults,contract_template_line_terms,contract_template_line_fixed_config,contract_template_pricing_schedules, etc., matching the field-level plan. - Add staging views that mimic the legacy combined layout to simplify parity queries (
legacy_contract_templates_view,legacy_template_lines_view). - Define foreign keys as
NOT VALIDso we can backfill before enforcing; include indexes mirroring existing query patterns (template ID, tenant, display order). - Merge these migrations and confirm CI/preview environments apply them cleanly.
Phase 2 Tracker (in progress)
- ✅ Added migration
20250107162000_create_contract_template_tables.cjsintroducing dedicated template tables (templates, lines, mappings, services, configuration variants, defaults, terms, fixed config, pricing schedules) withNOT VALIDforeign keys and supporting indexes. - ✅ Created comparison views
contract_template_compare_viewandcontract_template_lines_compare_viewto contrast legacy rows with the new schema during validation. - ✅ Validated the migration via
npm run migrate:ee, confirming views build successfully after joiningcontract_line_template_termsfor missing legacy columns. - ✅ Added backfill migration
20250107164500_backfill_contract_template_tables.cjsto copy existing template data into the new tables (idempotent inserts) so subsequent phases can rely solely on the separated schema. - ✅ Updated server actions/models/UI to read/write via the new template tables (contract actions, wizard flows, line mappings, dashboards) eliminating dependency on
contracts.is_templatefor template CRUD. (Follow-up: finish decoupling clone utilities from legacy tables.)
Phase 3 Tracker (in progress)
- ✅ Introduced
server/scripts/verify-template-migration.ts(Knex-based parity checker) to compare legacycontractstemplate data against the newcontract_template_*tables per tenant (counts + name diffs). Usage:cd server NODE_ENV=migration npx tsx ./scripts/verify-template-migration.ts - ✅ Executed the validation script locally; parity check passed. Next run it against a staging snapshot before the hard cutover window.
Phase 3 — Data Migration Dry Run
- Build idempotent scripts (Knex tasks or SQL plus node runner) that:
- Extract template rows from legacy tables based on
is_template. - Copy line/service/config rows into their new homes, preserving UUIDs where possible.
- Record mapping tables for contracts and lines (
legacy_contract_id → template_id, etc.).
- Extract template rows from legacy tables based on
- Run the scripts against a staging database cloned from production. Compare:
- Row counts per table.
- Aggregations (lines per template, services per line, tag counts).
- Spot-check sample templates (metadata, guidance, pricing schedules).
- Iterate until the dry run produces zero discrepancies aside from intentional clean-up (e.g., orphaned services that should be dropped).
- Document runtime expectations and resource usage to size the production window.
Phase 4 — Application Readiness
- Introduce new models/repos (
ContractTemplate,ContractTemplateLine, etc.) and update server actions (contractActions,contractLineAction, repository helpers) to read/write via those models. - Adjust TypeScript interfaces to split
IContractTemplatefromIContract(instances), updating UI components and hooks accordingly. - Skip feature-flag gating (new template flow becomes the default) while ensuring fallbacks are removed.
- Update migrations that create sample data/fixtures, ensuring test suites build valid templates in the new schema.
- Expand automated tests: template CRUD, assignment dashboards, contract creation wizard, and reporting queries.
Phase 4 Tracker (in progress)
- ✅ Server actions now create and maintain template data directly in
contract_template_*tables (template wizard, inline mapping, deletion) without legacy feature flags. - ✅
contractTemplate/contractTemplateLinepathways deliver display order + metadata via new models (detail views and mapping lookups). - ✅ Contract wizard snapshot/query paths read from
contract_template_*tables. - ✅ Added parity-check script to plan; new flows validated locally (phase 3).
- ✅ Template line add/remove UX now drives the updated actions (no feature flags) and client wizard cloning reads exclusively from
contract_template_*data.
Phase 5 — Hard Cutover Window
- Place the app into maintenance mode at window start; block writes via load balancer rules or database-level toggles.
- Take a full database snapshot/backup for rollback.
- Execute the migration scripts on production:
- Run the final data copy leveraging the validated scripts.
- Update foreign keys in client contract tables to reference the new template tables.
- Remove template rows from legacy tables and drop template-only columns/flags.
ALTER TABLE ... VALIDATE CONSTRAINTto enforce referential integrity.
- Deploy the feature-flag-enabled application version (flag on) and run smoke tests while still in maintenance mode: template detail load, inline edit, contract creation wizard template selection, reporting queries.
- If tests pass, exit maintenance mode and re-enable user traffic.
Phase 6 — Post-Cutover Validation
- Monitor logs, error dashboards, and database metrics for 24–48 hours with heightened alerting on contract and billing flows.
- Run targeted regression suites (Playwright, RTL, backend integration) against production or a production clone populated post-cutover.
- Communicate status updates to stakeholders, including any follow-on tasks discovered during validation.
- Schedule clean-up stories: drop temporary views/mapping tables, remove feature flag scaffolding, and update BI/integration documentation with the new schema diagrams.
Risks & Mitigations
- Migration Errors — Mitigate with thorough dry runs, mapping-table audits, and a ready-to-execute rollback snapshot.
- Extended Downtime — Keep scripts optimized, rehearse runbooks, and assign owners for each step to minimize time in maintenance mode.
- Blind Spots in Application Updates — Use feature flags plus extended automated coverage to vet new code paths before the cutover; have rollback builds prepared.
- Stakeholder Confusion — Communicate timelines and status proactively; provide clear post-cutover guidance for support/ops teams reviewing contract data.
Success Criteria
- Template CRUD and guidance management operate exclusively on the new tables with zero fallback to legacy columns.
- Client contract creation and synchronization correctly reference templates through the new foreign keys.
- Reporting, assignment summaries, and service management show no data regressions (verified via automated and manual checks).
- Legacy template columns (
contracts.is_template,contracts.template_metadata, etc.) are fully removed, and documentation reflects the new architecture.