-- BOC Schema Extension: Quotes, Orders, Suppliers, Inventory
-- Created: 2026-07-12

-- ============================================
-- SALES: Quotes (Offert)
-- ============================================
CREATE TABLE IF NOT EXISTS boc_quotes (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    customer_id     UUID REFERENCES boc_customers(id) ON DELETE CASCADE,
    contact_id      UUID REFERENCES boc_contacts(id),
    quote_number    TEXT NOT NULL,
    title           TEXT NOT NULL,
    description     TEXT,
    status          TEXT NOT NULL DEFAULT 'draft', -- draft, sent, accepted, rejected, expired
    amount          DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_amount      DECIMAL(15,2) DEFAULT 0,
    currency        TEXT NOT NULL DEFAULT 'USD',
    valid_until     DATE,
    accepted_at     TIMESTAMPTZ,
    converted_to_order_id UUID,
    notes           TEXT,
    terms           TEXT,
    metadata        JSONB DEFAULT '{}',
    created_by      UUID REFERENCES boc_users(id),
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_quote_items (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    quote_id    UUID REFERENCES boc_quotes(id) ON DELETE CASCADE,
    product_id  UUID REFERENCES boc_products(id),
    description TEXT NOT NULL,
    quantity    DECIMAL(10,2) NOT NULL DEFAULT 1,
    unit_price  DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_rate    DECIMAL(5,2) DEFAULT 0,
    discount    DECIMAL(5,2) DEFAULT 0,
    total       DECIMAL(15,2) NOT NULL DEFAULT 0,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- SALES: Orders
-- ============================================
CREATE TABLE IF NOT EXISTS boc_orders (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    customer_id     UUID REFERENCES boc_customers(id) ON DELETE CASCADE,
    quote_id        UUID REFERENCES boc_quotes(id),
    order_number    TEXT NOT NULL,
    title           TEXT NOT NULL,
    status          TEXT NOT NULL DEFAULT 'draft', -- draft, confirmed, processing, shipped, delivered, cancelled
    amount          DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_amount      DECIMAL(15,2) DEFAULT 0,
    currency        TEXT NOT NULL DEFAULT 'USD',
    delivery_date   DATE,
    shipped_at      TIMESTAMPTZ,
    delivered_at    TIMESTAMPTZ,
    tracking_number TEXT,
    notes           TEXT,
    metadata        JSONB DEFAULT '{}',
    created_by      UUID REFERENCES boc_users(id),
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_order_items (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    order_id    UUID REFERENCES boc_orders(id) ON DELETE CASCADE,
    product_id  UUID REFERENCES boc_products(id),
    description TEXT NOT NULL,
    quantity    DECIMAL(10,2) NOT NULL DEFAULT 1,
    unit_price  DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_rate    DECIMAL(5,2) DEFAULT 0,
    discount    DECIMAL(5,2) DEFAULT 0,
    total       DECIMAL(15,2) NOT NULL DEFAULT 0,
    delivered_qty DECIMAL(10,2) DEFAULT 0,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- PURCHASE: Suppliers
-- ============================================
CREATE TABLE IF NOT EXISTS boc_suppliers (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    name        TEXT NOT NULL,
    email       TEXT,
    phone       TEXT,
    org_number  TEXT,
    address     JSONB,
    payment_terms TEXT DEFAULT '30 days',
    bank_account TEXT,
    bankgiro    TEXT,
    postgiro    TEXT,
    currency    TEXT DEFAULT 'USD',
    status      TEXT NOT NULL DEFAULT 'active',
    metadata    JSONB DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- PURCHASE: Purchase Orders
-- ============================================
CREATE TABLE IF NOT EXISTS boc_purchase_orders (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    supplier_id     UUID REFERENCES boc_suppliers(id) ON DELETE CASCADE,
    po_number       TEXT NOT NULL,
    status          TEXT NOT NULL DEFAULT 'draft', -- draft, sent, confirmed, received, invoiced, paid
    amount          DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_amount      DECIMAL(15,2) DEFAULT 0,
    currency        TEXT NOT NULL DEFAULT 'USD',
    expected_delivery DATE,
    received_at     TIMESTAMPTZ,
    notes           TEXT,
    metadata        JSONB DEFAULT '{}',
    created_by      UUID REFERENCES boc_users(id),
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_purchase_order_items (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    po_id       UUID REFERENCES boc_purchase_orders(id) ON DELETE CASCADE,
    product_id  UUID REFERENCES boc_products(id),
    description TEXT NOT NULL,
    quantity    DECIMAL(10,2) NOT NULL DEFAULT 1,
    unit_price  DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_rate    DECIMAL(5,2) DEFAULT 0,
    total       DECIMAL(15,2) NOT NULL DEFAULT 0,
    received_qty DECIMAL(10,2) DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- PURCHASE: Supplier Invoices (Leverantörsfakturor)
-- ============================================
CREATE TABLE IF NOT EXISTS boc_supplier_invoices (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    supplier_id     UUID REFERENCES boc_suppliers(id),
    po_id           UUID REFERENCES boc_purchase_orders(id),
    invoice_number  TEXT NOT NULL,
    amount          DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_amount      DECIMAL(15,2) DEFAULT 0,
    currency        TEXT NOT NULL DEFAULT 'USD',
    status          TEXT NOT NULL DEFAULT 'draft', -- draft, received, approved, paid, disputed
    due_date        DATE,
    paid_at         TIMESTAMPTZ,
    paid_amount     DECIMAL(15,2) DEFAULT 0,
    ocr_number      TEXT,
    notes           TEXT,
    metadata        JSONB DEFAULT '{}',
    created_by      UUID REFERENCES boc_users(id),
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- INVENTORY: Stock / Warehouse
-- ============================================
CREATE TABLE IF NOT EXISTS boc_warehouses (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    name        TEXT NOT NULL,
    location    TEXT,
    address     JSONB,
    is_default  BOOLEAN DEFAULT FALSE,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_inventory (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    product_id  UUID REFERENCES boc_products(id) ON DELETE CASCADE,
    warehouse_id UUID REFERENCES boc_warehouses(id),
    quantity    DECIMAL(10,2) NOT NULL DEFAULT 0,
    reserved_qty DECIMAL(10,2) DEFAULT 0,
    reorder_point DECIMAL(10,2) DEFAULT 0,
    reorder_qty DECIMAL(10,2) DEFAULT 0,
    unit_cost   DECIMAL(15,2) DEFAULT 0,
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE(tenant_id, product_id, warehouse_id)
);

CREATE TABLE IF NOT EXISTS boc_inventory_movements (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    product_id  UUID REFERENCES boc_products(id),
    warehouse_id UUID REFERENCES boc_warehouses(id),
    type        TEXT NOT NULL, -- in, out, adjustment, transfer
    quantity    DECIMAL(10,2) NOT NULL,
    reference_type TEXT, -- order, po, adjustment
    reference_id TEXT,
    notes       TEXT,
    created_by  UUID REFERENCES boc_users(id),
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- RECURRING: Subscriptions & Recurring Invoices
-- ============================================
CREATE TABLE IF NOT EXISTS boc_subscription_plans (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    name        TEXT NOT NULL,
    description TEXT,
    product_id  UUID REFERENCES boc_products(id),
    interval    TEXT NOT NULL DEFAULT 'monthly', -- weekly, monthly, quarterly, yearly
    interval_count INTEGER DEFAULT 1,
    price       DECIMAL(15,2) NOT NULL DEFAULT 0,
    currency    TEXT NOT NULL DEFAULT 'USD',
    trial_days  INTEGER DEFAULT 0,
    setup_fee   DECIMAL(15,2) DEFAULT 0,
    status      TEXT NOT NULL DEFAULT 'active',
    metadata    JSONB DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_subscriptions (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    customer_id UUID REFERENCES boc_customers(id) ON DELETE CASCADE,
    plan_id     UUID REFERENCES boc_subscription_plans(id),
    status      TEXT NOT NULL DEFAULT 'active', -- active, paused, cancelled, expired
    start_date  DATE NOT NULL,
    end_date    DATE,
    trial_end   DATE,
    current_period_start DATE,
    current_period_end DATE,
    price       DECIMAL(15,2) NOT NULL,
    currency    TEXT NOT NULL DEFAULT 'USD',
    metadata    JSONB DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_recurring_invoices (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    customer_id     UUID REFERENCES boc_customers(id),
    subscription_id UUID REFERENCES boc_subscriptions(id),
    plan_id         UUID REFERENCES boc_subscription_plans(id),
    invoice_number  TEXT,
    amount          DECIMAL(15,2) NOT NULL DEFAULT 0,
    currency        TEXT NOT NULL DEFAULT 'USD',
    status          TEXT NOT NULL DEFAULT 'pending', -- pending, generated, sent, paid, failed
    scheduled_date  DATE NOT NULL,
    generated_at    TIMESTAMPTZ,
    sent_at         TIMESTAMPTZ,
    error           TEXT,
    metadata        JSONB DEFAULT '{}',
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- EXPENSES: Receipts & OCR
-- ============================================
CREATE TABLE IF NOT EXISTS boc_receipts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    employee_id UUID REFERENCES boc_employees(id),
    expense_id  UUID REFERENCES boc_expenses(id),
    image_url   TEXT NOT NULL,
    ocr_text    TEXT,
    ocr_data    JSONB DEFAULT '{}', -- extracted: amount, date, vendor, category
    ocr_confidence DECIMAL(5,2) DEFAULT 0,
    status      TEXT NOT NULL DEFAULT 'pending', -- pending, processed, failed
    processed_at TIMESTAMPTZ,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- PAYROLL: Basic structure
-- ============================================
CREATE TABLE IF NOT EXISTS boc_payroll_runs (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    period_start DATE NOT NULL,
    period_end  DATE NOT NULL,
    pay_date    DATE NOT NULL,
    status      TEXT NOT NULL DEFAULT 'draft', -- draft, processing, approved, paid
    total_gross DECIMAL(15,2) DEFAULT 0,
    total_tax   DECIMAL(15,2) DEFAULT 0,
    total_net   DECIMAL(15,2) DEFAULT 0,
    total_employer_tax DECIMAL(15,2) DEFAULT 0,
    currency    TEXT DEFAULT 'USD',
    metadata    JSONB DEFAULT '{}',
    created_by  UUID REFERENCES boc_users(id),
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_payroll_lines (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    payroll_run_id UUID REFERENCES boc_payroll_runs(id) ON DELETE CASCADE,
    employee_id UUID REFERENCES boc_employees(id),
    gross_salary DECIMAL(15,2) NOT NULL DEFAULT 0,
    tax_deduction DECIMAL(15,2) DEFAULT 0,
    social_fees DECIMAL(15,2) DEFAULT 0,
    pension DECIMAL(15,2) DEFAULT 0,
    other_deductions DECIMAL(15,2) DEFAULT 0,
    net_salary DECIMAL(15,2) DEFAULT 0,
    hours_worked DECIMAL(5,2) DEFAULT 0,
    vacation_days_used DECIMAL(4,1) DEFAULT 0,
    sick_days DECIMAL(4,1) DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- BANK: Accounts & Transactions
-- ============================================
CREATE TABLE IF NOT EXISTS boc_bank_accounts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    name        TEXT NOT NULL,
    bank_name   TEXT NOT NULL,
    account_number TEXT NOT NULL,
    iban        TEXT,
    bic         TEXT,
    currency    TEXT DEFAULT 'USD',
    balance     DECIMAL(15,2) DEFAULT 0,
    is_default  BOOLEAN DEFAULT FALSE,
    status      TEXT NOT NULL DEFAULT 'active',
    last_sync   TIMESTAMPTZ,
    metadata    JSONB DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_bank_transactions (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    account_id  UUID REFERENCES boc_bank_accounts(id) ON DELETE CASCADE,
    transaction_date DATE NOT NULL,
    amount      DECIMAL(15,2) NOT NULL,
    currency    TEXT DEFAULT 'USD',
    description TEXT,
    counterparty TEXT,
    reference   TEXT,
    external_id TEXT,
    status      TEXT NOT NULL DEFAULT 'unmatched', -- unmatched, matched, reconciled
    matched_to_type TEXT, -- invoice, expense, payroll
    matched_to_id UUID,
    metadata    JSONB DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================
-- PROJECTS
-- ============================================
CREATE TABLE IF NOT EXISTS boc_projects (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    name        TEXT NOT NULL,
    description TEXT,
    customer_id UUID REFERENCES boc_customers(id),
    status      TEXT NOT NULL DEFAULT 'active', -- active, completed, on_hold, cancelled
    budget      DECIMAL(15,2) DEFAULT 0,
    spent       DECIMAL(15,2) DEFAULT 0,
    currency    TEXT DEFAULT 'USD',
    start_date  DATE,
    end_date    DATE,
    manager_id  UUID REFERENCES boc_employees(id),
    metadata    JSONB DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_project_times (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    project_id  UUID REFERENCES boc_projects(id) ON DELETE CASCADE,
    employee_id UUID REFERENCES boc_employees(id),
    date        DATE NOT NULL,
    hours       DECIMAL(4,2) NOT NULL DEFAULT 0,
    description TEXT,
    billable    BOOLEAN DEFAULT TRUE,
    hourly_rate DECIMAL(15,2) DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS boc_project_expenses (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
    project_id  UUID REFERENCES boc_projects(id) ON DELETE CASCADE,
    expense_id  UUID REFERENCES boc_expenses(id),
    amount      DECIMAL(15,2) NOT NULL,
    description TEXT,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Indexes
CREATE INDEX IF NOT EXISTS idx_quotes_tenant ON boc_quotes(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_quotes_customer ON boc_quotes(customer_id);
CREATE INDEX IF NOT EXISTS idx_orders_tenant ON boc_orders(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_orders_customer ON boc_orders(customer_id);
CREATE INDEX IF NOT EXISTS idx_suppliers_tenant ON boc_suppliers(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_po_tenant ON boc_purchase_orders(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_supplier_invoices_tenant ON boc_supplier_invoices(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_inventory_product ON boc_inventory(product_id, warehouse_id);
CREATE INDEX IF NOT EXISTS idx_inventory_movements ON boc_inventory_movements(product_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_subscriptions_customer ON boc_subscriptions(customer_id, status);
CREATE INDEX IF NOT EXISTS idx_recurring_invoices ON boc_recurring_invoices(scheduled_date, status);
CREATE INDEX IF NOT EXISTS idx_receipts_status ON boc_receipts(status);
CREATE INDEX IF NOT EXISTS idx_payroll_runs ON boc_payroll_runs(period_start, status);
CREATE INDEX IF NOT EXISTS idx_bank_transactions ON boc_bank_transactions(account_id, transaction_date DESC);
CREATE INDEX IF NOT EXISTS idx_projects_tenant ON boc_projects(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_project_times ON boc_project_times(project_id, date);

-- Triggers for updated_at
DO $$
BEGIN
    CREATE TRIGGER update_boc_quotes_updated_at BEFORE UPDATE ON boc_quotes FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_orders_updated_at BEFORE UPDATE ON boc_orders FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_suppliers_updated_at BEFORE UPDATE ON boc_suppliers FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_purchase_orders_updated_at BEFORE UPDATE ON boc_purchase_orders FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_supplier_invoices_updated_at BEFORE UPDATE ON boc_supplier_invoices FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_inventory_updated_at BEFORE UPDATE ON boc_inventory FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_subscription_plans_updated_at BEFORE UPDATE ON boc_subscription_plans FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_subscriptions_updated_at BEFORE UPDATE ON boc_subscriptions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_payroll_runs_updated_at BEFORE UPDATE ON boc_payroll_runs FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_bank_accounts_updated_at BEFORE UPDATE ON boc_bank_accounts FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
    CREATE TRIGGER update_boc_projects_updated_at BEFORE UPDATE ON boc_projects FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
EXCEPTION WHEN duplicate_object THEN
    -- Triggers already exist, ignore
END;
$$;
