feat: add Board Planning module with investment/assessment scenario modeling
Implements Phase 11 Forecasting Tools - a "what-if" scenario planning system for HOA boards to model financial decisions before committing. Backend: - 3 new tenant-scoped tables: board_scenarios, scenario_investments, scenario_assessments - Migration script (013) for existing tenants - Full CRUD service for scenarios, investments, and assessments - Projection engine adapted from cash flow forecast with investment/assessment deltas - Scenario comparison endpoint (up to 4 scenarios) - Investment execution flow: converts planned → real investment_accounts + journal entry Frontend: - New "Board Planning" sidebar section with 3 pages - Investment Scenarios: list, create, detail with investments table + timeline - Assessment Scenarios: list, create, detail with changes table - Scenario Comparison: multi-select overlay chart + summary metrics - Shared components: ProjectionChart, InvestmentTimeline, ScenarioCard, forms - AI Recommendation → Investment Plan integration (Story 1A) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
83
db/migrations/013-board-planning.sql
Normal file
83
db/migrations/013-board-planning.sql
Normal file
@@ -0,0 +1,83 @@
|
||||
-- Migration 013: Board Planning tables (scenarios, investments, assessments)
|
||||
-- Applies to all existing tenant schemas
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
tenant_schema TEXT;
|
||||
BEGIN
|
||||
FOR tenant_schema IN
|
||||
SELECT schema_name FROM information_schema.schemata
|
||||
WHERE schema_name LIKE 'tenant_%'
|
||||
LOOP
|
||||
-- Board Scenarios
|
||||
EXECUTE format('
|
||||
CREATE TABLE IF NOT EXISTS %I.board_scenarios (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
scenario_type VARCHAR(30) NOT NULL CHECK (scenario_type IN (''investment'', ''assessment'')),
|
||||
status VARCHAR(20) DEFAULT ''draft'' CHECK (status IN (''draft'', ''active'', ''approved'', ''archived'')),
|
||||
projection_months INTEGER DEFAULT 36,
|
||||
projection_cache JSONB,
|
||||
projection_cached_at TIMESTAMPTZ,
|
||||
created_by UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
)', tenant_schema);
|
||||
|
||||
-- Scenario Investments
|
||||
EXECUTE format('
|
||||
CREATE TABLE IF NOT EXISTS %I.scenario_investments (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
scenario_id UUID NOT NULL REFERENCES %I.board_scenarios(id) ON DELETE CASCADE,
|
||||
source_recommendation_id UUID,
|
||||
label VARCHAR(255) NOT NULL,
|
||||
investment_type VARCHAR(50) CHECK (investment_type IN (''cd'', ''money_market'', ''treasury'', ''savings'', ''other'')),
|
||||
fund_type VARCHAR(20) NOT NULL CHECK (fund_type IN (''operating'', ''reserve'')),
|
||||
principal DECIMAL(15,2) NOT NULL,
|
||||
interest_rate DECIMAL(6,4),
|
||||
term_months INTEGER,
|
||||
institution VARCHAR(255),
|
||||
purchase_date DATE,
|
||||
maturity_date DATE,
|
||||
auto_renew BOOLEAN DEFAULT FALSE,
|
||||
executed_investment_id UUID,
|
||||
notes TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
)', tenant_schema, tenant_schema);
|
||||
|
||||
-- Scenario Assessments
|
||||
EXECUTE format('
|
||||
CREATE TABLE IF NOT EXISTS %I.scenario_assessments (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
scenario_id UUID NOT NULL REFERENCES %I.board_scenarios(id) ON DELETE CASCADE,
|
||||
change_type VARCHAR(30) NOT NULL CHECK (change_type IN (''dues_increase'', ''special_assessment'', ''dues_decrease'')),
|
||||
label VARCHAR(255) NOT NULL,
|
||||
target_fund VARCHAR(20) CHECK (target_fund IN (''operating'', ''reserve'', ''both'')),
|
||||
percentage_change DECIMAL(6,3),
|
||||
flat_amount_change DECIMAL(10,2),
|
||||
special_total DECIMAL(15,2),
|
||||
special_per_unit DECIMAL(10,2),
|
||||
special_installments INTEGER DEFAULT 1,
|
||||
effective_date DATE NOT NULL,
|
||||
end_date DATE,
|
||||
applies_to_group_id UUID,
|
||||
notes TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
)', tenant_schema, tenant_schema);
|
||||
|
||||
-- Indexes
|
||||
EXECUTE format('CREATE INDEX IF NOT EXISTS idx_%s_bs_type_status ON %I.board_scenarios(scenario_type, status)',
|
||||
replace(tenant_schema, '.', '_'), tenant_schema);
|
||||
EXECUTE format('CREATE INDEX IF NOT EXISTS idx_%s_si_scenario ON %I.scenario_investments(scenario_id)',
|
||||
replace(tenant_schema, '.', '_'), tenant_schema);
|
||||
EXECUTE format('CREATE INDEX IF NOT EXISTS idx_%s_sa_scenario ON %I.scenario_assessments(scenario_id)',
|
||||
replace(tenant_schema, '.', '_'), tenant_schema);
|
||||
|
||||
RAISE NOTICE 'Board planning tables created for schema: %', tenant_schema;
|
||||
END LOOP;
|
||||
END $$;
|
||||
Reference in New Issue
Block a user