-- HOA LedgerIQ - Database Initialization -- Creates shared schema and base tables for multi-tenant architecture CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Shared schema for cross-tenant data CREATE SCHEMA IF NOT EXISTS shared; -- Organizations (HOAs) CREATE TABLE shared.organizations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(255) NOT NULL, schema_name VARCHAR(63) NOT NULL UNIQUE, subdomain VARCHAR(63) UNIQUE, status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'trial', 'archived')), settings JSONB DEFAULT '{}', contract_number VARCHAR(100), plan_level VARCHAR(50) DEFAULT 'standard' CHECK (plan_level IN ('standard', 'premium', 'enterprise')), address_line1 VARCHAR(255), address_line2 VARCHAR(255), city VARCHAR(100), state VARCHAR(2), zip_code VARCHAR(10), phone VARCHAR(20), email VARCHAR(255), tax_id VARCHAR(20), fiscal_year_start_month INTEGER DEFAULT 1 CHECK (fiscal_year_start_month BETWEEN 1 AND 12), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Users (global, cross-tenant) CREATE TABLE shared.users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255), first_name VARCHAR(100), last_name VARCHAR(100), phone VARCHAR(20), is_email_verified BOOLEAN DEFAULT FALSE, mfa_enabled BOOLEAN DEFAULT FALSE, mfa_secret VARCHAR(255), oauth_provider VARCHAR(50), oauth_provider_id VARCHAR(255), last_login_at TIMESTAMPTZ, is_superadmin BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- User-Organization memberships with roles CREATE TABLE shared.user_organizations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES shared.users(id) ON DELETE CASCADE, organization_id UUID NOT NULL REFERENCES shared.organizations(id) ON DELETE CASCADE, role VARCHAR(50) NOT NULL CHECK (role IN ('president', 'treasurer', 'secretary', 'member_at_large', 'manager', 'homeowner', 'admin', 'viewer')), is_active BOOLEAN DEFAULT TRUE, joined_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(user_id, organization_id) ); -- Invitations CREATE TABLE shared.invitations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), organization_id UUID NOT NULL REFERENCES shared.organizations(id) ON DELETE CASCADE, email VARCHAR(255) NOT NULL, role VARCHAR(50) NOT NULL, invited_by UUID NOT NULL REFERENCES shared.users(id), token VARCHAR(255) NOT NULL UNIQUE, expires_at TIMESTAMPTZ NOT NULL, accepted_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW() ); -- Indexes CREATE INDEX idx_user_orgs_user ON shared.user_organizations(user_id); CREATE INDEX idx_user_orgs_org ON shared.user_organizations(organization_id); CREATE INDEX idx_users_email ON shared.users(email); CREATE INDEX idx_orgs_schema ON shared.organizations(schema_name); CREATE INDEX idx_invitations_token ON shared.invitations(token); CREATE INDEX idx_invitations_email ON shared.invitations(email);