Add Stripe fee tracking on paid invoices + backfill function
- Store stripe_fee on invoices when webhook receives checkout.session.completed - Display Stripe fee and net received in InvoiceDetail when paid via Stripe - Add backfill-stripe-fees edge function to populate fee on existing paid invoices - Migration: add stripe_fee column to invoices table - Includes all pending portal changes (brand book, sign survey, task/project/company updates, etc.) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
-- Add stripe_fee column to store the Stripe processing fee on paid invoices
|
||||
alter table public.invoices add column if not exists stripe_fee numeric(10,2);
|
||||
@@ -0,0 +1,13 @@
|
||||
-- Add cover page fields to brand_books table
|
||||
ALTER TABLE brand_books
|
||||
ADD COLUMN IF NOT EXISTS creation_date date,
|
||||
ADD COLUMN IF NOT EXISTS revision_date date,
|
||||
ADD COLUMN IF NOT EXISTS customer_name text,
|
||||
ADD COLUMN IF NOT EXISTS customer_address text,
|
||||
ADD COLUMN IF NOT EXISTS project_logo_path text,
|
||||
ADD COLUMN IF NOT EXISTS client_logo_url text,
|
||||
ADD COLUMN IF NOT EXISTS client_contact_name text,
|
||||
ADD COLUMN IF NOT EXISTS client_contact_email text,
|
||||
ADD COLUMN IF NOT EXISTS client_contact_phone text,
|
||||
ADD COLUMN IF NOT EXISTS approved_date date,
|
||||
ADD COLUMN IF NOT EXISTS approval_notes text;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add template and inventory map fields to brand_books table
|
||||
ALTER TABLE brand_books
|
||||
ADD COLUMN IF NOT EXISTS template text DEFAULT 'fourge',
|
||||
ADD COLUMN IF NOT EXISTS inventory_map_path text;
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Add brand book / cover page fields to companies
|
||||
ALTER TABLE companies
|
||||
ADD COLUMN IF NOT EXISTS address text,
|
||||
ADD COLUMN IF NOT EXISTS contact_name text,
|
||||
ADD COLUMN IF NOT EXISTS contact_email text,
|
||||
ADD COLUMN IF NOT EXISTS contact_phone text,
|
||||
ADD COLUMN IF NOT EXISTS client_logo_url text;
|
||||
|
||||
-- Create public bucket for company logos
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM storage.buckets WHERE id = 'company-logos') THEN
|
||||
INSERT INTO storage.buckets (id, name, public) VALUES ('company-logos', 'company-logos', true);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Storage policies for company-logos
|
||||
DROP POLICY IF EXISTS "Authenticated users can manage company logos" ON storage.objects;
|
||||
DROP POLICY IF EXISTS "Public can read company logos" ON storage.objects;
|
||||
|
||||
CREATE POLICY "Authenticated users can manage company logos"
|
||||
ON storage.objects FOR ALL
|
||||
TO authenticated
|
||||
USING (bucket_id = 'company-logos')
|
||||
WITH CHECK (bucket_id = 'company-logos');
|
||||
|
||||
CREATE POLICY "Public can read company logos"
|
||||
ON storage.objects FOR SELECT
|
||||
USING (bucket_id = 'company-logos');
|
||||
@@ -0,0 +1,123 @@
|
||||
-- ============================================================
|
||||
-- Migration: Add external role and project_members table
|
||||
-- Run this in Supabase → SQL Editor → Run
|
||||
-- ============================================================
|
||||
|
||||
-- 1. Update profiles.role check constraint to include 'external'
|
||||
do $$
|
||||
declare
|
||||
cname text;
|
||||
begin
|
||||
select constraint_name into cname
|
||||
from information_schema.table_constraints
|
||||
where table_schema = 'public'
|
||||
and table_name = 'profiles'
|
||||
and constraint_type = 'CHECK'
|
||||
and constraint_name ilike '%role%';
|
||||
if cname is not null then
|
||||
execute 'alter table public.profiles drop constraint ' || quote_ident(cname);
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
|
||||
alter table public.profiles
|
||||
add constraint profiles_role_check check (role in ('team', 'client', 'external'));
|
||||
|
||||
-- 2. project_members table
|
||||
create table public.project_members (
|
||||
id uuid default gen_random_uuid() primary key,
|
||||
project_id uuid references public.projects(id) on delete cascade not null,
|
||||
profile_id uuid references public.profiles(id) on delete cascade not null,
|
||||
created_at timestamptz default now() not null,
|
||||
unique(project_id, profile_id)
|
||||
);
|
||||
alter table public.project_members enable row level security;
|
||||
|
||||
-- 3. Helper function
|
||||
create or replace function public.is_external()
|
||||
returns boolean as $$
|
||||
select get_my_role() = 'external';
|
||||
$$ language sql security definer stable;
|
||||
|
||||
-- 4. RLS: project_members
|
||||
create policy "Team all project_members" on public.project_members
|
||||
for all using (get_my_role() = 'team');
|
||||
create policy "External reads own memberships" on public.project_members
|
||||
for select using (profile_id = auth.uid());
|
||||
|
||||
-- 5. RLS: projects (external reads assigned only)
|
||||
create policy "External reads assigned projects" on public.projects
|
||||
for select using (
|
||||
get_my_role() = 'external' and
|
||||
id in (select project_id from public.project_members where profile_id = auth.uid())
|
||||
);
|
||||
|
||||
-- 6. RLS: tasks (external reads + updates assigned projects)
|
||||
create policy "External reads assigned tasks" on public.tasks
|
||||
for select using (
|
||||
get_my_role() = 'external' and
|
||||
project_id in (select project_id from public.project_members where profile_id = auth.uid())
|
||||
);
|
||||
create policy "External updates assigned tasks" on public.tasks
|
||||
for update using (
|
||||
get_my_role() = 'external' and
|
||||
project_id in (select project_id from public.project_members where profile_id = auth.uid())
|
||||
);
|
||||
|
||||
-- 7. RLS: submissions
|
||||
create policy "External reads assigned submissions" on public.submissions
|
||||
for select using (
|
||||
get_my_role() = 'external' and
|
||||
task_id in (
|
||||
select t.id from public.tasks t
|
||||
join public.project_members pm on pm.project_id = t.project_id
|
||||
where pm.profile_id = auth.uid()
|
||||
)
|
||||
);
|
||||
create policy "External inserts submissions" on public.submissions
|
||||
for insert with check (
|
||||
get_my_role() = 'external' and submitted_by = auth.uid()
|
||||
);
|
||||
|
||||
-- 8. RLS: submission_files
|
||||
create policy "External reads assigned submission_files" on public.submission_files
|
||||
for select using (
|
||||
get_my_role() = 'external' and
|
||||
submission_id in (
|
||||
select s.id from public.submissions s
|
||||
join public.tasks t on t.id = s.task_id
|
||||
join public.project_members pm on pm.project_id = t.project_id
|
||||
where pm.profile_id = auth.uid()
|
||||
)
|
||||
);
|
||||
create policy "External inserts submission_files" on public.submission_files
|
||||
for insert with check (get_my_role() = 'external');
|
||||
|
||||
-- 9. RLS: deliveries (read only)
|
||||
create policy "External reads assigned deliveries" on public.deliveries
|
||||
for select using (
|
||||
get_my_role() = 'external' and
|
||||
submission_id in (
|
||||
select s.id from public.submissions s
|
||||
join public.tasks t on t.id = s.task_id
|
||||
join public.project_members pm on pm.project_id = t.project_id
|
||||
where pm.profile_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- 10. RLS: delivery_files (read only)
|
||||
create policy "External reads assigned delivery_files" on public.delivery_files
|
||||
for select using (
|
||||
get_my_role() = 'external' and
|
||||
delivery_id in (
|
||||
select d.id from public.deliveries d
|
||||
join public.submissions s on s.id = d.submission_id
|
||||
join public.tasks t on t.id = s.task_id
|
||||
join public.project_members pm on pm.project_id = t.project_id
|
||||
where pm.profile_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- 11. RLS: profiles (external reads own profile only — already covered by existing policy)
|
||||
-- "Own profile select" policy already handles this with: id = auth.uid()
|
||||
-- No additional policy needed.
|
||||
@@ -0,0 +1,17 @@
|
||||
-- ============================================================
|
||||
-- Migration: Add price_type to company_prices (new vs revision)
|
||||
-- Run in Supabase → SQL Editor → Run
|
||||
-- ============================================================
|
||||
|
||||
-- Add price_type column (existing rows default to 'new')
|
||||
alter table public.company_prices
|
||||
add column price_type text not null default 'new'
|
||||
check (price_type in ('new', 'revision'));
|
||||
|
||||
-- Drop old unique constraint and add new one that includes price_type
|
||||
alter table public.company_prices
|
||||
drop constraint company_prices_company_id_service_type_key;
|
||||
|
||||
alter table public.company_prices
|
||||
add constraint company_prices_company_id_service_type_price_type_key
|
||||
unique (company_id, service_type, price_type);
|
||||
@@ -0,0 +1,15 @@
|
||||
-- ============================================================
|
||||
-- Migration: Add revision billing tracking
|
||||
-- Run in Supabase → SQL Editor → Run
|
||||
-- ============================================================
|
||||
|
||||
-- Add revision_type and invoiced to submissions
|
||||
alter table public.submissions
|
||||
add column revision_type text check (revision_type in ('fourge_error', 'client_revision'));
|
||||
|
||||
alter table public.submissions
|
||||
add column invoiced boolean not null default false;
|
||||
|
||||
-- Add submission_id to invoice_items (links a line item to a specific revision)
|
||||
alter table public.invoice_items
|
||||
add column submission_id uuid references public.submissions(id) on delete set null;
|
||||
Reference in New Issue
Block a user