-- Financials Plugin Database Schema -- This schema is isolated in the plugin_financials namespace -- Chart of Accounts CREATE TABLE IF NOT EXISTS plugin_financials.chart_of_accounts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, account_code VARCHAR(20) NOT NULL, account_name VARCHAR(255) NOT NULL, account_type VARCHAR(50) NOT NULL, -- asset, liability, equity, revenue, expense parent_account_id UUID REFERENCES plugin_financials.chart_of_accounts(id), description TEXT, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL, UNIQUE(site_id, account_code) ); -- Budgets CREATE TABLE IF NOT EXISTS plugin_financials.budgets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, fiscal_year INTEGER NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL, total_amount DECIMAL(15,2) NOT NULL, status VARCHAR(20) DEFAULT 'draft', -- draft, active, closed created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Budget Items CREATE TABLE IF NOT EXISTS plugin_financials.budget_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), budget_id UUID NOT NULL REFERENCES plugin_financials.budgets(id) ON DELETE CASCADE, account_id UUID NOT NULL REFERENCES plugin_financials.chart_of_accounts(id), planned_amount DECIMAL(15,2) NOT NULL, actual_amount DECIMAL(15,2) DEFAULT 0, notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Transactions CREATE TABLE IF NOT EXISTS plugin_financials.transactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, transaction_date DATE NOT NULL, reference_number VARCHAR(50), description TEXT NOT NULL, transaction_type VARCHAR(20) NOT NULL, -- income, expense, transfer, opening_balance, adjustment amount DECIMAL(15,2) NOT NULL, currency VARCHAR(3) DEFAULT 'USD', status VARCHAR(20) DEFAULT 'pending', -- pending, approved, rejected, posted category VARCHAR(100), vendor_id UUID, unit_id UUID, payment_method VARCHAR(50), notes TEXT, attachments JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL, approved_by UUID, approved_at TIMESTAMP WITH TIME ZONE ); -- Transaction Lines (Double-entry accounting) CREATE TABLE IF NOT EXISTS plugin_financials.transaction_lines ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), transaction_id UUID NOT NULL REFERENCES plugin_financials.transactions(id) ON DELETE CASCADE, account_id UUID NOT NULL REFERENCES plugin_financials.chart_of_accounts(id), debit_amount DECIMAL(15,2) DEFAULT 0, credit_amount DECIMAL(15,2) DEFAULT 0, description TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Expenses CREATE TABLE IF NOT EXISTS plugin_financials.expenses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, expense_date DATE NOT NULL, vendor_name VARCHAR(255), vendor_id UUID, category VARCHAR(100) NOT NULL, description TEXT NOT NULL, amount DECIMAL(15,2) NOT NULL, tax_amount DECIMAL(15,2) DEFAULT 0, total_amount DECIMAL(15,2) NOT NULL, payment_status VARCHAR(20) DEFAULT 'pending', -- pending, paid, overdue payment_method VARCHAR(50), receipt_url VARCHAR(500), approved_by UUID, approved_at TIMESTAMP WITH TIME ZONE, notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Revenue/Income CREATE TABLE IF NOT EXISTS plugin_financials.revenue ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, revenue_date DATE NOT NULL, source VARCHAR(100) NOT NULL, -- maintenance_fees, late_fees, special_assessments, etc. description TEXT NOT NULL, amount DECIMAL(15,2) NOT NULL, unit_id UUID, resident_id UUID, payment_status VARCHAR(20) DEFAULT 'pending', -- pending, received, overdue payment_method VARCHAR(50), receipt_url VARCHAR(500), notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Unit Balance Tracking CREATE TABLE IF NOT EXISTS plugin_financials.unit_balances ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), unit_id UUID NOT NULL, site_id UUID NOT NULL, current_balance DECIMAL(15,2) DEFAULT 0, -- Current total balance (calculated from transactions) last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL, UNIQUE(unit_id, site_id) ); -- Invoices (for monthly fees, services, work orders, etc.) CREATE TABLE IF NOT EXISTS plugin_financials.invoices ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, unit_id UUID NOT NULL, invoice_number VARCHAR(50) NOT NULL, invoice_type VARCHAR(50) NOT NULL, -- monthly_fee, service, work_order, other description TEXT NOT NULL, amount DECIMAL(15,2) NOT NULL, due_date DATE NOT NULL, issue_date DATE NOT NULL DEFAULT CURRENT_DATE, status VARCHAR(20) DEFAULT 'pending', -- pending, paid, overdue, cancelled work_order_id UUID, -- Reference to work order if applicable payment_date DATE, payment_method VARCHAR(50), payment_reference VARCHAR(100), notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL, UNIQUE(site_id, invoice_number) ); -- Unit Monthly Fee Settings CREATE TABLE IF NOT EXISTS plugin_financials.unit_monthly_fees ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), unit_id UUID NOT NULL, site_id UUID NOT NULL, monthly_fee_amount DECIMAL(15,2) NOT NULL, effective_from DATE NOT NULL DEFAULT CURRENT_DATE, effective_to DATE, -- NULL means currently active fee_description TEXT DEFAULT 'Monthly maintenance fee', auto_generate_invoice BOOLEAN DEFAULT true, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL, UNIQUE(unit_id, effective_from) ); -- Payments (tracks all payments made against invoices) CREATE TABLE IF NOT EXISTS plugin_financials.payments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, unit_id UUID NOT NULL, invoice_id UUID REFERENCES plugin_financials.invoices(id), payment_amount DECIMAL(15,2) NOT NULL, payment_date DATE NOT NULL DEFAULT CURRENT_DATE, payment_method VARCHAR(50) NOT NULL, -- cash, check, bank_transfer, credit_card, etc. payment_reference VARCHAR(100), -- check number, transaction ID, etc. notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Balance History (audit trail for balance changes) CREATE TABLE IF NOT EXISTS plugin_financials.balance_history ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), unit_id UUID NOT NULL, site_id UUID NOT NULL, change_type VARCHAR(50) NOT NULL, -- invoice_added, payment_made, adjustment, starting_balance change_amount DECIMAL(15,2) NOT NULL, -- positive for charges, negative for payments balance_before DECIMAL(15,2) NOT NULL, balance_after DECIMAL(15,2) NOT NULL, reference_id UUID, -- invoice_id, payment_id, etc. reference_type VARCHAR(50), -- invoice, payment, adjustment description TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Indexes for performance CREATE INDEX IF NOT EXISTS idx_unit_balances_unit_id ON plugin_financials.unit_balances(unit_id); CREATE INDEX IF NOT EXISTS idx_invoices_unit_id ON plugin_financials.invoices(unit_id); CREATE INDEX IF NOT EXISTS idx_invoices_status ON plugin_financials.invoices(status); CREATE INDEX IF NOT EXISTS idx_invoices_due_date ON plugin_financials.invoices(due_date); CREATE INDEX IF NOT EXISTS idx_payments_unit_id ON plugin_financials.payments(unit_id); CREATE INDEX IF NOT EXISTS idx_payments_invoice_id ON plugin_financials.payments(invoice_id); CREATE INDEX IF NOT EXISTS idx_balance_history_unit_id ON plugin_financials.balance_history(unit_id); -- Financial Reports CREATE TABLE IF NOT EXISTS plugin_financials.reports ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, report_type VARCHAR(50) NOT NULL, -- income_statement, balance_sheet, cash_flow, budget_variance report_name VARCHAR(255) NOT NULL, report_date DATE NOT NULL, parameters JSONB, generated_by UUID NOT NULL, generated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), file_url VARCHAR(500), status VARCHAR(20) DEFAULT 'generated' -- generated, sent, archived ); -- Tax Settings CREATE TABLE IF NOT EXISTS plugin_financials.tax_settings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, tax_name VARCHAR(100) NOT NULL, tax_rate DECIMAL(5,4) NOT NULL, tax_type VARCHAR(20) NOT NULL, -- percentage, fixed is_active BOOLEAN DEFAULT true, applies_to VARCHAR(100), -- all, expenses, specific_categories effective_date DATE NOT NULL, end_date DATE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Financial Analytics CREATE TABLE IF NOT EXISTS plugin_financials.analytics ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, metric_name VARCHAR(100) NOT NULL, metric_value DECIMAL(15,2) NOT NULL, metric_date DATE NOT NULL, category VARCHAR(100), metadata JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Special Assessments CREATE TABLE IF NOT EXISTS plugin_financials.special_assessments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, assessment_name VARCHAR(255) NOT NULL, description TEXT NOT NULL, total_amount DECIMAL(15,2) NOT NULL, amount_per_unit DECIMAL(15,2) NOT NULL, due_date DATE NOT NULL, unit_count INTEGER NOT NULL, status VARCHAR(20) DEFAULT 'pending', -- pending, invoiced, collected, cancelled project_type VARCHAR(100), -- renovation, repair, upgrade, emergency created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Bank Accounts CREATE TABLE IF NOT EXISTS plugin_financials.bank_accounts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, account_name VARCHAR(255) NOT NULL, bank_name VARCHAR(255) NOT NULL, account_number VARCHAR(100), routing_number VARCHAR(50), account_type VARCHAR(50) DEFAULT 'checking', -- checking, savings, reserve ledger_account_id UUID REFERENCES plugin_financials.chart_of_accounts(id), current_balance DECIMAL(15,2) DEFAULT 0, currency VARCHAR(3) DEFAULT 'USD', is_active BOOLEAN DEFAULT true, notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Bank Statements CREATE TABLE IF NOT EXISTS plugin_financials.bank_statements ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), bank_account_id UUID NOT NULL REFERENCES plugin_financials.bank_accounts(id) ON DELETE CASCADE, site_id UUID NOT NULL, statement_date DATE NOT NULL, opening_balance DECIMAL(15,2) NOT NULL, closing_balance DECIMAL(15,2) NOT NULL, total_deposits DECIMAL(15,2) DEFAULT 0, total_withdrawals DECIMAL(15,2) DEFAULT 0, statement_file_url VARCHAR(500), is_reconciled BOOLEAN DEFAULT false, reconciled_date DATE, reconciled_by UUID, notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL ); -- Bank Statement Transactions CREATE TABLE IF NOT EXISTS plugin_financials.bank_statement_transactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), bank_statement_id UUID NOT NULL REFERENCES plugin_financials.bank_statements(id) ON DELETE CASCADE, transaction_date DATE NOT NULL, description TEXT NOT NULL, amount DECIMAL(15,2) NOT NULL, transaction_type VARCHAR(20) NOT NULL, -- deposit, withdrawal, fee, interest reference_number VARCHAR(100), is_matched BOOLEAN DEFAULT false, matched_transaction_id UUID, -- Links to plugin_financials.transactions if matched notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Bank Reconciliation Records CREATE TABLE IF NOT EXISTS plugin_financials.bank_reconciliation_records ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), site_id UUID NOT NULL, bank_account_id UUID NOT NULL REFERENCES plugin_financials.bank_accounts(id) ON DELETE CASCADE, statement_id UUID NOT NULL REFERENCES plugin_financials.bank_statements(id), reconciliation_date DATE NOT NULL, ledger_balance DECIMAL(15,2) NOT NULL, bank_balance DECIMAL(15,2) NOT NULL, outstanding_deposits DECIMAL(15,2) DEFAULT 0, outstanding_withdrawals DECIMAL(15,2) DEFAULT 0, adjustments DECIMAL(15,2) DEFAULT 0, reconciled_balance DECIMAL(15,2) NOT NULL, notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by UUID NOT NULL, approved_by UUID, approved_at TIMESTAMP WITH TIME ZONE ); -- Create indexes for better performance CREATE INDEX IF NOT EXISTS idx_chart_of_accounts_site_id ON plugin_financials.chart_of_accounts(site_id); CREATE INDEX IF NOT EXISTS idx_chart_of_accounts_account_type ON plugin_financials.chart_of_accounts(account_type); CREATE INDEX IF NOT EXISTS idx_budgets_site_id ON plugin_financials.budgets(site_id); CREATE INDEX IF NOT EXISTS idx_budgets_fiscal_year ON plugin_financials.budgets(fiscal_year); CREATE INDEX IF NOT EXISTS idx_transactions_site_id ON plugin_financials.transactions(site_id); CREATE INDEX IF NOT EXISTS idx_transactions_date ON plugin_financials.transactions(transaction_date); CREATE INDEX IF NOT EXISTS idx_transactions_type ON plugin_financials.transactions(transaction_type); CREATE INDEX IF NOT EXISTS idx_expenses_site_id ON plugin_financials.expenses(site_id); CREATE INDEX IF NOT EXISTS idx_expenses_date ON plugin_financials.expenses(expense_date); CREATE INDEX IF NOT EXISTS idx_revenue_site_id ON plugin_financials.revenue(site_id); CREATE INDEX IF NOT EXISTS idx_revenue_date ON plugin_financials.revenue(revenue_date); CREATE INDEX IF NOT EXISTS idx_reports_site_id ON plugin_financials.reports(site_id); CREATE INDEX IF NOT EXISTS idx_analytics_site_id ON plugin_financials.analytics(site_id); CREATE INDEX IF NOT EXISTS idx_analytics_date ON plugin_financials.analytics(metric_date); -- Bank Reconciliation Indexes CREATE INDEX IF NOT EXISTS idx_bank_accounts_site_id ON plugin_financials.bank_accounts(site_id); CREATE INDEX IF NOT EXISTS idx_bank_accounts_ledger_account ON plugin_financials.bank_accounts(ledger_account_id); CREATE INDEX IF NOT EXISTS idx_bank_statements_account ON plugin_financials.bank_statements(bank_account_id); CREATE INDEX IF NOT EXISTS idx_bank_statements_site_id ON plugin_financials.bank_statements(site_id); CREATE INDEX IF NOT EXISTS idx_bank_statement_transactions_statement ON plugin_financials.bank_statement_transactions(bank_statement_id); CREATE INDEX IF NOT EXISTS idx_bank_reconciliation_account ON plugin_financials.bank_reconciliation_records(bank_account_id); CREATE INDEX IF NOT EXISTS idx_bank_reconciliation_site_id ON plugin_financials.bank_reconciliation_records(site_id);