| 1 | CREATE OR REPLACE FUNCTION public.fn_recalculate_account_balance(p_account_id bigint)
|
|---|
| 2 | RETURNS void
|
|---|
| 3 | LANGUAGE sql
|
|---|
| 4 | AS $$
|
|---|
| 5 | UPDATE public.accounts a
|
|---|
| 6 | SET current_balance = GREATEST(COALESCE((
|
|---|
| 7 | SELECT SUM(i.total_amount - COALESCE((
|
|---|
| 8 | SELECT SUM(p.amount)
|
|---|
| 9 | FROM public.payments p
|
|---|
| 10 | WHERE p.invoice_id = i.invoice_id AND p.status = 'completed'
|
|---|
| 11 | ), 0))
|
|---|
| 12 | FROM public.invoices i
|
|---|
| 13 | WHERE i.account_id = p_account_id AND i.status <> 'cancelled'
|
|---|
| 14 | ), 0), 0)
|
|---|
| 15 | WHERE a.account_id = p_account_id;
|
|---|
| 16 | $$;
|
|---|
| 17 |
|
|---|
| 18 | CREATE OR REPLACE FUNCTION public.fn_recalculate_invoice_status(p_invoice_id bigint)
|
|---|
| 19 | RETURNS void
|
|---|
| 20 | LANGUAGE plpgsql
|
|---|
| 21 | AS $$
|
|---|
| 22 | DECLARE
|
|---|
| 23 | v_total numeric;
|
|---|
| 24 | v_paid numeric;
|
|---|
| 25 | v_due_date date;
|
|---|
| 26 | v_status text;
|
|---|
| 27 | BEGIN
|
|---|
| 28 | SELECT total_amount, due_date, status
|
|---|
| 29 | INTO v_total, v_due_date, v_status
|
|---|
| 30 | FROM public.invoices
|
|---|
| 31 | WHERE invoice_id = p_invoice_id;
|
|---|
| 32 |
|
|---|
| 33 | IF NOT FOUND OR v_status = 'cancelled' THEN
|
|---|
| 34 | RETURN;
|
|---|
| 35 | END IF;
|
|---|
| 36 |
|
|---|
| 37 | SELECT COALESCE(SUM(amount), 0)
|
|---|
| 38 | INTO v_paid
|
|---|
| 39 | FROM public.payments
|
|---|
| 40 | WHERE invoice_id = p_invoice_id AND status = 'completed';
|
|---|
| 41 |
|
|---|
| 42 | UPDATE public.invoices
|
|---|
| 43 | SET status = CASE
|
|---|
| 44 | WHEN v_paid >= v_total - 0.01 THEN 'paid'
|
|---|
| 45 | WHEN v_due_date < CURRENT_DATE AND v_paid < v_total - 0.01 THEN 'overdue'
|
|---|
| 46 | WHEN v_paid > 0 THEN 'partially_paid'
|
|---|
| 47 | ELSE 'issued'
|
|---|
| 48 | END
|
|---|
| 49 | WHERE invoice_id = p_invoice_id;
|
|---|
| 50 | END;
|
|---|
| 51 | $$;
|
|---|
| 52 |
|
|---|
| 53 | DROP TRIGGER IF EXISTS trg_sync_financials_on_payment ON public.payments;
|
|---|
| 54 | CREATE OR REPLACE FUNCTION public.trg_fn_sync_financials_on_payment()
|
|---|
| 55 | RETURNS trigger
|
|---|
| 56 | LANGUAGE plpgsql
|
|---|
| 57 | AS $$
|
|---|
| 58 | DECLARE
|
|---|
| 59 | v_new_invoice_id bigint;
|
|---|
| 60 | v_old_invoice_id bigint;
|
|---|
| 61 | v_new_account_id bigint;
|
|---|
| 62 | v_old_account_id bigint;
|
|---|
| 63 | BEGIN
|
|---|
| 64 | IF TG_OP <> 'DELETE' THEN
|
|---|
| 65 | v_new_invoice_id := NEW.invoice_id;
|
|---|
| 66 | v_new_account_id := NEW.account_id;
|
|---|
| 67 | END IF;
|
|---|
| 68 | IF TG_OP <> 'INSERT' THEN
|
|---|
| 69 | v_old_invoice_id := OLD.invoice_id;
|
|---|
| 70 | v_old_account_id := OLD.account_id;
|
|---|
| 71 | END IF;
|
|---|
| 72 |
|
|---|
| 73 | IF v_new_invoice_id IS NOT NULL THEN
|
|---|
| 74 | PERFORM public.fn_recalculate_invoice_status(v_new_invoice_id);
|
|---|
| 75 | END IF;
|
|---|
| 76 | IF v_old_invoice_id IS NOT NULL AND v_old_invoice_id IS DISTINCT FROM v_new_invoice_id THEN
|
|---|
| 77 | PERFORM public.fn_recalculate_invoice_status(v_old_invoice_id);
|
|---|
| 78 | END IF;
|
|---|
| 79 | IF v_new_account_id IS NOT NULL THEN
|
|---|
| 80 | PERFORM public.fn_recalculate_account_balance(v_new_account_id);
|
|---|
| 81 | END IF;
|
|---|
| 82 | IF v_old_account_id IS NOT NULL AND v_old_account_id IS DISTINCT FROM v_new_account_id THEN
|
|---|
| 83 | PERFORM public.fn_recalculate_account_balance(v_old_account_id);
|
|---|
| 84 | END IF;
|
|---|
| 85 |
|
|---|
| 86 | IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
|
|---|
| 87 | RETURN NEW;
|
|---|
| 88 | END;
|
|---|
| 89 | $$;
|
|---|
| 90 |
|
|---|
| 91 | CREATE TRIGGER trg_sync_financials_on_payment
|
|---|
| 92 | AFTER INSERT OR UPDATE OR DELETE ON public.payments
|
|---|
| 93 | FOR EACH ROW
|
|---|
| 94 | EXECUTE FUNCTION public.trg_fn_sync_financials_on_payment();
|
|---|