fix(security): JWT require env, remove *** token, WS auth disabled
fix(automation): implement all 6 actions + real cron parser fix(db): pq.Array for TEXT[], add sqlmock tests fix(schema): single source migrations docs: v2 architecture + frontend refactor proposals
This commit is contained in:
@@ -0,0 +1,525 @@
|
||||
-- BOC Initial Schema
|
||||
-- Business Operations Center — Full schema for all modules
|
||||
-- Created: 2026-07-12
|
||||
|
||||
-- Enable UUID extension
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- Core: Tenants (multi-tenant support)
|
||||
CREATE TABLE IF NOT EXISTS boc_tenants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT UNIQUE NOT NULL,
|
||||
domain TEXT,
|
||||
settings JSONB DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Core: Users
|
||||
CREATE TABLE IF NOT EXISTS boc_users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
email TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'user',
|
||||
avatar_url TEXT,
|
||||
settings JSONB DEFAULT '{}',
|
||||
last_login TIMESTAMPTZ,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, email)
|
||||
);
|
||||
|
||||
-- Core: Audit log (immutable)
|
||||
CREATE TABLE IF NOT EXISTS boc_audit_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES boc_users(id),
|
||||
action TEXT NOT NULL,
|
||||
entity_type TEXT NOT NULL,
|
||||
entity_id TEXT NOT NULL,
|
||||
old_value JSONB,
|
||||
new_value JSONB,
|
||||
ip_address INET,
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- CRM: Customers
|
||||
CREATE TABLE IF NOT EXISTS boc_customers (
|
||||
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,
|
||||
company TEXT,
|
||||
org_number TEXT,
|
||||
address JSONB,
|
||||
status TEXT NOT NULL DEFAULT 'lead',
|
||||
source TEXT,
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
assigned_to UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- CRM: Customer interactions
|
||||
CREATE TABLE IF NOT EXISTS boc_customer_interactions (
|
||||
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,
|
||||
type TEXT NOT NULL, -- call, email, meeting, note, task
|
||||
direction TEXT, -- inbound, outbound
|
||||
subject TEXT,
|
||||
content TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- CRM: Contacts (people within customer orgs)
|
||||
CREATE TABLE IF NOT EXISTS boc_contacts (
|
||||
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,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT,
|
||||
phone TEXT,
|
||||
title TEXT,
|
||||
is_primary BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Sales: Deals
|
||||
CREATE TABLE IF NOT EXISTS boc_deals (
|
||||
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),
|
||||
contact_id UUID REFERENCES boc_contacts(id),
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
value DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
currency TEXT NOT NULL DEFAULT 'USD',
|
||||
status TEXT NOT NULL DEFAULT 'open',
|
||||
stage TEXT NOT NULL DEFAULT 'prospect',
|
||||
probability INTEGER NOT NULL DEFAULT 0,
|
||||
expected_close DATE,
|
||||
actual_close TIMESTAMPTZ,
|
||||
won_reason TEXT,
|
||||
lost_reason TEXT,
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
assigned_to UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Sales: Deal timeline / activities
|
||||
CREATE TABLE IF NOT EXISTS boc_deal_activities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
deal_id UUID REFERENCES boc_deals(id) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL, -- call, email, meeting, proposal, note, stage_change
|
||||
description TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Sales: Products/Services
|
||||
CREATE TABLE IF NOT EXISTS boc_products (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
sku TEXT,
|
||||
price DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
currency TEXT NOT NULL DEFAULT 'USD',
|
||||
unit TEXT DEFAULT 'piece',
|
||||
is_recurring BOOLEAN DEFAULT FALSE,
|
||||
billing_period TEXT, -- monthly, quarterly, yearly
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Sales: Deal line items
|
||||
CREATE TABLE IF NOT EXISTS boc_deal_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
deal_id UUID REFERENCES boc_deals(id) ON DELETE CASCADE,
|
||||
product_id UUID REFERENCES boc_products(id),
|
||||
quantity DECIMAL(10,2) NOT NULL DEFAULT 1,
|
||||
unit_price DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
discount DECIMAL(5,2) DEFAULT 0,
|
||||
total DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Finance: Invoices
|
||||
CREATE TABLE IF NOT EXISTS boc_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),
|
||||
deal_id UUID REFERENCES boc_deals(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',
|
||||
due_date DATE,
|
||||
paid_at TIMESTAMPTZ,
|
||||
paid_amount DECIMAL(15,2) DEFAULT 0,
|
||||
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()
|
||||
);
|
||||
|
||||
-- Finance: Invoice items
|
||||
CREATE TABLE IF NOT EXISTS boc_invoice_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
invoice_id UUID REFERENCES boc_invoices(id) ON DELETE CASCADE,
|
||||
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,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Finance: Payments
|
||||
CREATE TABLE IF NOT EXISTS boc_payments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
invoice_id UUID REFERENCES boc_invoices(id),
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'USD',
|
||||
method TEXT, -- bank_transfer, card, cash, stripe, etc
|
||||
reference TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'completed',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Finance: Expenses
|
||||
CREATE TABLE IF NOT EXISTS boc_expenses (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
category TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'USD',
|
||||
vendor TEXT,
|
||||
receipt_url TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
approved_by UUID REFERENCES boc_users(id),
|
||||
approved_at TIMESTAMPTZ,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Finance: Budgets
|
||||
CREATE TABLE IF NOT EXISTS boc_budgets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
fiscal_year INTEGER NOT NULL,
|
||||
category TEXT,
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'USD',
|
||||
spent DECIMAL(15,2) DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- HR: Employees
|
||||
CREATE TABLE IF NOT EXISTS boc_employees (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES boc_users(id),
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
phone TEXT,
|
||||
department TEXT,
|
||||
position TEXT,
|
||||
employment_type TEXT DEFAULT 'full_time',
|
||||
salary DECIMAL(15,2),
|
||||
currency TEXT DEFAULT 'USD',
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
manager_id UUID REFERENCES boc_employees(id),
|
||||
address JSONB,
|
||||
bank_info JSONB,
|
||||
documents JSONB DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- HR: Time off / Leave
|
||||
CREATE TABLE IF NOT EXISTS boc_leaves (
|
||||
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) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL, -- vacation, sick, parental, unpaid
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
days DECIMAL(4,1) NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
approved_by UUID REFERENCES boc_users(id),
|
||||
approved_at TIMESTAMPTZ,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- HR: Timesheets
|
||||
CREATE TABLE IF NOT EXISTS boc_timesheets (
|
||||
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) ON DELETE CASCADE,
|
||||
date DATE NOT NULL,
|
||||
hours DECIMAL(4,2) NOT NULL DEFAULT 0,
|
||||
project TEXT,
|
||||
task TEXT,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
approved_by UUID REFERENCES boc_users(id),
|
||||
approved_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(tenant_id, employee_id, date)
|
||||
);
|
||||
|
||||
-- Legal: Contracts
|
||||
CREATE TABLE IF NOT EXISTS boc_contracts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL,
|
||||
counterparty TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- service, employment, nda, partnership, etc
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
value DECIMAL(15,2),
|
||||
currency TEXT DEFAULT 'USD',
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
renewal_date DATE,
|
||||
document_url 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()
|
||||
);
|
||||
|
||||
-- Legal: Contract reminders
|
||||
CREATE TABLE IF NOT EXISTS boc_contract_reminders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
contract_id UUID REFERENCES boc_contracts(id) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL, -- renewal, expiration, payment, review
|
||||
due_date DATE NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
sent_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Marketing: Campaigns
|
||||
CREATE TABLE IF NOT EXISTS boc_campaigns (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
type TEXT NOT NULL, -- email, social, content, event, ad
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
budget DECIMAL(15,2),
|
||||
spent DECIMAL(15,2) DEFAULT 0,
|
||||
currency TEXT DEFAULT 'USD',
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
metrics JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Marketing: Content items
|
||||
CREATE TABLE IF NOT EXISTS boc_content (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
campaign_id UUID REFERENCES boc_campaigns(id),
|
||||
title TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- blog, social, email, video, whitepaper
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
publish_at TIMESTAMPTZ,
|
||||
published_at TIMESTAMPTZ,
|
||||
url TEXT,
|
||||
metrics JSONB DEFAULT '{}',
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Support: Tickets
|
||||
CREATE TABLE IF NOT EXISTS boc_tickets (
|
||||
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),
|
||||
contact_id UUID REFERENCES boc_contacts(id),
|
||||
subject TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'open',
|
||||
priority TEXT NOT NULL DEFAULT 'medium',
|
||||
category TEXT,
|
||||
source TEXT, -- email, chat, phone, web
|
||||
assigned_to UUID REFERENCES boc_users(id),
|
||||
resolved_at TIMESTAMPTZ,
|
||||
resolution TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Support: Ticket comments
|
||||
CREATE TABLE IF NOT EXISTS boc_ticket_comments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
ticket_id UUID REFERENCES boc_tickets(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
is_internal BOOLEAN DEFAULT FALSE,
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Automation: Workflows
|
||||
CREATE TABLE IF NOT EXISTS boc_workflows (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
trigger_type TEXT NOT NULL, -- schedule, event, webhook, manual
|
||||
trigger_config JSONB DEFAULT '{}',
|
||||
actions JSONB NOT NULL DEFAULT '[]',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
last_run_at TIMESTAMPTZ,
|
||||
next_run_at TIMESTAMPTZ,
|
||||
run_count INTEGER DEFAULT 0,
|
||||
fail_count INTEGER DEFAULT 0,
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Automation: Workflow runs
|
||||
CREATE TABLE IF NOT EXISTS boc_workflow_runs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
workflow_id UUID REFERENCES boc_workflows(id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL DEFAULT 'running',
|
||||
input JSONB DEFAULT '{}',
|
||||
output JSONB DEFAULT '{}',
|
||||
error TEXT,
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Automation: Scheduled jobs
|
||||
CREATE TABLE IF NOT EXISTS boc_scheduled_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
cron_expr TEXT NOT NULL,
|
||||
timezone TEXT DEFAULT 'UTC',
|
||||
job_type TEXT NOT NULL, -- report, reminder, sync, cleanup, backup
|
||||
job_config JSONB DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
last_run_at TIMESTAMPTZ,
|
||||
next_run_at TIMESTAMPTZ,
|
||||
run_count INTEGER DEFAULT 0,
|
||||
fail_count INTEGER DEFAULT 0,
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Automation: Scheduled job runs
|
||||
CREATE TABLE IF NOT EXISTS boc_scheduled_job_runs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id) ON DELETE CASCADE,
|
||||
job_id UUID REFERENCES boc_scheduled_jobs(id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL DEFAULT 'running',
|
||||
output JSONB DEFAULT '{}',
|
||||
error TEXT,
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_tenant ON boc_audit_log(tenant_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_customers_tenant ON boc_customers(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_customers_assigned ON boc_customers(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_interactions_customer ON boc_customer_interactions(customer_id, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_deals_tenant ON boc_deals(tenant_id, status, stage);
|
||||
CREATE INDEX IF NOT EXISTS idx_deals_customer ON boc_deals(customer_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_deals_assigned ON boc_deals(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_deals_expected_close ON boc_deals(expected_close);
|
||||
CREATE INDEX IF NOT EXISTS idx_invoices_tenant ON boc_invoices(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_invoices_due ON boc_invoices(due_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_expenses_tenant ON boc_expenses(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_tenant ON boc_employees(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_leaves_employee ON boc_leaves(employee_id, start_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_timesheets_employee ON boc_timesheets(employee_id, date);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_tenant ON boc_contracts(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_renewal ON boc_contracts(renewal_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_tenant ON boc_tickets(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON boc_tickets(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_workflows_tenant ON boc_workflows(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_scheduled_jobs_tenant ON boc_scheduled_jobs(tenant_id, status);
|
||||
|
||||
-- Functions
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Triggers for updated_at
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE TRIGGER update_boc_tenants_updated_at BEFORE UPDATE ON boc_tenants FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_users_updated_at BEFORE UPDATE ON boc_users FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_customers_updated_at BEFORE UPDATE ON boc_customers FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_deals_updated_at BEFORE UPDATE ON boc_deals FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_invoices_updated_at BEFORE UPDATE ON boc_invoices FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_expenses_updated_at BEFORE UPDATE ON boc_expenses FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_budgets_updated_at BEFORE UPDATE ON boc_budgets FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_employees_updated_at BEFORE UPDATE ON boc_employees FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_leaves_updated_at BEFORE UPDATE ON boc_leaves FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_timesheets_updated_at BEFORE UPDATE ON boc_timesheets FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_contracts_updated_at BEFORE UPDATE ON boc_contracts FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_campaigns_updated_at BEFORE UPDATE ON boc_campaigns FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_content_updated_at BEFORE UPDATE ON boc_content FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_tickets_updated_at BEFORE UPDATE ON boc_tickets FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_workflows_updated_at BEFORE UPDATE ON boc_workflows FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_boc_scheduled_jobs_updated_at BEFORE UPDATE ON boc_scheduled_jobs FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
EXCEPTION WHEN duplicate_object THEN
|
||||
-- Triggers already exist, ignore
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,437 @@
|
||||
-- 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;
|
||||
$$;
|
||||
Reference in New Issue
Block a user