mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
Accounting: back-sync bill paid status to bill_approvals on INSERT
trg_acct_bill_paid_back was AFTER UPDATE only, so bills mirrored into accounting already-paid never triggered the back-sync that flips public.bills + bill_approvals to 'paid'. Fire the trigger on INSERT too and reconcile existing already-paid mirrored bills. Also backfill invoice-track approvals that uniquely match a bill (bill_id was null). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
-- Fix: bill_approvals not reflecting "paid" when an accounting.bills row is
|
||||
-- created already-paid.
|
||||
--
|
||||
-- Background (see 20260601140000_accounting_sync_bills.sql):
|
||||
-- accounting.sync_accounting_bill_paid() flips public.bills + public.bill_approvals
|
||||
-- to 'paid' once the matching accounting bill is fully paid. It runs from the
|
||||
-- trigger trg_acct_bill_paid_back, which was AFTER UPDATE only.
|
||||
--
|
||||
-- When a public bill is ALREADY paid at the moment it first mirrors into
|
||||
-- accounting, the forward sync INSERTs the accounting row at status='paid'
|
||||
-- with paid_amount set. With no subsequent UPDATE to paid_amount/total, the
|
||||
-- UPDATE-only trigger never fired, so the bill's approvals were left stuck at
|
||||
-- 'approved'/'pending'. This widens the back-sync to also run on INSERT.
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Back-sync trigger: now fires on INSERT as well as UPDATE.
|
||||
-- ---------------------------------------------------------------------------
|
||||
create or replace function accounting.tg_accounting_bill_paid_sync()
|
||||
returns trigger
|
||||
language plpgsql security definer set search_path to 'public','accounting'
|
||||
as $$
|
||||
begin
|
||||
begin
|
||||
if tg_op = 'INSERT' then
|
||||
-- a bill mirrored in already-paid never produces an UPDATE; handle it here
|
||||
perform accounting.sync_accounting_bill_paid(new.id);
|
||||
-- on UPDATE only act when the paid position actually changed, to avoid loops
|
||||
elsif old.paid_amount is distinct from new.paid_amount
|
||||
or old.total is distinct from new.total then
|
||||
perform accounting.sync_accounting_bill_paid(new.id);
|
||||
end if;
|
||||
exception when others then
|
||||
raise warning 'accounting: accounting bill paid sync failed for %: %', new.id, sqlerrm;
|
||||
end;
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists trg_acct_bill_paid_back on accounting.bills;
|
||||
create trigger trg_acct_bill_paid_back
|
||||
after insert or update on accounting.bills
|
||||
for each row execute function accounting.tg_accounting_bill_paid_sync();
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- One-time reconciliation: back-sync any accounting bills that are already
|
||||
-- fully paid but whose linked public bill / approvals were never updated
|
||||
-- (the rows that fell into the INSERT gap above).
|
||||
-- ---------------------------------------------------------------------------
|
||||
do $$
|
||||
declare _id uuid;
|
||||
begin
|
||||
for _id in
|
||||
select id from accounting.bills
|
||||
where external_source = 'acmacc_bill' and external_id is not null
|
||||
and coalesce(paid_amount,0) >= coalesce(total,0) and coalesce(total,0) > 0
|
||||
loop
|
||||
perform accounting.sync_accounting_bill_paid(_id);
|
||||
end loop;
|
||||
end $$;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Backfill orphaned, invoice-track approvals (bill_id IS NULL) that were
|
||||
-- created via the invoice flow and never linked to a public.bills row.
|
||||
-- Only link when exactly one matching bill exists for the same association,
|
||||
-- invoice number and amount; adopt that bill's status when it is paid.
|
||||
-- ---------------------------------------------------------------------------
|
||||
update public.bill_approvals ba
|
||||
set bill_id = m.bill_id,
|
||||
status = case when m.bill_status = 'paid' then 'paid' else ba.status end,
|
||||
updated_at = now()
|
||||
from (
|
||||
select i.id as invoice_id, b.id as bill_id, b.status as bill_status
|
||||
from public.invoices i
|
||||
join public.bills b
|
||||
on b.association_id = i.association_id
|
||||
and b.amount = i.amount
|
||||
and b.invoice_number is not distinct from i.invoice_number
|
||||
group by i.id, b.id, b.status
|
||||
having (
|
||||
select count(*) from public.bills b2
|
||||
where b2.association_id = i.association_id
|
||||
and b2.amount = i.amount
|
||||
and b2.invoice_number is not distinct from i.invoice_number
|
||||
) = 1
|
||||
) m
|
||||
where ba.bill_id is null
|
||||
and ba.invoice_id = m.invoice_id;
|
||||
Reference in New Issue
Block a user