mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 14:03:16 +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>
81 lines
3.5 KiB
PL/PgSQL
81 lines
3.5 KiB
PL/PgSQL
--liquibase formatted sql
|
|
-- PANELS · core · reporte de nómina por periodo
|
|
|
|
--changeset panel:core-049a-fn-payroll-report splitStatements:false
|
|
CREATE OR REPLACE FUNCTION core.fn_payroll_report(payload jsonb)
|
|
RETURNS jsonb
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = core
|
|
AS $$
|
|
DECLARE
|
|
v_tid integer := core._payroll_tenant_id(payload);
|
|
v_from date := NULLIF(payload->>'date_from', '')::date;
|
|
v_to date := NULLIF(payload->>'date_to', '')::date;
|
|
v_worker_id bigint := NULLIF(payload->>'worker_id', '')::bigint;
|
|
v_project_id bigint := NULLIF(payload->>'project_id', '')::bigint;
|
|
v_lines jsonb;
|
|
v_adjustments jsonb;
|
|
v_loans jsonb;
|
|
v_settlements jsonb;
|
|
v_total numeric;
|
|
BEGIN
|
|
IF v_from IS NULL OR v_to IS NULL THEN
|
|
RETURN core.rpc_err('VALIDATION', 'fn_payroll_report: date_from y date_to son obligatorios',
|
|
jsonb_build_object('fn', 'fn_payroll_report'));
|
|
END IF;
|
|
SELECT COALESCE(jsonb_agg(to_jsonb(x) ORDER BY x.week_start, x.last_name_p), '[]'::jsonb),
|
|
COALESCE(SUM(x.payable_net), 0)
|
|
INTO v_lines, v_total
|
|
FROM (
|
|
SELECT l.id, l.worker_id, w.first_name, w.last_name_p, s.kind, s.project_id, p.name AS project_name,
|
|
wk.week_start, wk.week_end, l.days, l.daily_wage, l.gross, l.discounts, l.loan_discount, l.payable_net
|
|
FROM payroll_week_lines l
|
|
JOIN payroll_sheets s ON s.id = l.sheet_id
|
|
JOIN payroll_weeks wk ON wk.id = s.week_id
|
|
JOIN workers w ON w.id = l.worker_id
|
|
LEFT JOIN projects p ON p.id = COALESCE(s.project_id, l.project_id)
|
|
WHERE wk.tenant_id = v_tid
|
|
AND wk.week_start BETWEEN v_from AND v_to
|
|
AND (v_worker_id IS NULL OR l.worker_id = v_worker_id)
|
|
AND (v_project_id IS NULL OR s.project_id = v_project_id OR l.project_id = v_project_id)
|
|
) x;
|
|
SELECT COALESCE(jsonb_agg(to_jsonb(a) ORDER BY a.adjustment_date), '[]'::jsonb) INTO v_adjustments
|
|
FROM payroll_adjustments a
|
|
JOIN payroll_weeks wk ON wk.id = a.week_id
|
|
WHERE a.tenant_id = v_tid
|
|
AND wk.week_start BETWEEN v_from AND v_to
|
|
AND (v_worker_id IS NULL OR a.worker_id = v_worker_id)
|
|
AND (v_project_id IS NULL OR a.project_id = v_project_id);
|
|
SELECT COALESCE(jsonb_agg(to_jsonb(p) ORDER BY p.created_at), '[]'::jsonb) INTO v_loans
|
|
FROM (
|
|
SELECT lp.amount, lp.label, lp.loan_id, l.worker_id, wk.week_start
|
|
FROM loan_payments lp
|
|
JOIN loans l ON l.id = lp.loan_id
|
|
JOIN payroll_weeks wk ON wk.id = lp.week_id
|
|
WHERE wk.tenant_id = v_tid
|
|
AND wk.week_start BETWEEN v_from AND v_to
|
|
AND (v_worker_id IS NULL OR l.worker_id = v_worker_id)
|
|
) p;
|
|
SELECT COALESCE(jsonb_agg(to_jsonb(s) ORDER BY s.termination_date), '[]'::jsonb) INTO v_settlements
|
|
FROM settlements s
|
|
WHERE s.tenant_id = v_tid
|
|
AND s.status = 'pagado'
|
|
AND s.termination_date BETWEEN v_from AND v_to
|
|
AND (v_worker_id IS NULL OR s.worker_id = v_worker_id)
|
|
AND (v_project_id IS NULL OR s.project_id = v_project_id);
|
|
RETURN core.rpc_ok(
|
|
jsonb_build_object(
|
|
'lines', v_lines, 'adjustments', v_adjustments, 'loan_payments', v_loans,
|
|
'settlements', v_settlements, 'payable_total', core.round_money(v_total)
|
|
),
|
|
format('Reporte de nómina %s a %s', v_from, v_to),
|
|
jsonb_build_object('fn', 'fn_payroll_report', 'date_from', v_from, 'date_to', v_to)
|
|
);
|
|
EXCEPTION WHEN OTHERS THEN
|
|
RETURN core.rpc_from_exception('fn_payroll_report', SQLSTATE, SQLERRM);
|
|
END;
|
|
$$;
|
|
|
|
--changeset panel:core-049b-grants endDelimiter:; splitStatements:true
|
|
GRANT EXECUTE ON FUNCTION core.fn_payroll_report(jsonb) TO panels_core_app;
|