Files
HOA_Financial_Platform/db/seed/seed.sql
olsch01 01502e07bc Implement Phase 2 features: roles, assessment groups, budget import, Kanban
- Add hierarchical roles: SuperUser Admin (is_superadmin flag), Tenant Admin,
  Tenant User with separate /admin route and admin panel
- Add Assessment Groups module for property type-based assessment rates
  (SFHs, Condos, Estate Lots with different regular/special rates)
- Enhance Chart of Accounts: initial balance on create (with journal entry),
  archive/restore accounts, edit all fields including account number & fund type
- Add Budget CSV import with downloadable template and account mapping
- Add Capital Projects Kanban board with drag-and-drop between year columns,
  table/kanban view toggle, and PDF export via browser print
- Update seed data with assessment groups, second test user, superadmin flag
- Create repeatable reseed.sh script for clean database population
- Fix AgingReportPage Mantine v7 Table prop compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:28:46 -05:00

841 lines
46 KiB
SQL

-- ============================================================
-- HOA LedgerIQ - Comprehensive Seed Data
-- "Sunrise Valley HOA" - 50 units, full year of financial data
-- ============================================================
-- This script:
-- 1. Creates a test user + organization
-- 2. Provisions the tenant schema
-- 3. Seeds 50 units with homeowner data
-- 4. Creates 12 months of assessment invoices
-- 5. Creates payments (some units are delinquent)
-- 6. Creates vendor records and expense transactions
-- 7. Creates budgets for current year
-- 8. Creates reserve components, investments, capital projects
-- ============================================================
-- Enable UUID generation
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- ============================================================
-- 1. Create test user and organization
-- ============================================================
DO $$
DECLARE
v_user_id UUID;
v_org_id UUID;
v_schema TEXT := 'tenant_sunrise_valley';
v_year INT := EXTRACT(YEAR FROM CURRENT_DATE)::INT;
v_month INT;
v_unit_id UUID;
v_invoice_id UUID;
v_je_id UUID;
v_fp_id UUID;
v_ar_id UUID;
v_cash_id UUID;
v_income_id UUID;
v_expense_account_id UUID;
v_unit_rec RECORD;
v_vendor_rec RECORD;
v_acct_rec RECORD;
v_inv_num TEXT;
v_amount NUMERIC;
v_inv_date DATE;
v_due_date DATE;
v_pay_date DATE;
v_pay_amount NUMERIC;
BEGIN
-- Check if user exists
SELECT id INTO v_user_id FROM shared.users WHERE email = 'admin@sunrisevalley.org';
IF v_user_id IS NULL THEN
INSERT INTO shared.users (id, email, password_hash, first_name, last_name, is_superadmin)
VALUES (
uuid_generate_v4(),
'admin@sunrisevalley.org',
-- bcrypt hash of 'password123'
'$2b$10$1mtM00QBNQpAsyopajk3BeFY5DdxksvRYuM1E8qB.ePjCIYkfHMHO',
'Sarah',
'Johnson',
true
) RETURNING id INTO v_user_id;
END IF;
-- Check if org exists
SELECT id INTO v_org_id FROM shared.organizations WHERE schema_name = v_schema;
IF v_org_id IS NULL THEN
INSERT INTO shared.organizations (id, name, subdomain, address_line1, city, state, zip_code, schema_name)
VALUES (
uuid_generate_v4(),
'Sunrise Valley HOA',
'sunrise-valley',
'100 Sunrise Valley Drive',
'Scottsdale',
'AZ',
'85255',
v_schema
) RETURNING id INTO v_org_id;
INSERT INTO shared.user_organizations (user_id, organization_id, role)
VALUES (v_user_id, v_org_id, 'president');
END IF;
-- Create a second test user (viewer/homeowner)
DECLARE v_viewer_id UUID;
BEGIN
SELECT id INTO v_viewer_id FROM shared.users WHERE email = 'viewer@sunrisevalley.org';
IF v_viewer_id IS NULL THEN
INSERT INTO shared.users (id, email, password_hash, first_name, last_name, is_superadmin)
VALUES (
uuid_generate_v4(),
'viewer@sunrisevalley.org',
'$2b$10$1mtM00QBNQpAsyopajk3BeFY5DdxksvRYuM1E8qB.ePjCIYkfHMHO',
'Mike',
'Resident',
false
) RETURNING id INTO v_viewer_id;
INSERT INTO shared.user_organizations (user_id, organization_id, role)
VALUES (v_viewer_id, v_org_id, 'homeowner');
END IF;
END;
-- ============================================================
-- 2. Create tenant schema (if not exists)
-- ============================================================
EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I', v_schema);
-- Create tables in tenant schema
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.accounts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
account_number INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
account_type VARCHAR(50) NOT NULL,
fund_type VARCHAR(20) NOT NULL,
parent_account_id UUID,
is_1099_reportable BOOLEAN DEFAULT FALSE,
is_active BOOLEAN DEFAULT TRUE,
is_system BOOLEAN DEFAULT FALSE,
balance DECIMAL(15,2) DEFAULT 0.00,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(account_number)
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.fiscal_periods (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
year INTEGER NOT NULL,
month INTEGER NOT NULL,
status VARCHAR(20) DEFAULT ''open'',
closed_by UUID,
closed_at TIMESTAMPTZ,
locked_by UUID,
locked_at TIMESTAMPTZ,
UNIQUE(year, month)
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.journal_entries (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
entry_date DATE NOT NULL,
description TEXT NOT NULL,
reference_number VARCHAR(100),
entry_type VARCHAR(50) NOT NULL,
fiscal_period_id UUID NOT NULL,
source_type VARCHAR(50),
source_id UUID,
is_posted BOOLEAN DEFAULT FALSE,
posted_by UUID,
posted_at TIMESTAMPTZ,
is_void BOOLEAN DEFAULT FALSE,
voided_by UUID,
voided_at TIMESTAMPTZ,
void_reason TEXT,
created_by UUID NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.journal_entry_lines (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
journal_entry_id UUID NOT NULL,
account_id UUID NOT NULL,
debit DECIMAL(15,2) DEFAULT 0.00,
credit DECIMAL(15,2) DEFAULT 0.00,
memo TEXT
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.assessment_groups (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
description TEXT,
regular_assessment DECIMAL(10,2) NOT NULL DEFAULT 0.00,
special_assessment DECIMAL(10,2) DEFAULT 0.00,
unit_count INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.units (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
unit_number VARCHAR(50) NOT NULL UNIQUE,
address_line1 VARCHAR(255),
address_line2 VARCHAR(255),
city VARCHAR(100),
state VARCHAR(2),
zip_code VARCHAR(10),
square_footage INTEGER,
lot_size DECIMAL(10,2),
owner_user_id UUID,
owner_name VARCHAR(255),
owner_email VARCHAR(255),
owner_phone VARCHAR(20),
is_rented BOOLEAN DEFAULT FALSE,
monthly_assessment DECIMAL(10,2),
assessment_group_id UUID,
status VARCHAR(20) DEFAULT ''active'',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.invoices (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
invoice_number VARCHAR(50) NOT NULL UNIQUE,
unit_id UUID NOT NULL,
invoice_date DATE NOT NULL,
due_date DATE NOT NULL,
invoice_type VARCHAR(50) NOT NULL,
description TEXT,
amount DECIMAL(10,2) NOT NULL,
amount_paid DECIMAL(10,2) DEFAULT 0.00,
status VARCHAR(20) DEFAULT ''draft'',
journal_entry_id UUID,
sent_at TIMESTAMPTZ,
paid_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.payments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
unit_id UUID NOT NULL,
invoice_id UUID,
payment_date DATE NOT NULL,
amount DECIMAL(10,2) NOT NULL,
payment_method VARCHAR(50),
reference_number VARCHAR(100),
stripe_payment_id VARCHAR(255),
status VARCHAR(20) DEFAULT ''completed'',
journal_entry_id UUID,
received_by UUID,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.vendors (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
contact_name VARCHAR(255),
email VARCHAR(255),
phone VARCHAR(20),
address_line1 VARCHAR(255),
address_line2 VARCHAR(255),
city VARCHAR(100),
state VARCHAR(2),
zip_code VARCHAR(10),
tax_id VARCHAR(20),
is_1099_eligible BOOLEAN DEFAULT FALSE,
default_account_id UUID,
is_active BOOLEAN DEFAULT TRUE,
ytd_payments DECIMAL(15,2) DEFAULT 0.00,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.budgets (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
fiscal_year INTEGER NOT NULL,
account_id UUID NOT NULL,
fund_type VARCHAR(20) NOT NULL,
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,
notes TEXT,
UNIQUE(fiscal_year, account_id, fund_type)
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.reserve_components (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
category VARCHAR(100),
description TEXT,
useful_life_years INTEGER NOT NULL,
remaining_life_years DECIMAL(5,1),
replacement_cost DECIMAL(15,2) NOT NULL,
current_fund_balance DECIMAL(15,2) DEFAULT 0.00,
annual_contribution DECIMAL(12,2) DEFAULT 0.00,
last_replacement_date DATE,
next_replacement_date DATE,
condition_rating INTEGER,
account_id UUID,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.investment_accounts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
institution VARCHAR(255),
account_number_last4 VARCHAR(4),
investment_type VARCHAR(50),
fund_type VARCHAR(20) NOT NULL,
principal DECIMAL(15,2) NOT NULL,
interest_rate DECIMAL(6,4),
maturity_date DATE,
purchase_date DATE,
current_value DECIMAL(15,2),
account_id UUID,
is_active BOOLEAN DEFAULT TRUE,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I.capital_projects (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
description TEXT,
estimated_cost DECIMAL(15,2) NOT NULL,
actual_cost DECIMAL(15,2),
target_year INTEGER NOT NULL,
target_month INTEGER,
status VARCHAR(20) DEFAULT ''planned'',
reserve_component_id UUID,
fund_source VARCHAR(20),
priority INTEGER DEFAULT 3,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)', v_schema);
-- ============================================================
-- 3. Seed Chart of Accounts
-- ============================================================
-- Clear existing data if re-running
EXECUTE format('DELETE FROM %I.journal_entry_lines', v_schema);
EXECUTE format('DELETE FROM %I.journal_entries', v_schema);
EXECUTE format('DELETE FROM %I.payments', v_schema);
EXECUTE format('DELETE FROM %I.invoices', v_schema);
EXECUTE format('DELETE FROM %I.budgets', v_schema);
EXECUTE format('DELETE FROM %I.capital_projects', v_schema);
EXECUTE format('DELETE FROM %I.investment_accounts', v_schema);
EXECUTE format('DELETE FROM %I.reserve_components', v_schema);
EXECUTE format('DELETE FROM %I.vendors', v_schema);
EXECUTE format('DELETE FROM %I.units', v_schema);
EXECUTE format('DELETE FROM %I.assessment_groups', v_schema);
EXECUTE format('DELETE FROM %I.fiscal_periods', v_schema);
EXECUTE format('DELETE FROM %I.accounts', v_schema);
-- Insert chart of accounts
EXECUTE format('INSERT INTO %I.accounts (account_number, name, account_type, fund_type, is_1099_reportable, is_system) VALUES
(1000, ''Operating Cash - Checking'', ''asset'', ''operating'', false, true),
(1010, ''Operating Cash - Savings'', ''asset'', ''operating'', false, true),
(1100, ''Reserve Cash - Checking'', ''asset'', ''reserve'', false, true),
(1110, ''Reserve Cash - Savings'', ''asset'', ''reserve'', false, true),
(1120, ''Reserve Cash - Money Market'', ''asset'', ''reserve'', false, true),
(1130, ''Reserve Cash - CDs'', ''asset'', ''reserve'', false, true),
(1200, ''Accounts Receivable - Assessments'', ''asset'', ''operating'', false, true),
(1210, ''Accounts Receivable - Late Fees'', ''asset'', ''operating'', false, true),
(1300, ''Prepaid Insurance'', ''asset'', ''operating'', false, true),
(2000, ''Accounts Payable'', ''liability'', ''operating'', false, true),
(2100, ''Accrued Expenses'', ''liability'', ''operating'', false, true),
(2200, ''Prepaid Assessments'', ''liability'', ''operating'', false, true),
(3000, ''Operating Fund Balance'', ''equity'', ''operating'', false, true),
(3100, ''Reserve Fund Balance'', ''equity'', ''reserve'', false, true),
(3200, ''Retained Earnings'', ''equity'', ''operating'', false, true),
(4000, ''Regular Assessments'', ''income'', ''operating'', false, true),
(4010, ''Special Assessments'', ''income'', ''operating'', false, true),
(4100, ''Late Fees'', ''income'', ''operating'', false, true),
(4200, ''Interest Income - Operating'', ''income'', ''operating'', false, true),
(4210, ''Interest Income - Reserve'', ''income'', ''reserve'', false, true),
(4300, ''Transfer Fees'', ''income'', ''operating'', false, false),
(4500, ''Other Income'', ''income'', ''operating'', false, false),
(4600, ''Reserve Contributions'', ''income'', ''reserve'', false, true),
(5000, ''Management Fees'', ''expense'', ''operating'', true, true),
(5100, ''Insurance - Property'', ''expense'', ''operating'', false, true),
(5110, ''Insurance - D&O'', ''expense'', ''operating'', false, true),
(5200, ''Utilities - Water/Sewer'', ''expense'', ''operating'', false, true),
(5210, ''Utilities - Electric (Common)'', ''expense'', ''operating'', false, true),
(5230, ''Utilities - Trash/Recycling'', ''expense'', ''operating'', false, true),
(5300, ''Landscape Maintenance'', ''expense'', ''operating'', true, true),
(5400, ''Pool Maintenance'', ''expense'', ''operating'', true, true),
(5500, ''Building Maintenance'', ''expense'', ''operating'', false, true),
(5510, ''Janitorial'', ''expense'', ''operating'', true, true),
(5600, ''Pest Control'', ''expense'', ''operating'', true, true),
(5700, ''Legal Fees'', ''expense'', ''operating'', true, true),
(5800, ''Accounting/Audit Fees'', ''expense'', ''operating'', true, true),
(5900, ''Office & Admin Expenses'', ''expense'', ''operating'', false, true),
(5920, ''Bank Fees'', ''expense'', ''operating'', false, true),
(6000, ''Repairs & Maintenance'', ''expense'', ''operating'', false, true),
(6500, ''Contingency/Miscellaneous'', ''expense'', ''operating'', false, true),
(7000, ''Reserve - Roof Replacement'', ''expense'', ''reserve'', false, true),
(7100, ''Reserve - Paving/Asphalt'', ''expense'', ''reserve'', false, true),
(7200, ''Reserve - Pool Renovation'', ''expense'', ''reserve'', false, true),
(7400, ''Reserve - Painting/Exterior'', ''expense'', ''reserve'', false, true),
(7500, ''Reserve - Fencing'', ''expense'', ''reserve'', false, true)
', v_schema);
-- ============================================================
-- 4. Create fiscal periods (current year + previous year)
-- ============================================================
FOR v_month IN 1..12 LOOP
EXECUTE format('INSERT INTO %I.fiscal_periods (year, month, status) VALUES ($1, $2, $3)', v_schema)
USING v_year, v_month, CASE WHEN v_month <= EXTRACT(MONTH FROM CURRENT_DATE)::INT THEN 'open' ELSE 'open' END;
EXECUTE format('INSERT INTO %I.fiscal_periods (year, month, status) VALUES ($1, $2, $3)', v_schema)
USING v_year - 1, v_month, 'closed';
END LOOP;
-- ============================================================
-- 4b. Seed Assessment Groups
-- ============================================================
EXECUTE format('INSERT INTO %I.assessment_groups (name, description, regular_assessment, special_assessment, unit_count) VALUES
(''Single Family Homes'', ''Standard single family detached homes (Units 1-20)'', 350.00, 0.00, 20),
(''Patio Homes'', ''Medium-sized patio homes (Units 21-35)'', 425.00, 0.00, 15),
(''Estate Lots'', ''Large estate lots (Units 36-50)'', 500.00, 75.00, 15)
', v_schema);
-- ============================================================
-- 5. Seed 50 units
-- ============================================================
DECLARE
v_first_names TEXT[] := ARRAY['James','Mary','Robert','Patricia','John','Jennifer','Michael','Linda',
'David','Elizabeth','William','Barbara','Richard','Susan','Joseph','Jessica','Thomas','Sarah',
'Christopher','Karen','Charles','Lisa','Daniel','Nancy','Matthew','Betty','Anthony','Margaret',
'Mark','Sandra','Donald','Ashley','Steven','Dorothy','Paul','Kimberly','Andrew','Emily',
'Joshua','Donna','Kenneth','Michelle','Kevin','Carol','Brian','Amanda','George','Melissa',
'Timothy','Deborah'];
v_last_names TEXT[] := ARRAY['Smith','Johnson','Williams','Brown','Jones','Garcia','Miller','Davis',
'Rodriguez','Martinez','Hernandez','Lopez','Gonzalez','Wilson','Anderson','Thomas','Taylor',
'Moore','Jackson','Martin','Lee','Perez','Thompson','White','Harris','Sanchez','Clark',
'Ramirez','Lewis','Robinson','Walker','Young','Allen','King','Wright','Scott','Torres',
'Nguyen','Hill','Flores','Green','Adams','Nelson','Baker','Hall','Rivera','Campbell',
'Mitchell','Carter','Roberts'];
v_unit_num INT;
v_assess NUMERIC;
v_ag_id UUID;
v_ag_name TEXT;
BEGIN
FOR v_unit_num IN 1..50 LOOP
-- Vary assessment based on unit size and assign assessment group
IF v_unit_num <= 20 THEN
v_assess := 350.00;
v_ag_name := 'Single Family Homes';
ELSIF v_unit_num <= 35 THEN
v_assess := 425.00;
v_ag_name := 'Patio Homes';
ELSE
v_assess := 500.00;
v_ag_name := 'Estate Lots';
END IF;
EXECUTE format('SELECT id FROM %I.assessment_groups WHERE name = $1', v_schema) INTO v_ag_id USING v_ag_name;
EXECUTE format('INSERT INTO %I.units (unit_number, address_line1, city, state, zip_code, owner_name, owner_email, owner_phone, monthly_assessment, square_footage, assessment_group_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)', v_schema)
USING
LPAD(v_unit_num::TEXT, 3, '0'),
(100 + v_unit_num * 2)::TEXT || ' Sunrise Valley Drive',
'Scottsdale', 'AZ', '85255',
v_first_names[v_unit_num] || ' ' || v_last_names[v_unit_num],
LOWER(v_first_names[v_unit_num]) || '.' || LOWER(v_last_names[v_unit_num]) || '@email.com',
'(480) 555-' || LPAD((1000 + v_unit_num)::TEXT, 4, '0'),
v_assess,
CASE WHEN v_unit_num <= 20 THEN 1200 WHEN v_unit_num <= 35 THEN 1600 ELSE 2000 END,
v_ag_id;
END LOOP;
END;
-- ============================================================
-- 6. Get account IDs for transactions
-- ============================================================
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 1200', v_schema) INTO v_ar_id;
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 1000', v_schema) INTO v_cash_id;
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 4000', v_schema) INTO v_income_id;
-- ============================================================
-- 7. Create 12 months of invoices + payments
-- ============================================================
FOR v_month IN 1..12 LOOP
-- Get fiscal period
EXECUTE format('SELECT id FROM %I.fiscal_periods WHERE year = $1 AND month = $2', v_schema)
INTO v_fp_id USING v_year, v_month;
v_inv_date := make_date(v_year, v_month, 1);
v_due_date := make_date(v_year, v_month, 15);
-- Create invoice and payment for each unit
FOR v_unit_rec IN EXECUTE format('SELECT id, unit_number, monthly_assessment FROM %I.units ORDER BY unit_number', v_schema) LOOP
v_inv_num := 'INV-' || v_year || LPAD(v_month::TEXT, 2, '0') || '-' || v_unit_rec.unit_number;
v_amount := v_unit_rec.monthly_assessment;
-- Skip future months
IF v_inv_date > CURRENT_DATE THEN
CONTINUE;
END IF;
-- Create invoice
EXECUTE format('INSERT INTO %I.invoices (invoice_number, unit_id, invoice_date, due_date, invoice_type, description, amount, status)
VALUES ($1, $2, $3, $4, ''regular_assessment'', $5, $6, ''sent'') RETURNING id', v_schema)
INTO v_invoice_id
USING v_inv_num, v_unit_rec.id, v_inv_date, v_due_date,
'Monthly Assessment - ' || TO_CHAR(v_inv_date, 'Month YYYY'), v_amount;
-- Create assessment journal entry (DR AR, CR Income)
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, source_type, source_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''assessment'', $3, ''invoice'', $4, true, NOW(), $5) RETURNING id', v_schema)
INTO v_je_id
USING v_inv_date, 'Assessment - Unit ' || v_unit_rec.unit_number, v_fp_id, v_invoice_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, $3, 0)', v_schema)
USING v_je_id, v_ar_id, v_amount;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, $3)', v_schema)
USING v_je_id, v_income_id, v_amount;
EXECUTE format('UPDATE %I.invoices SET journal_entry_id = $1 WHERE id = $2', v_schema) USING v_je_id, v_invoice_id;
-- Create payment (most units pay, some are delinquent)
-- Units 45-50 are delinquent for the last 3 months
-- Units 40-44 are late payers (paid partial)
IF v_unit_rec.unit_number::INT >= 45 AND v_month >= (EXTRACT(MONTH FROM CURRENT_DATE)::INT - 2) THEN
-- Delinquent - mark overdue, no payment
EXECUTE format('UPDATE %I.invoices SET status = ''overdue'' WHERE id = $1', v_schema) USING v_invoice_id;
ELSIF v_unit_rec.unit_number::INT >= 40 AND v_month >= (EXTRACT(MONTH FROM CURRENT_DATE)::INT - 1) THEN
-- Partial payment
v_pay_amount := v_amount * 0.5;
v_pay_date := v_due_date + INTERVAL '5 days';
IF v_pay_date <= CURRENT_DATE THEN
EXECUTE format('INSERT INTO %I.payments (unit_id, invoice_id, payment_date, amount, payment_method, reference_number, received_by)
VALUES ($1, $2, $3, $4, ''check'', $5, $6)', v_schema)
USING v_unit_rec.id, v_invoice_id, v_pay_date, v_pay_amount, 'CHK-' || v_unit_rec.unit_number || '-' || v_month, v_user_id;
EXECUTE format('UPDATE %I.invoices SET amount_paid = $1, status = ''partial'' WHERE id = $2', v_schema)
USING v_pay_amount, v_invoice_id;
-- Payment JE
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, source_type, is_posted, posted_at, created_by)
VALUES ($1, $2, ''payment'', $3, ''payment'', true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING v_pay_date, 'Payment - Unit ' || v_unit_rec.unit_number, v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, $3, 0)', v_schema)
USING v_je_id, v_cash_id, v_pay_amount;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, $3)', v_schema)
USING v_je_id, v_ar_id, v_pay_amount;
END IF;
ELSE
-- Full payment
v_pay_date := v_due_date - INTERVAL '3 days' + (random() * 10)::INT * INTERVAL '1 day';
IF v_pay_date > CURRENT_DATE THEN
v_pay_date := CURRENT_DATE;
END IF;
EXECUTE format('INSERT INTO %I.payments (unit_id, invoice_id, payment_date, amount, payment_method, reference_number, received_by)
VALUES ($1, $2, $3, $4, $5, $6, $7)', v_schema)
USING v_unit_rec.id, v_invoice_id, v_pay_date, v_amount,
CASE WHEN random() < 0.6 THEN 'ach' WHEN random() < 0.8 THEN 'check' ELSE 'credit_card' END,
'PAY-' || v_unit_rec.unit_number || '-' || v_month, v_user_id;
EXECUTE format('UPDATE %I.invoices SET amount_paid = $1, status = ''paid'', paid_at = $2 WHERE id = $3', v_schema)
USING v_amount, v_pay_date, v_invoice_id;
-- Payment JE
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, source_type, is_posted, posted_at, created_by)
VALUES ($1, $2, ''payment'', $3, ''payment'', true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING v_pay_date, 'Payment - Unit ' || v_unit_rec.unit_number, v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, $3, 0)', v_schema)
USING v_je_id, v_cash_id, v_amount;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, $3)', v_schema)
USING v_je_id, v_ar_id, v_amount;
END IF;
END LOOP; -- units
END LOOP; -- months
-- ============================================================
-- 8. Create vendors and expense transactions
-- ============================================================
EXECUTE format('INSERT INTO %I.vendors (name, contact_name, email, phone, address_line1, city, state, zip_code, tax_id, is_1099_eligible) VALUES
(''Desert Star Management'', ''Mike Peters'', ''mike@desertstar.com'', ''(480) 555-2000'', ''500 N Scottsdale Rd'', ''Scottsdale'', ''AZ'', ''85251'', ''82-1234567'', true),
(''Green Oasis Landscaping'', ''Rosa Gutierrez'', ''rosa@greenoasis.com'', ''(480) 555-2100'', ''1200 E Camelback'', ''Phoenix'', ''AZ'', ''85014'', ''82-2345678'', true),
(''AquaBlue Pool Service'', ''Tom Bradley'', ''tom@aquablue.com'', ''(480) 555-2200'', ''300 W Indian School'', ''Phoenix'', ''AZ'', ''85013'', ''82-3456789'', true),
(''Valley Pest Solutions'', ''Dan Kim'', ''dan@valleypest.com'', ''(480) 555-2300'', ''800 S Mill Ave'', ''Tempe'', ''AZ'', ''85281'', ''82-4567890'', true),
(''Southwest Legal Group'', ''Lisa Chen'', ''lisa@swlegal.com'', ''(480) 555-2400'', ''2 N Central Ave'', ''Phoenix'', ''AZ'', ''85004'', ''82-5678901'', true),
(''Saguaro CPA Group'', ''Jim Torres'', ''jim@saguarocpa.com'', ''(480) 555-2500'', ''4000 N Scottsdale Rd'', ''Scottsdale'', ''AZ'', ''85251'', ''82-6789012'', true),
(''CleanRight Janitorial'', ''Pat Morgan'', ''pat@cleanright.com'', ''(480) 555-2600'', ''1500 N Hayden'', ''Scottsdale'', ''AZ'', ''85257'', ''82-7890123'', true),
(''Arizona Insurance Group'', ''Amy Russell'', ''amy@azinsurance.com'', ''(480) 555-2700'', ''7000 E Shea Blvd'', ''Scottsdale'', ''AZ'', ''85254'', NULL, false)
', v_schema);
-- Link vendor default accounts
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5000) WHERE name = ''Desert Star Management''', v_schema, v_schema);
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5300) WHERE name = ''Green Oasis Landscaping''', v_schema, v_schema);
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5400) WHERE name = ''AquaBlue Pool Service''', v_schema, v_schema);
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5600) WHERE name = ''Valley Pest Solutions''', v_schema, v_schema);
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5700) WHERE name = ''Southwest Legal Group''', v_schema, v_schema);
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5800) WHERE name = ''Saguaro CPA Group''', v_schema, v_schema);
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5510) WHERE name = ''CleanRight Janitorial''', v_schema, v_schema);
EXECUTE format('UPDATE %I.vendors SET default_account_id = (SELECT id FROM %I.accounts WHERE account_number = 5100) WHERE name = ''Arizona Insurance Group''', v_schema, v_schema);
-- Create monthly expense transactions
FOR v_month IN 1..LEAST(12, EXTRACT(MONTH FROM CURRENT_DATE)::INT) LOOP
EXECUTE format('SELECT id FROM %I.fiscal_periods WHERE year = $1 AND month = $2', v_schema) INTO v_fp_id USING v_year, v_month;
-- Management Fee: $2,500/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5000', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 5), 'Management Fee - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 2500, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 2500)', v_schema) USING v_je_id, v_cash_id;
-- Landscape: $3,200/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5300', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 10), 'Landscape Maintenance - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 3200, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 3200)', v_schema) USING v_je_id, v_cash_id;
-- Pool: $800/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5400', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 10), 'Pool Maintenance - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 800, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 800)', v_schema) USING v_je_id, v_cash_id;
-- Utilities Water: $1,200/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5200', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 15), 'Water/Sewer - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 1200, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 1200)', v_schema) USING v_je_id, v_cash_id;
-- Electric: $650/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5210', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 18), 'Electric Common Areas - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 650, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 650)', v_schema) USING v_je_id, v_cash_id;
-- Trash: $450/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5230', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 20), 'Trash/Recycling - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 450, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 450)', v_schema) USING v_je_id, v_cash_id;
-- Pest Control: $200/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5600', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 25), 'Pest Control - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 200, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 200)', v_schema) USING v_je_id, v_cash_id;
-- Janitorial: $600/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5510', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 28), 'Janitorial Service - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 600, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 600)', v_schema) USING v_je_id, v_cash_id;
-- Reserve contribution: $2,750/mo
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 1100', v_schema) INTO v_expense_account_id;
DECLARE v_reserve_income_id UUID;
BEGIN
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 4600', v_schema) INTO v_reserve_income_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''transfer'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, v_month, 1), 'Reserve Contribution - ' || TO_CHAR(make_date(v_year, v_month, 1), 'Mon YYYY'), v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 2750, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 2750)', v_schema) USING v_je_id, v_reserve_income_id;
END;
END LOOP;
-- Quarterly expenses
-- Insurance (annual paid quarterly): $4,500/quarter
FOR v_month IN 1..4 LOOP
EXIT WHEN (v_month - 1) * 3 + 1 > EXTRACT(MONTH FROM CURRENT_DATE)::INT;
EXECUTE format('SELECT id FROM %I.fiscal_periods WHERE year = $1 AND month = $2', v_schema)
INTO v_fp_id USING v_year, (v_month - 1) * 3 + 1;
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5100', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, (v_month - 1) * 3 + 1, 1), 'Property Insurance - Q' || v_month, v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 4500, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 4500)', v_schema) USING v_je_id, v_cash_id;
END LOOP;
-- Annual Audit: $3,500 (January)
EXECUTE format('SELECT id FROM %I.fiscal_periods WHERE year = $1 AND month = 1', v_schema) INTO v_fp_id USING v_year;
EXECUTE format('SELECT id FROM %I.accounts WHERE account_number = 5800', v_schema) INTO v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entries (entry_date, description, entry_type, fiscal_period_id, is_posted, posted_at, created_by)
VALUES ($1, $2, ''manual'', $3, true, NOW(), $4) RETURNING id', v_schema)
INTO v_je_id
USING make_date(v_year, 1, 15), 'Annual Audit Fee', v_fp_id, v_user_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 3500, 0)', v_schema) USING v_je_id, v_expense_account_id;
EXECUTE format('INSERT INTO %I.journal_entry_lines (journal_entry_id, account_id, debit, credit) VALUES ($1, $2, 0, 3500)', v_schema) USING v_je_id, v_cash_id;
-- ============================================================
-- 9. Budgets for current year
-- ============================================================
-- Income budgets
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 17500, 17500, 17500, 17500, 17500, 17500, 17500, 17500, 17500, 17500, 17500, 17500
FROM %I.accounts WHERE account_number = 4000', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
FROM %I.accounts WHERE account_number = 4100', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''reserve'', 2750, 2750, 2750, 2750, 2750, 2750, 2750, 2750, 2750, 2750, 2750, 2750
FROM %I.accounts WHERE account_number = 4600', v_schema, v_schema)
USING v_year;
-- Expense budgets
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500
FROM %I.accounts WHERE account_number = 5000', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 4500, 0, 0, 4500, 0, 0, 4500, 0, 0, 4500, 0, 0
FROM %I.accounts WHERE account_number = 5100', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200
FROM %I.accounts WHERE account_number = 5200', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, 650
FROM %I.accounts WHERE account_number = 5210', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200
FROM %I.accounts WHERE account_number = 5300', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800
FROM %I.accounts WHERE account_number = 5400', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600
FROM %I.accounts WHERE account_number = 5510', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
FROM %I.accounts WHERE account_number = 5600', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450
FROM %I.accounts WHERE account_number = 5230', v_schema, v_schema)
USING v_year;
EXECUTE format('INSERT INTO %I.budgets (fiscal_year, account_id, fund_type, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec_amt)
SELECT $1, id, ''operating'', 3500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
FROM %I.accounts WHERE account_number = 5800', v_schema, v_schema)
USING v_year;
-- ============================================================
-- 10. Reserve components
-- ============================================================
EXECUTE format('INSERT INTO %I.reserve_components (name, category, description, useful_life_years, remaining_life_years, replacement_cost, current_fund_balance, annual_contribution, condition_rating, last_replacement_date, next_replacement_date) VALUES
(''Roof - Building A'', ''Roofing'', ''Tile roof on main building A'', 25, 18.0, 125000, 35000, 5000, 7, ''2016-06-01'', ''2041-06-01''),
(''Roof - Building B'', ''Roofing'', ''Tile roof on building B'', 25, 20.0, 95000, 20000, 3800, 8, ''2018-03-01'', ''2043-03-01''),
(''Parking Lot Resurfacing'', ''Paving'', ''Asphalt parking areas'', 15, 8.0, 85000, 47000, 5667, 6, ''2019-09-01'', ''2034-09-01''),
(''Pool Replaster'', ''Pool'', ''Replaster community pool'', 10, 4.0, 35000, 21000, 3500, 5, ''2020-04-01'', ''2030-04-01''),
(''Exterior Paint'', ''Painting'', ''Full exterior paint all buildings'', 8, 3.0, 65000, 48750, 8125, 4, ''2021-10-01'', ''2029-10-01''),
(''Perimeter Fencing'', ''Fencing'', ''Wrought iron perimeter fence'', 20, 14.0, 45000, 13500, 2250, 7, ''2018-01-01'', ''2038-01-01''),
(''Clubhouse HVAC'', ''HVAC'', ''Central HVAC system for clubhouse'', 15, 9.0, 28000, 11200, 1867, 6, ''2020-08-01'', ''2035-08-01''),
(''Irrigation System'', ''Landscape'', ''Landscape irrigation infrastructure'', 12, 6.0, 22000, 11000, 1833, 5, ''2020-01-01'', ''2032-01-01'')
', v_schema);
-- ============================================================
-- 11. Investment accounts
-- ============================================================
EXECUTE format('INSERT INTO %I.investment_accounts (name, institution, account_number_last4, investment_type, fund_type, principal, interest_rate, maturity_date, purchase_date, current_value) VALUES
(''Reserve CD - 12 Month'', ''Chase Bank'', ''4521'', ''cd'', ''reserve'', 50000, 5.2500, ''2027-01-15'', ''2026-01-15'', 51312.50),
(''Reserve CD - 6 Month'', ''Wells Fargo'', ''7834'', ''cd'', ''reserve'', 25000, 4.7500, ''2026-07-15'', ''2026-01-15'', 25593.75),
(''Reserve Money Market'', ''Schwab'', ''9912'', ''money_market'', ''reserve'', 75000, 4.5000, NULL, ''2025-06-01'', 77625.00),
(''Operating Savings'', ''Chase Bank'', ''3345'', ''savings'', ''operating'', 15000, 3.0000, NULL, ''2025-01-01'', 15450.00),
(''Treasury Bills - 3 Mo'', ''Fidelity'', ''6678'', ''treasury'', ''reserve'', 30000, 5.0000, ''2026-05-15'', ''2026-02-15'', 30375.00)
', v_schema);
-- ============================================================
-- 12. Capital projects (5-year plan)
-- ============================================================
EXECUTE format('INSERT INTO %I.capital_projects (name, description, estimated_cost, target_year, target_month, status, fund_source, priority) VALUES
(''Pool Heater Replacement'', ''Replace aging pool heater system'', 8500, $1, 4, ''approved'', ''reserve'', 2),
(''Landscape Renovation - Phase 1'', ''Update entry and common area landscaping'', 15000, $1, 5, ''in_progress'', ''reserve'', 2),
(''Security Camera Upgrade'', ''Replace analog cameras with HD IP system'', 12000, $1, 8, ''planned'', ''operating'', 3),
(''Parking Lot Seal Coat'', ''Seal coat and re-stripe parking areas'', 18000, $1 + 1, 3, ''planned'', ''reserve'', 2),
(''Clubhouse Furniture'', ''Replace common area furniture'', 8000, $1 + 1, 6, ''planned'', ''operating'', 4),
(''Exterior Paint - Building A'', ''Full exterior repaint of Building A'', 35000, $1 + 1, 9, ''planned'', ''reserve'', 1),
(''Playground Equipment'', ''New playground equipment and surfacing'', 22000, $1 + 2, 4, ''planned'', ''reserve'', 3),
(''Roof Repair - Building B'', ''Patch and repair sections of Building B roof'', 15000, $1 + 2, 7, ''planned'', ''reserve'', 2),
(''Irrigation System Overhaul'', ''Replace aging irrigation controllers and valves'', 12000, $1 + 3, 3, ''planned'', ''reserve'', 3),
(''Pool Replastering'', ''Complete pool replaster and tile work'', 35000, $1 + 3, 9, ''planned'', ''reserve'', 1),
(''Parking Lot Resurface'', ''Full asphalt resurfacing of parking areas'', 85000, $1 + 4, 5, ''planned'', ''reserve'', 1),
(''Perimeter Fence Repair'', ''Replace damaged fence sections and repaint'', 8000, $1 + 4, 8, ''planned'', ''reserve'', 4)
', v_schema) USING v_year;
RAISE NOTICE 'Seed data created successfully for Sunrise Valley HOA!';
RAISE NOTICE 'Admin Login: admin@sunrisevalley.org / password123 (SuperAdmin + President)';
RAISE NOTICE 'Viewer Login: viewer@sunrisevalley.org / password123 (Homeowner)';
END $$;