# Contract Template Overhaul & Client Assignment Alignment Plan ## Summary This plan restructures the contracts domain so that `contracts` and `contract_lines` represent reusable templates, while `client_contracts` and `client_contract_lines` continue to hold tenant-specific instances. The work spans backend schema changes, domain service updates, UI flow adjustments for both the contract template wizard and the client contract wizard, and a data migration to decouple template-only fields from client-specific values. ## Goals - Treat `contracts` / `contract_lines` as tenant-scoped templates that omit client-specific commercial terms (rates, quantities, bucket settings, etc.). - Ensure `client_contracts` and `client_contract_lines` own all mutable billing terms and lifecycle metadata used for invoicing. - Provide a streamlined Contract Template Wizard that mirrors the client contract wizard but skips per-client pricing fields. - Allow the Client Contract Wizard to bootstrap from a selected contract template, pre-filling compatible sections while prompting for client-specific details. - Migrate existing data so that all commercial terms live on `client_contract_lines` (with fallbacks for empty values). - Keep billing engine, reporting, and integrations functional during the transition. ## Non-Goals - Overhauling the service catalog or invoicing engine beyond required interface changes. - Introducing new billing models or pricing logic. - Building multi-tenant contract sharing across different Alga tenants. ## Stakeholders - Billing & Finance product owners - Frontend billing dashboard team - Backend billing platform team - QA & Test Automation team ## High-Level Timeline 1. **Phase 0 – Discovery & Alignment (1 week)** 2. **Phase 1 – Schema Preparation & Migrations (1.5 weeks)** 3. **Phase 2 – Backend Domain Updates (2 weeks)** 4. **Phase 3 – Frontend Template Wizard & Client Wizard Enhancements (2 weeks)** 5. **Phase 4 – Data Migration & Backfill (1 week)** 6. **Phase 5 – QA, Rollout, & Monitoring (1 week)** Total estimated duration: ~8.5 weeks including buffer. --- ## Phase 0 – Discovery & Alignment **Goals:** Validate assumptions about current contracts usage, document edge cases, and align on template vs. client instance semantics. - Inventory existing tables/fields using DB diagrams and confirm ownership (e.g. `contracts`, `contract_lines`, `contract_line_mappings`, `client_contracts`, `client_contract_lines`, `client_contract_line_service_configurations`). - Review Knex migrations in `server/migrations` (start with files containing `contract`/`billing` naming) to capture current columns, composite keys, and sequences on each related table. - Map multi-tenant helpers referenced by migrations (e.g., `server/src/lib/db/admin.ts`, `server/src/lib/db/knexfile.ts`, `shared/db/index.ts`) to understand how schema helpers are invoked during tenant provisioning. - Document any triggers, row level security policies, and dependent views/materialized views defined in SQL files (see `server/migrations/*cleanup_billing_to_contracts.cjs` and `cleanup_obsolete_email_tables.sql` for precedent). - Review backend usage patterns: - Run `rg "client_contract"` and `rg "contract_lines"` across `server/src/lib/api/services`, `server/src/lib/billing`, and `ee/server/src/lib` to list the service entrypoints, DTOs, and utilities that currently rely on template pricing data. - Trace how the REST/Next.js routes obtain contract data (e.g., `server/src/app/api/v1/contracts`, enterprise overrides under `ee/server/src/app/api`) and note which serializers expect price fields on `contracts` vs. `client_contracts`. - Summarize how integration and E2E suites seed contract data through helpers in `server/src/test/integration`, `server/src/test/e2e`, and scripts such as `server/scripts/create-tenant.ts` and `server/scripts/seed-templates.js` that provision default billing records. - Capture UI flows: - Walk through `ContractWizard.tsx`, `ContractDetail.tsx`, and billing dashboard routes to catalog inputs that currently bind to template pricing, quantities, or bucket settings. - Note feature flags, context providers, and state containers (`Contracts.tsx` wizard state, billing feature flags) that determine whether a template can be selected during client contract creation. - Produce a conclusive spec doc (append to this plan) enumerating which fields should remain on template tables, including an explicit mapping of “template column → client instance column” plus validation rules for missing data. - Align with stakeholders on acceptance criteria and rollout checkpoints; schedule reviews with Billing PM, finance, and QA to confirm migration sequencing and sign-off expectations. ### Phase 0 Progress Log (2025-02-14) - [x] **Inventory current schema surface** - `contracts` table stores template metadata today (`contract_name`, `billing_frequency`, activation flags) with tenant-scoped PKs and indexes (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:166-183`). - `contract_lines` and supporting config tables retain pricing knobs such as base rates, overtime multipliers, and bucket limits (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:400-936`). - Client instance data is split across `client_contracts` (start/end dates, active state) and `client_contract_lines` (per-client assignments with timestamps) (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:240-569`). - Multi-tenant enforcement relies on shared `addTenantForeignKey` helper; no triggers or RLS rules are defined for these tables in current migrations (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:1214-1246`). - [x] **Review backend usage patterns** - REST entry points live under Next routes (`server/src/app/api/v1/contracts/route.ts`, `server/src/app/api/v1/contract-lines/*`) backed by `ApiContractLineController`, which exposes CRUD, template copy, and client assignment flows (`server/src/lib/api/controllers/ApiContractLineController.ts:1-210`). - `createContractFromWizard` persists pricing, rate tiers, and bucket settings directly onto template tables before seeding client assignments (`server/src/lib/actions/contractWizardActions.ts:80-260`). - Reporting and billing pipelines pull amounts from `contract_line_fixed_config`, `contract_line_services`, and fall back to `contract_line_mappings.custom_rate`, so template-vs-instance separation must preserve these call sites (`server/src/lib/reports/definitions/contracts/expiration.ts:60-96`). - Test helpers in `server/test-utils/billingTestHelpers.ts:520-640` seed both template and client tables, mirroring production expectations for pricing fields. - [x] **Capture current UI flows** - `ContractWizard` collects client-facing pricing inputs (base rates, quantities, usage rates, bucket overlays) and posts them through `createContractFromWizard`, reinforcing that templates currently own monetary fields (`server/src/components/billing-dashboard/contracts/ContractWizard.tsx:23-210`). - Contract list surfaces (`server/src/components/billing-dashboard/contracts/Contracts.tsx:300-360`) and rate dialogs (`server/src/components/billing-dashboard/contracts/ContractPlanRateDialog.tsx:12-72`) expect per-line pricing to be editable on template entities. - [x] **Template field ownership spec** - Captured a column-by-column mapping for core tables plus proposed client-instance counterparts under “Template vs Instance Field Ownership”. - [ ] **Stakeholder alignment** - Schedule finance/QA/billing sync once ownership matrix is stable; confirm rollout checkpoints and validation requirements. ### Template vs Instance Field Ownership | Table | Column / Concern | Current Role | Proposed Owner | Notes / Follow-ups | | --- | --- | --- | --- | --- | | `contracts` | `contract_name`, `contract_description`, `billing_frequency`, `is_active`, timestamps (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:166-227`) | Wizard defaults + feature gating | Template | Keep columns; add `template_metadata` JSON for guidance strings and UI hints. | | `client_contracts` | `client_id`, `start_date`, `end_date`, `is_active`, `contract_id`, timestamps (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:240-284`) | Client lifecycle | Client instance | Add `template_contract_id` FK + `created_from_template_version` to track template revisions. | | `contract_line_mappings` | `display_order`, `custom_rate`, timestamps (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:300-345`) | Associates template contracts↔lines & overrides | Mixed | Keep ordering on template. Move `custom_rate` into `client_contract_line_pricing` table keyed by `client_contract_line_id`. | | `contract_lines` | `contract_line_name`, `description`, `contract_line_type`, `service_category`, activation flags (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:400-468`) | Template scaffolding | Template | Preserve; add `is_template` flag (Phase 1). | | `contract_lines` | `billing_frequency`, overtime/after-hours columns (`enable_overtime`, `overtime_rate`, etc.) | Currently shared defaults but billable | Client instance | Copy into `client_contract_line_terms` (new) linked to `client_contract_line_id`; template retains optional suggested defaults stored in `contract_line_template_terms`. | | `contract_line_fixed_config` | `base_rate`, `enable_proration`, `billing_cycle_alignment` (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:470-522`) | Controls fixed-fee pricing | Client instance | Duplicate structure into `client_contract_line_fixed_config`; template table keeps nullable default columns only. | | `contract_line_services` | `service_id`, `quantity`, `custom_rate` (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:790-838`) | Bundled services + rate overrides | Client instance | Create `client_contract_services` keyed by `client_contract_line_id`; template table keeps `service_id` + `default_role` style metadata (`quantity` -> `default_quantity`, `custom_rate` -> remove). | | `contract_line_service_configuration` | `configuration_type`, `custom_rate`, `quantity` (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:840-894`) | Service-specific config (Fixed/Bucket/Usage) | Client instance | Introduce `client_contract_service_configuration` keyed by `client_contract_service_id`; template table retains `configuration_type` + optional metadata only. | | `contract_line_service_bucket_config` | `total_minutes`, `overage_rate`, `allow_rollover` (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:960-1010`) | Bucket sizing & overage pricing | Client instance | New `client_contract_service_bucket_config`; templates store defaults in `contract_line_service_defaults` JSON. | | `contract_line_service_fixed_config` | `base_rate` (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:1012-1060`) | Fixed rate overrides | Client instance | Move to `client_contract_service_fixed_config`; template retains default guidance. | | `contract_line_service_hourly_config` | `minimum_billable_time`, overtime multipliers, etc. (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:1062-1114`) | Hourly behavior | Client instance | New `client_contract_service_hourly_config`; template table trimmed to recommended values. | | `contract_line_service_hourly_configs` | `hourly_rate`, `minimum_billable_time` (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:1116-1152`) | Hourly tier matrix | Client instance | Mirror table `client_contract_service_hourly_tiers` (per `config_id` + rate). Templates keep structural tiers without dollar amounts. | | `contract_line_service_rate_tiers` | Tier pricing ladder (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:1154-1194`) | Usage tier pricing | Client instance | Move rate fields to `client_contract_service_rate_tiers`; templates retain `min/max_quantity`. | | `contract_line_service_usage_config` | `unit_of_measure`, `enable_tiered_pricing`, `base_rate`, `minimum_usage` (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:1196-1244`) | Usage pricing baseline | Mixed | Keep descriptive fields (`unit_of_measure`, `enable_tiered_pricing`) on template; relocate `base_rate`, `minimum_usage` to client usage config table. | | `contract_line_discounts` | Discount linkages (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:896-940`) | Template-level discount association | Mixed | Maintain template associations for suggestion; create `client_contract_line_discounts` for actual applied discounts per client. | | `client_contract_lines` | `client_id`, `contract_line_id`, `start_date`, `is_active`, timestamps (`server/migrations/20251008000001_rename_billing_to_contracts.cjs:520-569`) | Client assignment roster | Client instance | Add `template_contract_line_id`, `billing_frequency`, `is_customized`, monetary columns or FK out to new pricing tables. | #### Client Instance Table Additions - **`client_contract_line_terms`**: (`client_contract_line_id` PK/FK, `billing_frequency`, `enable_overtime`, `overtime_rate`, `overtime_threshold`, `enable_after_hours_rate`, `after_hours_multiplier`). Values copied from `contract_lines` defaults during migration; future writes require these fields for billing engine parity. - **`client_contract_services`**: (`client_contract_service_id` uuid, `client_contract_line_id`, `service_id`, `quantity`, `rate_cents`, `effective_date`, timestamps). Supersedes `contract_line_services` pricing data for clients; templates keep a slim `contract_template_services` table with `default_quantity`/`default_role`. - **`client_contract_service_configuration`** tree: - `client_contract_service_configuration` (mirrors schema of `contract_line_service_configuration` minus template defaults; keyed by `client_contract_service_id`). - `client_contract_service_bucket_config`, `client_contract_service_fixed_config`, `client_contract_service_hourly_config`, `client_contract_service_hourly_tiers`, `client_contract_service_rate_tiers`, `client_contract_service_usage_config` matching existing template tables but referencing `client_contract_service_configuration_id`. - **`client_contract_line_pricing`**: (`client_contract_line_id`, `source_template_contract_line_id`, `custom_rate_cents`, `source_contract_line_mapping_id`, `notes`). Receives data from `contract_line_mappings.custom_rate`. - **`client_contract_line_discounts`**: (`client_contract_line_id`, `discount_id`, `applied_rate`, `start_date`, `end_date`) to represent actual discount applications, distinct from template recommendations. - **`contract_line_template_terms`**: New template companion storing optional defaults for term-related fields stripped from `contract_lines` (mirrors columns added to `client_contract_line_terms` but nullable). #### Cross-System Considerations - **Billing engine** (`server/src/lib/billing/billingEngine.ts:409-470`): Update all joins to reference `client_contract_*` tables for pricing, ensuring totals never pull from template columns. - **Reporting** (`server/src/lib/reports/definitions/contracts/expiration.ts:60-96`, `.../revenue.ts:45-85`): Redirect rate lookups to `client_contract_line_pricing` and corresponding client config tables. - **Test fixtures** (`server/test-utils/billingTestHelpers.ts:520-640`): Extend helper APIs to seed both template defaults and client-specific pricing, aligned with new schema. - **Historical data**: For closed contracts, persist original `contract_id`/`contract_line_id` associations via new `template_*` foreign keys to support audits without keeping legacy rate columns on templates. ## Phase 1 – Schema Preparation & Migrations **Goals:** Add new columns/constraints to support template semantics and prepare for data migration. - Draft ERD updates showing final relationships. - Database migrations (Knex + SQL): - Add `is_template` boolean flags to `contracts` and `contract_lines` (default `true`, enforced via check to keep backward compatibility during migration). - Introduce `template_contract_id` FK on `client_contracts` (nullable) referencing `contracts.id` to track origin template. - Introduce `template_contract_line_id` FK on `client_contract_lines` referencing `contract_lines.id`. - Create template companion tables (`contract_line_template_terms`, `contract_template_services`, `contract_line_service_defaults`) to store recommended values separated from instance pricing. - Add nullable JSONB column `template_metadata` to `contracts` for wizard hints (e.g. recommended payment cadence, notes). - Introduce client-instance tables (`client_contract_line_terms`, `client_contract_services`, `client_contract_service_configuration` and child tables, `client_contract_line_pricing`, `client_contract_line_discounts`) mirroring the template schema but keyed to `client_contract_line_id`. - Update existing template tables to drop or rename monetary fields: - Convert `contract_line_services.quantity` → `default_quantity`, remove `custom_rate`. - Remove rate columns from `contract_line_service_*` tables or mark nullable defaults pending migration. - Drop `contract_line_mappings.custom_rate` once client pricing table is populated. - Create forward migration scripts under `server/migrations` (e.g., `YYYYMMDDHHMM_contract_template_overhaul.cjs`), run locally with `npm run migrate`, and document verification queries for teardown. - Write migration rollback strategy and add to `ee/docs/plans/...` appendix. **Phase 1 Status – 2025-02-14** - [x] Drafted initial Knex migration (`server/migrations/20250214090000_contract_templates_phase1.cjs`) that introduces template flags, template metadata JSON, and scaffolds client-specific tables for pricing/config cloning. - [ ] Update existing migrations/services to remove legacy pricing fields once data migration completes (tracked in Phase 4). - [ ] Align migration with rip-and-replace approach (no dual-write): adjust contract assignment services once data backfill happens. ## Phase 2 – Backend Domain Updates **Goals:** Ensure services treat templates as blueprints and client contracts as concrete instances. - Update repositories/services: - `server/src/lib/api/services/ContractService.ts` → expose APIs to manage template contracts (CRUD) without pricing fields. - `server/src/lib/api/services/ClientContractService.ts` → accept optional `templateContractId` and clone relevant lines into `client_contract_lines` with new pricing inputs. - `server/src/lib/api/services/ContractLineService.ts` → ensure template lines handle service attachment and metadata only. - `server/src/lib/billing/billingEngine.ts` and related modules → confirm all billing amounts come from `client_contract_lines`. - Adjust DTOs/interfaces under `server/src/interfaces/billing.interfaces.ts` to distinguish `ContractTemplate` vs `ClientContract` payloads. - Implement cloning utilities in `server/src/lib/billing/utils/templateClone.ts` (new file) to copy template scaffolding into client contract context. - Update validation logic to forbid setting pricing on template endpoints; add API errors. - Ensure service catalog linkage remains consistent (e.g., `plan_service_configuration` usage if applicable). - Extend unit/integration tests: - New tests in `server/src/test/unit/contracts` verifying template CRUD. - Adjust existing tests in `server/src/test/integration/api/storageHandlers.test.ts` and `server/src/test/e2e/api/services.e2e.test.ts` to use client contract rates only. **Phase 2 Status – 2025-02-14** - [x] Implemented `cloneTemplateContractLine` utility and integrated it with API and server-action entry points for client contract assignments (`ContractLineService.assignPlanToClient`, client contract apply/add flows). Assignments now populate the new `client_contract_*` tables directly (rip-and-replace path). - [x] Updated shared interfaces/schemas to expose template linkage fields consumed by the revised services. - [ ] Update billing engine/report consumers to rely on `client_contract_line_pricing` and related tables (pending once data migration completes). ## Phase 3 – Frontend Template Wizard & Client Wizard Enhancements **Goals:** Provide UX for managing templates and applying them when creating client contracts. - Create Contract Template Wizard: - New component `server/src/components/billing-dashboard/contracts/ContractTemplateWizard.tsx` based on `ContractWizard.tsx` but omitting rate/quantity inputs. - Update routing in `BillingDashboard.tsx` to expose template management section. - Add API hooks in `server/src/components/billing-dashboard/contracts/hooks.ts` for template CRUD (`useCreateContractTemplate`, `useUpdateContractTemplate`). - Ensure UI surfaces template metadata (line types, services) with ability to add/remove lines and attach services. - Update existing `ContractWizard.tsx` to support selecting a template: - Add initial step that lists templates via new API. - On template selection, pre-fill line items, services, default descriptions. - Prompt user to enter client-specific pricing/quantities before finalizing. - Handle optional template metadata hints (e.g., recommended billing cadence) in the UI. - Split the contract detail surface into dedicated components: - [x] `ContractTemplateDetail.tsx` focused on template metadata, line composition, and recommended usage notes. - [ ] `ClientContractDetail.tsx` focused on client-specific terms (billing cycles, PO info, assignments) with clear linkage back to the originating template. - Update routing so the Billing Dashboard chooses the appropriate detail component based on whether the record is a template or a client assignment, and provide navigation between the two where relevant. - Refresh jest/react-testing-library coverage for wizards (`server/src/components/billing-dashboard/contracts/__tests__/`). ## Phase 4 – Data Migration & Backfill **Goals:** Move existing pricing data from templates to client contracts and link instances to their originating templates. - [x] Author migration script in `server/scripts/contract-template-decoupling.ts`: - For each `client_contract` referencing a `contract_id`, set `template_contract_id`. - For each `client_contract_line`, set `template_contract_line_id`. - Copy pricing fields from template tables into new client-instance tables: `contract_line_fixed_config.base_rate`, `contract_line_services.quantity/custom_rate`, `contract_line_service_bucket_config.total_minutes/overage_rate`, hourly configs, usage configs, and discount links. - Populate `client_contract_line_pricing` using data from `contract_line_mappings.custom_rate` and fixed-config fallbacks; ensure historical overrides preserved. - Null out or remove template pricing columns once client data is populated, leaving only optional defaults. - Provide dry-run mode and logging (tenant ID, contract ID counts). - Write verification queries (appendix) to ensure no non-null pricing remains on templates. - Coordinate deployment window; run migration in staging then production. ## Phase 5 – QA, Rollout, & Monitoring **Goals:** Validate functionality end-to-end and ensure stability after launch. - Manual QA checklist: - Create new template, apply to new client contract, confirm pricing only captured in client contract. - Update template and ensure existing client contracts unaffected. - Regression test billing runs/invoice generation. - Automation updates: - Extend Playwright flow `ee/server/src/__tests__/integration/contract-wizard-happy-path.playwright.test.ts` to cover template selection. - Update Cypress/Playwright dashboards (if any) to reflect new UI navigation. - Monitoring: - Add logs/metrics in contract creation endpoints to monitor template usage. - Add alert if billing engine encounters template pricing (should be null). - Launch plan: - Enable feature flag `billing.contractTemplatesV2` for internal tenants first. - Roll out to all tenants after one billing cycle. ## Appendix - **Schema Change Checklist:** - Add Knex migration files covering forward changes and matching rollback scripts. - Create SQL migrations for production deployment (`shared/db/migrations/20251020120000_contract_template_overhaul.sql`). - Add rollback scripts in `shared/db/migrations/rollback/`. - **Documentation:** - Update `docs/billing.md` and `docs/overview.md` sections on contracts. - Add how-to guide under `ee/docs/guides/contract-templates.md` once implemented. - **Decisions (2025-02-14):** - No third-party integrations currently ingest template pricing fields. Code search across `sdk/*`, `shared/**/*`, and enterprise services shows `contract_line_mappings.custom_rate` is only read by internal billing engines, reports, and UI controllers (`server/src/lib/reports/definitions/contracts/*.ts`, `server/src/lib/api/services/ContractLineService.ts`). Safe to migrate pricing without breaking external consumers; document in release notes for partners. - Contract template metadata will include a `recommended_services` array (service IDs plus optional notes) alongside UI hint fields in `template_metadata`. Wizard will surface these recommendations when initializing client contracts, while actual service bundling remains client-specific via `client_contract_services`.