Adds budget planning capability under Board Planning, allowing HOA boards to model future year budgets with configurable per-year inflation rates. Backend: - New budget_plans + budget_plan_lines tables (migration 014) - BudgetPlanningService: CRUD, inflation generation (per-month preservation), status workflow (planning → approved → ratified), ratify-to-official copy - 8 new API endpoints on board-planning controller - Projection engine (both board-planning and reports) now falls back to planned budgets via UNION ALL query when no official budget exists - Extended year range from 3 to dynamic based on projection months Frontend: - BudgetPlanningPage with monthly grid table (mirrors BudgetsPage pattern) - Year selector, inflation rate control, status progression buttons - Inline editing with save, confirmation modals for status changes - Manual edit tracking with visual indicator - Summary cards for income/expense totals Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
55 lines
2.3 KiB
SQL
55 lines
2.3 KiB
SQL
-- Migration: Add budget_plans and budget_plan_lines tables to all tenant schemas
|
|
DO $migration$
|
|
DECLARE
|
|
s TEXT;
|
|
BEGIN
|
|
FOR s IN
|
|
SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'tenant_%'
|
|
LOOP
|
|
-- budget_plans
|
|
EXECUTE format('
|
|
CREATE TABLE IF NOT EXISTS %I.budget_plans (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
fiscal_year INTEGER NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT ''planning'' CHECK (status IN (''planning'', ''approved'', ''ratified'')),
|
|
base_year INTEGER NOT NULL,
|
|
inflation_rate DECIMAL(5,2) NOT NULL DEFAULT 2.50,
|
|
notes TEXT,
|
|
created_by UUID,
|
|
approved_by UUID,
|
|
approved_at TIMESTAMPTZ,
|
|
ratified_by UUID,
|
|
ratified_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
UNIQUE(fiscal_year)
|
|
)', s);
|
|
|
|
-- budget_plan_lines
|
|
EXECUTE format('
|
|
CREATE TABLE IF NOT EXISTS %I.budget_plan_lines (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
budget_plan_id UUID NOT NULL REFERENCES %I.budget_plans(id) ON DELETE CASCADE,
|
|
account_id UUID NOT NULL REFERENCES %I.accounts(id),
|
|
fund_type VARCHAR(20) NOT NULL CHECK (fund_type IN (''operating'', ''reserve'')),
|
|
jan DECIMAL(12,2) DEFAULT 0, feb DECIMAL(12,2) DEFAULT 0,
|
|
mar DECIMAL(12,2) DEFAULT 0, apr DECIMAL(12,2) DEFAULT 0,
|
|
may DECIMAL(12,2) DEFAULT 0, jun DECIMAL(12,2) DEFAULT 0,
|
|
jul DECIMAL(12,2) DEFAULT 0, aug DECIMAL(12,2) DEFAULT 0,
|
|
sep DECIMAL(12,2) DEFAULT 0, oct DECIMAL(12,2) DEFAULT 0,
|
|
nov DECIMAL(12,2) DEFAULT 0, dec_amt DECIMAL(12,2) DEFAULT 0,
|
|
is_manually_adjusted BOOLEAN DEFAULT FALSE,
|
|
notes TEXT,
|
|
UNIQUE(budget_plan_id, account_id, fund_type)
|
|
)', s, s, s);
|
|
|
|
-- Indexes
|
|
EXECUTE format('CREATE INDEX IF NOT EXISTS idx_%s_bp_year ON %I.budget_plans(fiscal_year)', replace(s, 'tenant_', ''), s);
|
|
EXECUTE format('CREATE INDEX IF NOT EXISTS idx_%s_bp_status ON %I.budget_plans(status)', replace(s, 'tenant_', ''), s);
|
|
EXECUTE format('CREATE INDEX IF NOT EXISTS idx_%s_bpl_plan ON %I.budget_plan_lines(budget_plan_id)', replace(s, 'tenant_', ''), s);
|
|
|
|
RAISE NOTICE 'Migrated schema: %', s;
|
|
END LOOP;
|
|
END;
|
|
$migration$;
|