CORP Client Hub schema for invoicing and contracts
cgen-a6404a00b3b041d2b9c61fa935d68ef4
This commit is contained in:
parent
3a67836c25
commit
f372d248e7
1 changed files with 259 additions and 0 deletions
259
supabase/migrations/20250301_add_corp_hub_schema.sql
Normal file
259
supabase/migrations/20250301_add_corp_hub_schema.sql
Normal file
|
|
@ -0,0 +1,259 @@
|
||||||
|
-- CORP: Enterprise Client Portal Schema
|
||||||
|
-- For invoicing, contracts, team management, and reporting
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INVOICES & BILLING
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
create table if not exists public.corp_invoices (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
client_company_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
invoice_number text not null unique,
|
||||||
|
project_id uuid,
|
||||||
|
description text,
|
||||||
|
issue_date date not null default now(),
|
||||||
|
due_date date not null,
|
||||||
|
amount_due numeric(12, 2) not null,
|
||||||
|
amount_paid numeric(12, 2) not null default 0,
|
||||||
|
status text not null default 'draft' check (status in ('draft', 'sent', 'viewed', 'paid', 'overdue', 'cancelled')),
|
||||||
|
currency text not null default 'USD',
|
||||||
|
notes text,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_invoices_client_idx on public.corp_invoices (client_company_id);
|
||||||
|
create index if not exists corp_invoices_status_idx on public.corp_invoices (status);
|
||||||
|
create index if not exists corp_invoices_due_date_idx on public.corp_invoices (due_date);
|
||||||
|
create index if not exists corp_invoices_number_idx on public.corp_invoices (invoice_number);
|
||||||
|
|
||||||
|
-- Invoice Line Items
|
||||||
|
create table if not exists public.corp_invoice_items (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
invoice_id uuid not null references public.corp_invoices(id) on delete cascade,
|
||||||
|
description text not null,
|
||||||
|
quantity numeric(10, 2) not null default 1,
|
||||||
|
unit_price numeric(12, 2) not null,
|
||||||
|
amount numeric(12, 2) not null,
|
||||||
|
category text, -- 'service', 'product', 'license', etc.
|
||||||
|
created_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_invoice_items_invoice_idx on public.corp_invoice_items (invoice_id);
|
||||||
|
|
||||||
|
-- Payments received on invoices
|
||||||
|
create table if not exists public.corp_invoice_payments (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
invoice_id uuid not null references public.corp_invoices(id) on delete cascade,
|
||||||
|
amount_paid numeric(12, 2) not null,
|
||||||
|
payment_date date not null default now(),
|
||||||
|
payment_method text not null default 'bank_transfer', -- 'stripe', 'bank_transfer', 'check', etc.
|
||||||
|
reference_number text,
|
||||||
|
notes text,
|
||||||
|
created_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_invoice_payments_invoice_idx on public.corp_invoice_payments (invoice_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- CONTRACTS & AGREEMENTS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
create table if not exists public.corp_contracts (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
client_company_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
vendor_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
contract_name text not null,
|
||||||
|
contract_type text not null check (contract_type in ('service', 'retainer', 'license', 'nda', 'other')),
|
||||||
|
description text,
|
||||||
|
start_date date,
|
||||||
|
end_date date,
|
||||||
|
contract_value numeric(12, 2),
|
||||||
|
status text not null default 'draft' check (status in ('draft', 'pending_approval', 'signed', 'active', 'completed', 'terminated', 'archived')),
|
||||||
|
document_url text, -- URL to signed document
|
||||||
|
signed_at timestamptz,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_contracts_client_idx on public.corp_contracts (client_company_id);
|
||||||
|
create index if not exists corp_contracts_vendor_idx on public.corp_contracts (vendor_id);
|
||||||
|
create index if not exists corp_contracts_status_idx on public.corp_contracts (status);
|
||||||
|
|
||||||
|
-- Contract Milestones
|
||||||
|
create table if not exists public.corp_contract_milestones (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
contract_id uuid not null references public.corp_contracts(id) on delete cascade,
|
||||||
|
milestone_name text not null,
|
||||||
|
description text,
|
||||||
|
due_date date,
|
||||||
|
deliverables text,
|
||||||
|
status text not null default 'pending' check (status in ('pending', 'in_progress', 'completed', 'blocked')),
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_contract_milestones_contract_idx on public.corp_contract_milestones (contract_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TEAM COLLABORATION
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
create table if not exists public.corp_team_members (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
company_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
user_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
role text not null check (role in ('owner', 'admin', 'member', 'viewer')),
|
||||||
|
email text not null,
|
||||||
|
full_name text,
|
||||||
|
job_title text,
|
||||||
|
status text not null default 'active' check (status in ('active', 'inactive', 'pending_invite')),
|
||||||
|
invited_at timestamptz,
|
||||||
|
joined_at timestamptz,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now(),
|
||||||
|
unique(company_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_team_members_company_idx on public.corp_team_members (company_id);
|
||||||
|
create index if not exists corp_team_members_user_idx on public.corp_team_members (user_id);
|
||||||
|
|
||||||
|
-- Activity Log (for audit trail)
|
||||||
|
create table if not exists public.corp_activity_log (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
company_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
actor_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
action text not null, -- 'created_invoice', 'sent_contract', 'paid_invoice', etc.
|
||||||
|
resource_type text, -- 'invoice', 'contract', 'team_member'
|
||||||
|
resource_id uuid,
|
||||||
|
metadata jsonb,
|
||||||
|
ip_address text,
|
||||||
|
user_agent text,
|
||||||
|
created_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_activity_log_company_idx on public.corp_activity_log (company_id);
|
||||||
|
create index if not exists corp_activity_log_actor_idx on public.corp_activity_log (actor_id);
|
||||||
|
create index if not exists corp_activity_log_created_idx on public.corp_activity_log (created_at desc);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- PROJECTS & TRACKING
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
create table if not exists public.corp_projects (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
client_company_id uuid not null references public.user_profiles(id) on delete cascade,
|
||||||
|
project_name text not null,
|
||||||
|
description text,
|
||||||
|
status text not null default 'active' check (status in ('planning', 'active', 'paused', 'completed', 'archived')),
|
||||||
|
budget numeric(12, 2),
|
||||||
|
spent numeric(12, 2) default 0,
|
||||||
|
start_date date,
|
||||||
|
end_date date,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_projects_client_idx on public.corp_projects (client_company_id);
|
||||||
|
create index if not exists corp_projects_status_idx on public.corp_projects (status);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ANALYTICS & REPORTING
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
create table if not exists public.corp_financial_summary (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
company_id uuid not null unique references public.user_profiles(id) on delete cascade,
|
||||||
|
period_start date not null,
|
||||||
|
period_end date not null,
|
||||||
|
total_invoiced numeric(12, 2) default 0,
|
||||||
|
total_paid numeric(12, 2) default 0,
|
||||||
|
total_overdue numeric(12, 2) default 0,
|
||||||
|
active_contracts int default 0,
|
||||||
|
completed_contracts int default 0,
|
||||||
|
total_contract_value numeric(12, 2) default 0,
|
||||||
|
average_payment_days int,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
create index if not exists corp_financial_summary_company_idx on public.corp_financial_summary (company_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- RLS POLICIES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
alter table public.corp_invoices enable row level security;
|
||||||
|
alter table public.corp_invoice_items enable row level security;
|
||||||
|
alter table public.corp_invoice_payments enable row level security;
|
||||||
|
alter table public.corp_contracts enable row level security;
|
||||||
|
alter table public.corp_contract_milestones enable row level security;
|
||||||
|
alter table public.corp_team_members enable row level security;
|
||||||
|
alter table public.corp_activity_log enable row level security;
|
||||||
|
alter table public.corp_projects enable row level security;
|
||||||
|
alter table public.corp_financial_summary enable row level security;
|
||||||
|
|
||||||
|
-- Invoices: client and team members can view
|
||||||
|
create policy "Invoices visible to client and team" on public.corp_invoices
|
||||||
|
for select using (
|
||||||
|
auth.uid() = client_company_id or
|
||||||
|
exists(select 1 from public.corp_team_members where company_id = client_company_id and user_id = auth.uid())
|
||||||
|
);
|
||||||
|
|
||||||
|
create policy "Client creates invoices" on public.corp_invoices
|
||||||
|
for insert with check (auth.uid() = client_company_id);
|
||||||
|
|
||||||
|
create policy "Client manages invoices" on public.corp_invoices
|
||||||
|
for update using (auth.uid() = client_company_id);
|
||||||
|
|
||||||
|
-- Contracts: parties involved can view
|
||||||
|
create policy "Contracts visible to involved parties" on public.corp_contracts
|
||||||
|
for select using (
|
||||||
|
auth.uid() = client_company_id or auth.uid() = vendor_id or
|
||||||
|
exists(select 1 from public.corp_team_members where company_id = client_company_id and user_id = auth.uid())
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Team: company members can view
|
||||||
|
create policy "Team members visible to company" on public.corp_team_members
|
||||||
|
for select using (
|
||||||
|
auth.uid() = company_id or
|
||||||
|
exists(select 1 from public.corp_team_members where company_id = company_id and user_id = auth.uid())
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Activity: company members can view
|
||||||
|
create policy "Activity visible to company" on public.corp_activity_log
|
||||||
|
for select using (
|
||||||
|
exists(select 1 from public.corp_team_members where company_id = company_id and user_id = auth.uid())
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TRIGGERS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
create or replace function public.set_updated_at()
|
||||||
|
returns trigger as $$
|
||||||
|
begin
|
||||||
|
new.updated_at = now();
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
create trigger corp_invoices_set_updated_at before update on public.corp_invoices for each row execute function public.set_updated_at();
|
||||||
|
create trigger corp_contracts_set_updated_at before update on public.corp_contracts for each row execute function public.set_updated_at();
|
||||||
|
create trigger corp_contract_milestones_set_updated_at before update on public.corp_contract_milestones for each row execute function public.set_updated_at();
|
||||||
|
create trigger corp_team_members_set_updated_at before update on public.corp_team_members for each row execute function public.set_updated_at();
|
||||||
|
create trigger corp_projects_set_updated_at before update on public.corp_projects for each row execute function public.set_updated_at();
|
||||||
|
create trigger corp_financial_summary_set_updated_at before update on public.corp_financial_summary for each row execute function public.set_updated_at();
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMMENTS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
comment on table public.corp_invoices is 'Invoices issued by the company to clients';
|
||||||
|
comment on table public.corp_invoice_items is 'Line items on invoices';
|
||||||
|
comment on table public.corp_invoice_payments is 'Payments received on invoices';
|
||||||
|
comment on table public.corp_contracts is 'Contracts with vendors and clients';
|
||||||
|
comment on table public.corp_team_members is 'Team members with access to the hub';
|
||||||
|
comment on table public.corp_activity_log is 'Audit trail of all activities';
|
||||||
|
comment on table public.corp_projects is 'Client projects for tracking work';
|
||||||
|
comment on table public.corp_financial_summary is 'Financial summary and metrics';
|
||||||
Loading…
Reference in a new issue