CREATE OR REPLACE FUNCTION public.is_collection_legal_matter( p_case_type text, p_category text, p_collection_id uuid ) RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$ SELECT p_collection_id IS NOT NULL OR lower(coalesce(p_case_type, '')) = 'collection' OR lower(coalesce(p_category, '')) = 'collections'; $$; CREATE OR REPLACE FUNCTION public.calculate_legal_matter_ledger_amount(p_legal_matter_id uuid) RETURNS numeric LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$ WITH matter AS ( SELECT lm.id, lm.unit_id AS matter_unit_id, lm.collection_id, c.owner_id AS collection_owner_id, c.unit_id AS collection_unit_id FROM public.legal_matters lm LEFT JOIN public.collections c ON c.id = lm.collection_id WHERE lm.id = p_legal_matter_id AND public.is_collection_legal_matter(lm.case_type, lm.category, lm.collection_id) ), target_owners AS ( SELECT DISTINCT o.id FROM matter m JOIN public.owners o ON o.status <> 'archived' AND ( (m.collection_owner_id IS NOT NULL AND o.id = m.collection_owner_id) OR (coalesce(m.collection_unit_id, m.matter_unit_id) IS NOT NULL AND o.unit_id = coalesce(m.collection_unit_id, m.matter_unit_id)) ) ) SELECT COALESCE(ROUND(SUM(COALESCE(ole.debit, 0) - COALESCE(ole.credit, 0))::numeric, 2), 0) FROM public.owner_ledger_entries ole JOIN target_owners target ON target.id = ole.owner_id; $$; CREATE OR REPLACE FUNCTION public.sync_collection_legal_matter_amounts_for_owner_unit( p_owner_id uuid DEFAULT NULL, p_unit_id uuid DEFAULT NULL ) RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ BEGIN UPDATE public.legal_matters lm SET amount_due = public.calculate_legal_matter_ledger_amount(lm.id), updated_at = now() WHERE public.is_collection_legal_matter(lm.case_type, lm.category, lm.collection_id) AND ( (p_unit_id IS NOT NULL AND lm.unit_id = p_unit_id) OR EXISTS ( SELECT 1 FROM public.collections c WHERE c.id = lm.collection_id AND ( (p_owner_id IS NOT NULL AND c.owner_id = p_owner_id) OR (p_unit_id IS NOT NULL AND c.unit_id = p_unit_id) ) ) OR EXISTS ( SELECT 1 FROM public.owners o WHERE p_owner_id IS NOT NULL AND o.id = p_owner_id AND o.unit_id IS NOT NULL AND lm.unit_id = o.unit_id ) ); END; $$; CREATE OR REPLACE FUNCTION public.sync_collection_legal_matter_amounts_from_ledger() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ BEGIN IF TG_OP IN ('INSERT', 'UPDATE') THEN PERFORM public.sync_collection_legal_matter_amounts_for_owner_unit(NEW.owner_id, NEW.unit_id); END IF; IF TG_OP IN ('UPDATE', 'DELETE') THEN PERFORM public.sync_collection_legal_matter_amounts_for_owner_unit(OLD.owner_id, OLD.unit_id); END IF; RETURN COALESCE(NEW, OLD); END; $$; DROP TRIGGER IF EXISTS trg_sync_collection_legal_matter_amounts_from_ledger ON public.owner_ledger_entries; CREATE TRIGGER trg_sync_collection_legal_matter_amounts_from_ledger AFTER INSERT OR UPDATE OR DELETE ON public.owner_ledger_entries FOR EACH ROW EXECUTE FUNCTION public.sync_collection_legal_matter_amounts_from_ledger(); CREATE OR REPLACE FUNCTION public.sync_collection_legal_matter_amount_after_link_change() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ BEGIN IF public.is_collection_legal_matter(NEW.case_type, NEW.category, NEW.collection_id) THEN UPDATE public.legal_matters SET amount_due = public.calculate_legal_matter_ledger_amount(NEW.id), updated_at = now() WHERE id = NEW.id; END IF; RETURN NEW; END; $$; DROP TRIGGER IF EXISTS trg_sync_collection_legal_matter_amount_after_link_change ON public.legal_matters; CREATE TRIGGER trg_sync_collection_legal_matter_amount_after_link_change AFTER INSERT OR UPDATE OF collection_id, unit_id, case_type, category ON public.legal_matters FOR EACH ROW EXECUTE FUNCTION public.sync_collection_legal_matter_amount_after_link_change(); CREATE OR REPLACE FUNCTION public.sync_collection_legal_matter_amounts_from_collection() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ BEGIN IF TG_OP IN ('INSERT', 'UPDATE') THEN UPDATE public.legal_matters lm SET amount_due = public.calculate_legal_matter_ledger_amount(lm.id), updated_at = now() WHERE lm.collection_id = NEW.id AND public.is_collection_legal_matter(lm.case_type, lm.category, lm.collection_id); END IF; IF TG_OP IN ('UPDATE', 'DELETE') THEN UPDATE public.legal_matters lm SET amount_due = public.calculate_legal_matter_ledger_amount(lm.id), updated_at = now() WHERE lm.collection_id = OLD.id AND public.is_collection_legal_matter(lm.case_type, lm.category, lm.collection_id); END IF; RETURN COALESCE(NEW, OLD); END; $$; DROP TRIGGER IF EXISTS trg_sync_collection_legal_matter_amounts_from_collection ON public.collections; CREATE TRIGGER trg_sync_collection_legal_matter_amounts_from_collection AFTER INSERT OR UPDATE OR DELETE ON public.collections FOR EACH ROW EXECUTE FUNCTION public.sync_collection_legal_matter_amounts_from_collection();