initial commit: rocket.new export of broadcastbeat
This commit is contained in:
343
supabase/migrations/20260322260000_unified_billing_system.sql
Normal file
343
supabase/migrations/20260322260000_unified_billing_system.sql
Normal file
@@ -0,0 +1,343 @@
|
||||
-- ============================================================
|
||||
-- Migration: Unified Billing System
|
||||
-- Timestamp: 20260322260000
|
||||
-- Covers: billing_companies, billing_clients, billing_invoices,
|
||||
-- billing_line_items, billing_payments, billing_sync_log,
|
||||
-- ad_campaigns table, billing settings
|
||||
-- ============================================================
|
||||
|
||||
-- ─── 1. billing_companies ────────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.billing_companies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
legal_name TEXT NOT NULL,
|
||||
llc_entity TEXT,
|
||||
address TEXT,
|
||||
email TEXT,
|
||||
invoice_prefix TEXT NOT NULL DEFAULT 'INV-',
|
||||
stripe_account_id TEXT,
|
||||
mooninvoice_company_id TEXT,
|
||||
mooninvoice_api_key TEXT,
|
||||
stripe_secret_key TEXT,
|
||||
stripe_publishable_key TEXT,
|
||||
default_payment_terms TEXT NOT NULL DEFAULT 'Net 30',
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_companies_active ON public.billing_companies(is_active);
|
||||
|
||||
ALTER TABLE public.billing_companies ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_billing_companies" ON public.billing_companies;
|
||||
CREATE POLICY "admin_manage_billing_companies"
|
||||
ON public.billing_companies FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 2. billing_clients ──────────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.billing_clients (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES public.billing_companies(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
organization TEXT,
|
||||
email TEXT,
|
||||
phone TEXT,
|
||||
billing_address TEXT,
|
||||
notes TEXT,
|
||||
mooninvoice_contact_id TEXT,
|
||||
stripe_customer_id TEXT,
|
||||
total_billed NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
total_paid NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
invoice_consolidation BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_clients_company_id ON public.billing_clients(company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_clients_mooninvoice_id ON public.billing_clients(mooninvoice_contact_id);
|
||||
|
||||
ALTER TABLE public.billing_clients ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_billing_clients" ON public.billing_clients;
|
||||
CREATE POLICY "admin_manage_billing_clients"
|
||||
ON public.billing_clients FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 3. billing_invoices ─────────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.billing_invoices (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID REFERENCES public.billing_companies(id) ON DELETE CASCADE,
|
||||
client_id UUID REFERENCES public.billing_clients(id) ON DELETE SET NULL,
|
||||
invoice_number TEXT NOT NULL,
|
||||
issue_date DATE,
|
||||
due_date DATE,
|
||||
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','sent','viewed','paid','overdue','cancelled')),
|
||||
subtotal NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
tax NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
total NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
amount_paid NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
currency TEXT NOT NULL DEFAULT 'USD',
|
||||
notes TEXT,
|
||||
internal_memo TEXT,
|
||||
stripe_payment_link TEXT,
|
||||
mooninvoice_invoice_id TEXT,
|
||||
pdf_url TEXT,
|
||||
paid_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_invoices_company_id ON public.billing_invoices(company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_invoices_client_id ON public.billing_invoices(client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_invoices_status ON public.billing_invoices(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_invoices_mooninvoice_id ON public.billing_invoices(mooninvoice_invoice_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_invoices_due_date ON public.billing_invoices(due_date);
|
||||
|
||||
ALTER TABLE public.billing_invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_billing_invoices" ON public.billing_invoices;
|
||||
CREATE POLICY "admin_manage_billing_invoices"
|
||||
ON public.billing_invoices FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 4. billing_line_items ───────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.billing_line_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
invoice_id UUID REFERENCES public.billing_invoices(id) ON DELETE CASCADE,
|
||||
description TEXT NOT NULL,
|
||||
quantity NUMERIC(10,2) NOT NULL DEFAULT 1,
|
||||
rate NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
amount NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
campaign_id UUID,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_line_items_invoice_id ON public.billing_line_items(invoice_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_line_items_campaign_id ON public.billing_line_items(campaign_id);
|
||||
|
||||
ALTER TABLE public.billing_line_items ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_billing_line_items" ON public.billing_line_items;
|
||||
CREATE POLICY "admin_manage_billing_line_items"
|
||||
ON public.billing_line_items FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 5. billing_payments ─────────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.billing_payments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
invoice_id UUID REFERENCES public.billing_invoices(id) ON DELETE SET NULL,
|
||||
client_id UUID REFERENCES public.billing_clients(id) ON DELETE SET NULL,
|
||||
amount NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||||
payment_date DATE,
|
||||
payment_method TEXT,
|
||||
stripe_charge_id TEXT,
|
||||
stripe_payment_intent_id TEXT,
|
||||
notes TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'succeeded' CHECK (status IN ('succeeded','failed','pending','refunded','disputed')),
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_payments_invoice_id ON public.billing_payments(invoice_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_payments_client_id ON public.billing_payments(client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_payments_stripe_charge ON public.billing_payments(stripe_charge_id);
|
||||
|
||||
ALTER TABLE public.billing_payments ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_billing_payments" ON public.billing_payments;
|
||||
CREATE POLICY "admin_manage_billing_payments"
|
||||
ON public.billing_payments FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 6. billing_sync_log ─────────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.billing_sync_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
sync_type TEXT NOT NULL,
|
||||
company_id UUID REFERENCES public.billing_companies(id) ON DELETE SET NULL,
|
||||
records_synced INTEGER NOT NULL DEFAULT 0,
|
||||
errors JSONB,
|
||||
started_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMPTZ,
|
||||
status TEXT NOT NULL DEFAULT 'running' CHECK (status IN ('running','completed','failed'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_sync_log_company_id ON public.billing_sync_log(company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_billing_sync_log_started_at ON public.billing_sync_log(started_at);
|
||||
|
||||
ALTER TABLE public.billing_sync_log ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_billing_sync_log" ON public.billing_sync_log;
|
||||
CREATE POLICY "admin_manage_billing_sync_log"
|
||||
ON public.billing_sync_log FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 7. billing_settings ─────────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.billing_settings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
billing_mode TEXT NOT NULL DEFAULT 'mooninvoice' CHECK (billing_mode IN ('mooninvoice','native')),
|
||||
sync_frequency_minutes INTEGER NOT NULL DEFAULT 15,
|
||||
last_sync_rmp TIMESTAMPTZ,
|
||||
last_sync_jtr TIMESTAMPTZ,
|
||||
default_payment_terms TEXT NOT NULL DEFAULT 'Net 30',
|
||||
default_currency TEXT NOT NULL DEFAULT 'USD',
|
||||
late_payment_reminder_enabled BOOLEAN NOT NULL DEFAULT false,
|
||||
late_payment_reminder_days INTEGER NOT NULL DEFAULT 7,
|
||||
auto_send_reminders BOOLEAN NOT NULL DEFAULT false,
|
||||
stripe_test_mode BOOLEAN NOT NULL DEFAULT true,
|
||||
migration_import_status JSONB,
|
||||
migration_last_run TIMESTAMPTZ,
|
||||
migration_verified BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE public.billing_settings ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_billing_settings" ON public.billing_settings;
|
||||
CREATE POLICY "admin_manage_billing_settings"
|
||||
ON public.billing_settings FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 8. ad_campaigns table ───────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS public.ad_campaigns (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
client_name TEXT,
|
||||
campaign_type TEXT NOT NULL DEFAULT 'banner' CHECK (campaign_type IN ('banner','email','sponsored','newsletter')),
|
||||
site_id INTEGER REFERENCES public.sites(id) ON DELETE SET NULL,
|
||||
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','pending_approval','approved','active','paused','completed','cancelled')),
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
budget NUMERIC(12,2),
|
||||
rate NUMERIC(12,2),
|
||||
notes TEXT,
|
||||
billing_invoice_id UUID REFERENCES public.billing_invoices(id) ON DELETE SET NULL,
|
||||
billing_company_id UUID REFERENCES public.billing_companies(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ad_campaigns_status ON public.ad_campaigns(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_ad_campaigns_billing_invoice ON public.ad_campaigns(billing_invoice_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ad_campaigns_billing_company ON public.ad_campaigns(billing_company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ad_campaigns_site_id ON public.ad_campaigns(site_id);
|
||||
|
||||
ALTER TABLE public.ad_campaigns ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "admin_manage_ad_campaigns" ON public.ad_campaigns;
|
||||
CREATE POLICY "admin_manage_ad_campaigns"
|
||||
ON public.ad_campaigns FOR ALL TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles up
|
||||
WHERE up.id = auth.uid() AND up.role IN ('administrator', 'admin')
|
||||
)
|
||||
);
|
||||
|
||||
-- ─── 9. Seed default companies and settings ──────────────────────────────────
|
||||
DO $$
|
||||
DECLARE
|
||||
rmp_id UUID := gen_random_uuid();
|
||||
jtr_id UUID := gen_random_uuid();
|
||||
BEGIN
|
||||
INSERT INTO public.billing_companies (id, name, legal_name, llc_entity, address, email, invoice_prefix, default_payment_terms, is_active)
|
||||
VALUES
|
||||
(rmp_id, 'Relevant Media Properties', 'Relevant Media Properties, LLC', 'RMP',
|
||||
'1309 Coffeen Ave, Suite 1200, Sheridan, WY 82801',
|
||||
'ryan.salazar@relevantmediaproperties.com', 'RMP-', 'Net 30', true),
|
||||
(jtr_id, 'Just Two Roommates', 'Just Two Roommates, LLC', 'JTR',
|
||||
'1309 Coffeen Ave, Suite 1200, Sheridan, WY 82801',
|
||||
'ryan.salazar@relevantmediaproperties.com', 'JTR-', 'Net 30', true)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO public.billing_settings (billing_mode, sync_frequency_minutes, default_payment_terms, default_currency)
|
||||
VALUES ('mooninvoice', 15, 'Net 30', 'USD')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE NOTICE 'Seed data insertion failed: %', SQLERRM;
|
||||
END $$;
|
||||
Reference in New Issue
Block a user