mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 11:23:18 +00:00
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Resumen Implementa las observaciones de facturas, nómina e IMSS sobre el catálogo de proveedores y la nómina semanal que ya existían. ## Qué queda disponible - Proveedores: régimen fiscal, código postal, estatus activo/inactivo y filtro por nombre o RFC. - Facturas recibidas y emitidas: Excel, UUID, revisión si el RFC no está en el catálogo, clasificación a obra/concepto y gasto `source = invoice` solo para la recibida clasificada. - Nómina: bono, descuento con observación, horas y día festivo dentro del armado semanal. La semana pagada no acepta cambios. - Préstamos: edición (libre sin pagos; con pagos solo nota y cuota) y estado de cuenta con saldo = entregado + comisión − pagos. - Finiquitos y liquidaciones: conceptos, neto descontando descuentos vigentes y saldo de préstamo, y gasto al pagar. - Dispersión: corte semanal, corrección antes de definitiva y comprobante. - Historial de alta, baja y reingreso en la ficha, más movimientos IMSS. - Reporte por periodo en Excel y PDF. - Jornal IMSS distinto del jornal real y consulta de cuotas exportable a Excel. ## Verificación `deno check` de la API pasó. En este entorno no hay Postgres, así que `api/pendientes_test.ts` queda listo para correr cuando la base tenga las migraciones 042–050 y el permiso IAM 007. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-5e23dc3b-921e-4091-9650-7091d3a52bbf?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-5e23dc3b-921e-4091-9650-7091d3a52bbf&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
114 lines
4.6 KiB
PL/PgSQL
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;
|