-- Invoice numbers were generated client-side from a row count, which reuses -- numbers after deletes and collides under concurrency. Replace with a DB -- function that takes max+1 for the year under an advisory lock, and enforce -- uniqueness at the schema level. create or replace function public.next_invoice_number() returns text language plpgsql security definer set search_path = public as $$ declare yr text := to_char(now(), 'YYYY'); next_n int; begin -- Serialize concurrent invoice creation within this transaction perform pg_advisory_xact_lock(hashtext('invoice_number_' || yr)); select coalesce( max((regexp_match(invoice_number, '^INV-' || yr || '-(\d+)$'))[1]::int), 0 ) + 1 into next_n from public.invoices where invoice_number like 'INV-' || yr || '-%'; return 'INV-' || yr || '-' || lpad(next_n::text, 3, '0'); end; $$; revoke all on function public.next_invoice_number() from public; grant execute on function public.next_invoice_number() to authenticated; -- Enforce uniqueness going forward (fails if historical duplicates exist — -- those must be reviewed manually first, never auto-renumbered). create unique index if not exists invoices_invoice_number_key on public.invoices (invoice_number);