DatabaseProgramming: 03 Prevent overpaid invoice trigger.sql

File 03 Prevent overpaid invoice trigger.sql, 1.8 KB (added by 231139, 10 days ago)
Line 
1DROP TRIGGER IF EXISTS trg_prevent_overpaid_invoice ON public.payments;
2CREATE OR REPLACE FUNCTION public.trg_fn_prevent_overpaid_invoice()
3RETURNS trigger
4LANGUAGE plpgsql
5AS $$
6DECLARE
7 v_invoice_total numeric;
8 v_invoice_status text;
9 v_total_paid numeric;
10BEGIN
11 IF NEW.invoice_id IS NULL OR NEW.status <> 'completed' THEN
12 RETURN NEW;
13 END IF;
14
15 SELECT total_amount, status
16 INTO v_invoice_total, v_invoice_status
17 FROM public.invoices
18 WHERE invoice_id = NEW.invoice_id
19 AND account_id = NEW.account_id
20 FOR UPDATE;
21
22 IF NOT FOUND THEN
23 RAISE EXCEPTION 'Invoice % does not belong to account %.', NEW.invoice_id, NEW.account_id;
24 END IF;
25 IF v_invoice_status = 'cancelled' THEN
26 RAISE EXCEPTION 'Payment rejected: invoice % is cancelled.', NEW.invoice_id;
27 END IF;
28
29 SELECT COALESCE(SUM(amount), 0)
30 INTO v_total_paid
31 FROM public.payments
32 WHERE invoice_id = NEW.invoice_id
33 AND status = 'completed'
34 AND payment_id IS DISTINCT FROM NEW.payment_id;
35
36 IF v_total_paid >= v_invoice_total - 0.01 THEN
37 RAISE EXCEPTION 'Payment rejected: invoice % is already fully paid.', NEW.invoice_id;
38 END IF;
39 IF v_total_paid + NEW.amount > v_invoice_total + 0.01 THEN
40 RAISE EXCEPTION 'Payment rejected: invoice % has remaining balance %, attempted payment is %.',
41 NEW.invoice_id, v_invoice_total - v_total_paid, NEW.amount;
42 END IF;
43
44 RETURN NEW;
45END;
46$$;
47
48CREATE TRIGGER trg_prevent_overpaid_invoice
49BEFORE INSERT OR UPDATE ON public.payments
50FOR EACH ROW
51EXECUTE FUNCTION public.trg_fn_prevent_overpaid_invoice();
52COMMENT ON FUNCTION public.trg_fn_prevent_overpaid_invoice() IS
53'Locks the invoice row and prevents completed payments from exceeding its total, including concurrent attempts.';