--liquibase formatted sql -- PANELS · core · edición de préstamos y estado de cuenta --changeset panel:core-045a-fn-loan-update splitStatements:false CREATE OR REPLACE FUNCTION core.fn_loan_update(payload jsonb) RETURNS jsonb LANGUAGE plpgsql SECURITY INVOKER SET search_path = core AS $$ DECLARE v_id bigint := NULLIF(payload->>'id', '')::bigint; v_loan loans%ROWTYPE; v_paid numeric; v_n integer; v_delivered numeric; v_pct numeric; v_comm numeric; v_balance numeric; v_weekly numeric; v_note text; BEGIN IF v_id IS NULL THEN RETURN core.rpc_err('VALIDATION', 'fn_loan_update: id es obligatorio', jsonb_build_object('fn', 'fn_loan_update', 'field', 'id')); END IF; SELECT * INTO v_loan FROM loans WHERE id = v_id; IF NOT FOUND THEN RETURN core.rpc_err('NOT_FOUND', format('fn_loan_update: préstamo id=%s no encontrado', v_id), jsonb_build_object('fn', 'fn_loan_update', 'id', v_id)); END IF; SELECT COALESCE(SUM(amount), 0), COUNT(*)::integer INTO v_paid, v_n FROM loan_payments WHERE loan_id = v_id; v_note := CASE WHEN payload ? 'note' THEN nullif(btrim(payload->>'note'), '') ELSE v_loan.note END; IF v_n > 0 THEN v_weekly := COALESCE((payload->>'weekly_payment')::numeric, v_loan.weekly_payment); UPDATE loans SET note = v_note, weekly_payment = core.round_money(v_weekly) WHERE id = v_id; ELSE v_delivered := core.round_money(COALESCE((payload->>'delivered')::numeric, v_loan.delivered)); IF v_delivered <= 0 THEN RETURN core.rpc_err('VALIDATION', 'fn_loan_update: el monto entregado debe ser mayor a cero', jsonb_build_object('fn', 'fn_loan_update', 'field', 'delivered')); END IF; v_pct := COALESCE((payload->>'commission_pct')::numeric, v_loan.commission_pct); v_comm := core._payroll_commission_amount(v_delivered, v_pct); v_balance := core.round_money(v_delivered + v_comm); v_weekly := COALESCE((payload->>'weekly_payment')::numeric, core.round_money(v_balance / GREATEST(v_loan.installments_n, 1))); UPDATE loans SET amount = v_delivered, delivered = v_delivered, commission_pct = v_pct, commission_amount = v_comm, balance = v_balance, weekly_payment = core.round_money(v_weekly), note = v_note, plan = COALESCE(NULLIF(btrim(payload->>'plan'), ''), plan), installments_n = COALESCE(NULLIF(payload->>'installments_n', '')::integer, installments_n), first_due = COALESCE(NULLIF(payload->>'first_due', '')::date, first_due) WHERE id = v_id; END IF; UPDATE loans SET balance = core.round_money(delivered + commission_amount - v_paid) WHERE id = v_id; RETURN core.rpc_ok( jsonb_build_object('id', v_id), format('Préstamo id=%s actualizado', v_id), jsonb_build_object('fn', 'fn_loan_update', 'id', v_id, 'payments', v_n) ); EXCEPTION WHEN OTHERS THEN RETURN core.rpc_from_exception('fn_loan_update', SQLSTATE, SQLERRM); END; $$; --changeset panel:core-045b-fn-loan-statement splitStatements:false CREATE OR REPLACE FUNCTION core.fn_loan_statement(payload jsonb) RETURNS jsonb LANGUAGE plpgsql SECURITY INVOKER SET search_path = core AS $$ DECLARE v_id bigint := NULLIF(payload->>'id', '')::bigint; v_loan jsonb; v_payments jsonb; v_paid numeric; BEGIN SELECT to_jsonb(l) INTO v_loan FROM loans l WHERE l.id = v_id; IF v_loan IS NULL THEN RETURN core.rpc_err('NOT_FOUND', 'Préstamo no encontrado', jsonb_build_object('fn', 'fn_loan_statement', 'id', v_id)); END IF; SELECT COALESCE(SUM(amount), 0) INTO v_paid FROM loan_payments WHERE loan_id = v_id; UPDATE loans SET balance = core.round_money( (v_loan->>'delivered')::numeric + (v_loan->>'commission_amount')::numeric - v_paid ) WHERE id = v_id; SELECT to_jsonb(l) INTO v_loan FROM loans l WHERE l.id = v_id; SELECT COALESCE(jsonb_agg(to_jsonb(x) ORDER BY x.created_at, x.id), '[]'::jsonb) INTO v_payments FROM ( SELECT p.id, p.amount, p.label, p.installment_n, p.created_at, wk.week_start, wk.week_end FROM loan_payments p JOIN payroll_weeks wk ON wk.id = p.week_id WHERE p.loan_id = v_id ) x; RETURN core.rpc_ok( jsonb_build_object('loan', v_loan, 'payments', v_payments, 'paid', v_paid), format('Estado de cuenta del préstamo id=%s', v_id), jsonb_build_object('fn', 'fn_loan_statement', 'id', v_id) ); EXCEPTION WHEN OTHERS THEN RETURN core.rpc_from_exception('fn_loan_statement', SQLSTATE, SQLERRM); END; $$; --changeset panel:core-045c-grants endDelimiter:; splitStatements:true GRANT EXECUTE ON FUNCTION core.fn_loan_update(jsonb) TO panels_core_app; GRANT EXECUTE ON FUNCTION core.fn_loan_statement(jsonb) TO panels_core_app;