panels-origin/db/core/changesets/045-loan-update.sql
Cursor Agent caf40e29fb
feat: facturas, ajustes de nómina, finiquitos, dispersión e IMSS
Reutiliza el catálogo de proveedores y agrega carga de facturas, conceptos de la semana, préstamos editables, finiquitos, dispersión, historial de personal, reportes y jornal de cotización.

Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
2026-09-24 16:40:08 +00:00

114 lines
4.6 KiB
PL/PgSQL

--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;