| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; |
|
|
| |
| DROP MATERIALIZED VIEW IF EXISTS mv_monthly_revenue; |
| DROP TABLE IF EXISTS billing_events CASCADE; |
| DROP TABLE IF EXISTS invoices CASCADE; |
| DROP TABLE IF EXISTS subscriptions CASCADE; |
| DROP TABLE IF EXISTS tenants CASCADE; |
|
|
| |
| |
| |
| CREATE TABLE tenants ( |
| tenant_id VARCHAR(20) PRIMARY KEY, |
| company_name VARCHAR(255) NOT NULL, |
| industry VARCHAR(100), |
| plan VARCHAR(50) NOT NULL CHECK (plan IN ('starter','growth','business','enterprise')), |
| country VARCHAR(100), |
| signup_date DATE NOT NULL, |
| is_active BOOLEAN DEFAULT TRUE, |
| created_at TIMESTAMPTZ DEFAULT NOW(), |
| updated_at TIMESTAMPTZ DEFAULT NOW() |
| ); |
|
|
| CREATE INDEX idx_tenants_plan ON tenants(plan); |
| CREATE INDEX idx_tenants_country ON tenants(country); |
| CREATE INDEX idx_tenants_signup ON tenants(signup_date); |
|
|
| |
| |
| |
| CREATE TABLE subscriptions ( |
| subscription_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), |
| tenant_id VARCHAR(20) NOT NULL REFERENCES tenants(tenant_id), |
| plan VARCHAR(50) NOT NULL CHECK (plan IN ('starter','growth','business','enterprise')), |
| status VARCHAR(30) NOT NULL CHECK (status IN ('active','cancelled','paused','trial')), |
| monthly_amount INTEGER NOT NULL, |
| seats INTEGER DEFAULT 1, |
| start_date DATE NOT NULL, |
| end_date DATE, |
| trial_end_date DATE, |
| created_at TIMESTAMPTZ DEFAULT NOW() |
| ); |
|
|
| CREATE INDEX idx_subs_tenant ON subscriptions(tenant_id); |
| CREATE INDEX idx_subs_status ON subscriptions(status); |
| CREATE INDEX idx_subs_plan ON subscriptions(plan); |
| CREATE INDEX idx_subs_start_date ON subscriptions(start_date); |
|
|
| |
| |
| |
| CREATE TABLE invoices ( |
| invoice_id VARCHAR(20) PRIMARY KEY, |
| tenant_id VARCHAR(20) NOT NULL REFERENCES tenants(tenant_id), |
| subscription_id UUID REFERENCES subscriptions(subscription_id), |
| amount INTEGER NOT NULL, |
| currency CHAR(3) DEFAULT 'INR', |
| status VARCHAR(20) NOT NULL CHECK (status IN ('paid','failed','pending','refunded')), |
| billing_date DATE NOT NULL, |
| due_date DATE, |
| paid_date DATE, |
| created_at TIMESTAMPTZ DEFAULT NOW() |
| ); |
|
|
| CREATE INDEX idx_inv_tenant ON invoices(tenant_id); |
| CREATE INDEX idx_inv_billing_date ON invoices(billing_date); |
| CREATE INDEX idx_inv_status ON invoices(status); |
| CREATE INDEX idx_inv_mrr_query ON invoices(tenant_id, billing_date, status); |
|
|
| |
| |
| |
| CREATE TABLE billing_events ( |
| event_id VARCHAR(20) PRIMARY KEY, |
| tenant_id VARCHAR(20) NOT NULL REFERENCES tenants(tenant_id), |
| event_type VARCHAR(50) NOT NULL, |
| plan VARCHAR(50), |
| amount INTEGER DEFAULT 0, |
| currency CHAR(3) DEFAULT 'INR', |
| event_date TIMESTAMPTZ NOT NULL, |
| status VARCHAR(20) DEFAULT 'success', |
| invoice_id VARCHAR(20) REFERENCES invoices(invoice_id), |
| seats INTEGER, |
| trial BOOLEAN DEFAULT FALSE, |
| created_at TIMESTAMPTZ DEFAULT NOW() |
| ); |
|
|
| |
| |
|
|
| CREATE INDEX idx_events_tenant ON billing_events(tenant_id); |
| CREATE INDEX idx_events_type ON billing_events(event_type); |
| CREATE INDEX idx_events_date ON billing_events(event_date); |
| CREATE INDEX idx_events_tenant_date ON billing_events(tenant_id, event_date); |
|
|
| |
| |
| |
| CREATE MATERIALIZED VIEW mv_monthly_revenue AS |
| SELECT |
| t.tenant_id, |
| t.company_name, |
| t.industry, |
| t.plan, |
| t.country, |
| DATE_TRUNC('month', i.billing_date) AS billing_month, |
| COUNT(i.invoice_id) AS invoice_count, |
| SUM(i.amount) AS total_mrr, |
| SUM(i.amount) * 12 AS arr, |
| AVG(i.amount) AS avg_invoice_amount |
| FROM invoices i |
| JOIN tenants t ON i.tenant_id = t.tenant_id |
| WHERE i.status = 'paid' |
| GROUP BY |
| t.tenant_id, |
| t.company_name, |
| t.industry, |
| t.plan, |
| t.country, |
| DATE_TRUNC('month', i.billing_date); |
|
|
| CREATE UNIQUE INDEX idx_mv_revenue_unique |
| ON mv_monthly_revenue(tenant_id, billing_month); |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| ALTER TABLE tenants ENABLE ROW LEVEL SECURITY; |
| ALTER TABLE billing_events ENABLE ROW LEVEL SECURITY; |
| ALTER TABLE invoices ENABLE ROW LEVEL SECURITY; |
| ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY; |
|
|
| CREATE POLICY tenant_isolation_tenants ON tenants |
| USING (tenant_id = current_setting('app.tenant_id', TRUE)); |
|
|
| CREATE POLICY tenant_isolation_events ON billing_events |
| USING (tenant_id = current_setting('app.tenant_id', TRUE)); |
|
|
| CREATE POLICY tenant_isolation_invoices ON invoices |
| USING (tenant_id = current_setting('app.tenant_id', TRUE)); |
|
|
| CREATE POLICY tenant_isolation_subs ON subscriptions |
| USING (tenant_id = current_setting('app.tenant_id', TRUE)); |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|