-- Restrict the "paid" transition on bills to administrators only. -- -- Background: bills RLS lets admin, manager, association_management, and -- accounting staff update bills, so any of them could mark a bill paid. Per -- requirement, only admins may do that. This trigger enforces it at the data -- layer regardless of how the write arrives (UI, API, direct SQL). -- -- System/automation contexts (buildium-sync, accounting triggers, autopay) -- run with the service role, where auth.uid() is NULL — those remain allowed -- so imports and back-syncs continue to work. create or replace function public.enforce_admin_marks_bill_paid() returns trigger language plpgsql security definer set search_path to 'public' as $$ begin if NEW.status = 'paid' and (TG_OP = 'INSERT' or OLD.status is distinct from 'paid') then if auth.uid() is not null and not public.has_role(auth.uid(), 'admin'::app_role) then raise exception 'Only administrators can mark a bill as paid' using errcode = 'check_violation'; end if; end if; return NEW; end; $$; drop trigger if exists trg_bills_admin_paid_guard on public.bills; create trigger trg_bills_admin_paid_guard before insert or update on public.bills for each row execute function public.enforce_admin_marks_bill_paid();