/* * ------------------------------------------------------- * Finance / SEPA Schema * Phase 8: sepa_batches, sepa_items, invoices, invoice_items * ------------------------------------------------------- */ create type public.sepa_batch_type as enum('direct_debit', 'credit_transfer'); create type public.sepa_batch_status as enum('draft', 'ready', 'submitted', 'executed', 'failed', 'cancelled'); create type public.sepa_item_status as enum('pending', 'success', 'failed', 'rejected'); create type public.invoice_status as enum('draft', 'sent', 'paid', 'overdue', 'cancelled', 'credited'); -- SEPA batches create table if not exists public.sepa_batches ( id uuid primary key default gen_random_uuid(), account_id uuid not null references public.accounts(id) on delete cascade, batch_type public.sepa_batch_type not null, status public.sepa_batch_status not null default 'draft', description text, execution_date date not null, total_amount numeric(12,2) not null default 0, item_count integer not null default 0, xml_storage_path text, pain_format text not null default 'pain.008.003.02', created_by uuid references auth.users(id) on delete set null, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create index ix_sepa_batches_account on public.sepa_batches(account_id); alter table public.sepa_batches enable row level security; revoke all on public.sepa_batches from authenticated, service_role; grant select, insert, update, delete on public.sepa_batches to authenticated; grant all on public.sepa_batches to service_role; create policy sepa_batches_select on public.sepa_batches for select to authenticated using (public.has_role_on_account(account_id)); create policy sepa_batches_insert on public.sepa_batches for insert to authenticated with check (public.has_permission(auth.uid(), account_id, 'finance.sepa'::public.app_permissions)); create policy sepa_batches_update on public.sepa_batches for update to authenticated using (public.has_permission(auth.uid(), account_id, 'finance.sepa'::public.app_permissions)); create policy sepa_batches_delete on public.sepa_batches for delete to authenticated using (public.has_permission(auth.uid(), account_id, 'finance.sepa'::public.app_permissions)); -- SEPA items (individual transactions) create table if not exists public.sepa_items ( id uuid primary key default gen_random_uuid(), batch_id uuid not null references public.sepa_batches(id) on delete cascade, member_id uuid references public.members(id) on delete set null, debtor_name text not null, debtor_iban text not null, debtor_bic text, amount numeric(10,2) not null, mandate_id text, mandate_date date, remittance_info text, status public.sepa_item_status not null default 'pending', error_message text, created_at timestamptz not null default now() ); create index ix_sepa_items_batch on public.sepa_items(batch_id); create index ix_sepa_items_member on public.sepa_items(member_id); alter table public.sepa_items enable row level security; revoke all on public.sepa_items from authenticated, service_role; grant select, insert, update, delete on public.sepa_items to authenticated; grant all on public.sepa_items to service_role; create policy sepa_items_select on public.sepa_items for select to authenticated using (exists (select 1 from public.sepa_batches b where b.id = sepa_items.batch_id and public.has_role_on_account(b.account_id))); create policy sepa_items_mutate on public.sepa_items for all to authenticated using (exists (select 1 from public.sepa_batches b where b.id = sepa_items.batch_id and public.has_permission(auth.uid(), b.account_id, 'finance.sepa'::public.app_permissions))); -- Invoices create table if not exists public.invoices ( id uuid primary key default gen_random_uuid(), account_id uuid not null references public.accounts(id) on delete cascade, invoice_number text not null, member_id uuid references public.members(id) on delete set null, recipient_name text not null, recipient_address text, issue_date date not null default current_date, due_date date not null, status public.invoice_status not null default 'draft', subtotal numeric(10,2) not null default 0, tax_rate numeric(5,2) not null default 0, tax_amount numeric(10,2) not null default 0, total_amount numeric(10,2) not null default 0, paid_amount numeric(10,2) not null default 0, paid_at timestamptz, notes text, pdf_storage_path text, created_by uuid references auth.users(id) on delete set null, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique(account_id, invoice_number) ); create index ix_invoices_account on public.invoices(account_id); create index ix_invoices_member on public.invoices(member_id); create index ix_invoices_status on public.invoices(account_id, status); alter table public.invoices enable row level security; revoke all on public.invoices from authenticated, service_role; grant select, insert, update, delete on public.invoices to authenticated; grant all on public.invoices to service_role; create policy invoices_select on public.invoices for select to authenticated using (public.has_role_on_account(account_id)); create policy invoices_insert on public.invoices for insert to authenticated with check (public.has_permission(auth.uid(), account_id, 'finance.write'::public.app_permissions)); create policy invoices_update on public.invoices for update to authenticated using (public.has_permission(auth.uid(), account_id, 'finance.write'::public.app_permissions)); create policy invoices_delete on public.invoices for delete to authenticated using (public.has_permission(auth.uid(), account_id, 'finance.write'::public.app_permissions)); create trigger trg_invoices_updated_at before update on public.invoices for each row execute function public.update_account_settings_timestamp(); -- Invoice line items create table if not exists public.invoice_items ( id uuid primary key default gen_random_uuid(), invoice_id uuid not null references public.invoices(id) on delete cascade, description text not null, quantity numeric(10,2) not null default 1, unit_price numeric(10,2) not null, total_price numeric(10,2) not null, sort_order integer not null default 0, created_at timestamptz not null default now() ); create index ix_invoice_items_invoice on public.invoice_items(invoice_id); alter table public.invoice_items enable row level security; revoke all on public.invoice_items from authenticated, service_role; grant select, insert, update, delete on public.invoice_items to authenticated; grant all on public.invoice_items to service_role; create policy invoice_items_select on public.invoice_items for select to authenticated using (exists (select 1 from public.invoices i where i.id = invoice_items.invoice_id and public.has_role_on_account(i.account_id))); create policy invoice_items_mutate on public.invoice_items for all to authenticated using (exists (select 1 from public.invoices i where i.id = invoice_items.invoice_id and public.has_permission(auth.uid(), i.account_id, 'finance.write'::public.app_permissions)));