create table if not exists public.commerce_adjustment_vouchers ( id uuid primary key default gen_random_uuid(), tenant_id uuid not null references public.tenants(id) on delete cascade, voucher_no text not null, source_type text not null default 'manual' check (source_type in ('reconciliation_issue', 'reconciliation_item', 'order', 'payment', 'refund', 'manual')), source_id uuid, reconciliation_issue_id uuid references public.commerce_reconciliation_issues(id) on delete set null, reconciliation_item_id uuid references public.commerce_reconciliation_items(id) on delete set null, order_id uuid references public.orders(id) on delete set null, payment_id uuid references public.payments(id) on delete set null, refund_request_id uuid references public.commerce_refund_requests(id) on delete set null, order_no text, refund_no text, provider text, provider_trade_no text, provider_refund_no text, adjustment_type text not null default 'other' check (adjustment_type in ( 'manual_payment_confirm', 'refund_correction', 'provider_confirmed', 'local_corrected', 'write_off', 'duplicate', 'other' )), direction text not null default 'none' check (direction in ('increase', 'decrease', 'none')), amount_cents integer not null default 0 check (amount_cents >= 0), status text not null default 'draft' check (status in ('draft', 'submitted', 'approved', 'rejected', 'voided')), title text not null, description text, asset_id uuid references public.content_assets(id) on delete set null, external_url text, submitted_by uuid references public.platform_users(id) on delete set null, submitted_at timestamptz, reviewed_by uuid references public.platform_users(id) on delete set null, reviewed_at timestamptz, review_note text, voided_by uuid references public.platform_users(id) on delete set null, voided_at timestamptz, metadata jsonb not null default '{}'::jsonb, created_by uuid references public.platform_users(id) on delete set null, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (tenant_id, voucher_no) ); create table if not exists public.commerce_adjustment_voucher_events ( id uuid primary key default gen_random_uuid(), tenant_id uuid not null references public.tenants(id) on delete cascade, voucher_id uuid not null references public.commerce_adjustment_vouchers(id) on delete cascade, from_status text, to_status text, event_type text not null, actor_user_id uuid references public.platform_users(id) on delete set null, note text, details jsonb not null default '{}'::jsonb, created_at timestamptz not null default now(), check (from_status is null or from_status in ('draft', 'submitted', 'approved', 'rejected', 'voided')), check (to_status is null or to_status in ('draft', 'submitted', 'approved', 'rejected', 'voided')) ); create index if not exists idx_commerce_adjustment_vouchers_tenant_status on public.commerce_adjustment_vouchers(tenant_id, status, created_at desc); create index if not exists idx_commerce_adjustment_vouchers_source on public.commerce_adjustment_vouchers(tenant_id, source_type, source_id, created_at desc); create index if not exists idx_commerce_adjustment_vouchers_issue on public.commerce_adjustment_vouchers(tenant_id, reconciliation_issue_id, status, created_at desc); create index if not exists idx_commerce_adjustment_vouchers_order on public.commerce_adjustment_vouchers(tenant_id, order_no, status, created_at desc); create index if not exists idx_commerce_adjustment_voucher_events_voucher on public.commerce_adjustment_voucher_events(tenant_id, voucher_id, created_at desc); alter table public.commerce_adjustment_vouchers enable row level security; alter table public.commerce_adjustment_voucher_events enable row level security; drop policy if exists tenant_isolation on public.commerce_adjustment_vouchers; create policy tenant_isolation on public.commerce_adjustment_vouchers for all using (tenant_id = app.current_tenant_id() or app.is_platform_admin()) with check (tenant_id = app.current_tenant_id() or app.is_platform_admin()); drop policy if exists tenant_isolation on public.commerce_adjustment_voucher_events; create policy tenant_isolation on public.commerce_adjustment_voucher_events for all using (tenant_id = app.current_tenant_id() or app.is_platform_admin()) with check (tenant_id = app.current_tenant_id() or app.is_platform_admin()); drop trigger if exists set_updated_at on public.commerce_adjustment_vouchers; create trigger set_updated_at before update on public.commerce_adjustment_vouchers for each row execute function app.touch_updated_at();