155 lines
9.6 KiB
TypeScript
155 lines
9.6 KiB
TypeScript
/**
|
|
* Diseca cómo el CFDI 8ec2eaf3-7879-11f0-81a8-8daae9822b10 (P de pago $295,100,
|
|
* uuid_relacionado → I de activo I03) se compensa en el cálculo de deducciones
|
|
* de Husberto en agosto 2025, con considerarActivos=true vs false.
|
|
*
|
|
* Reproduce las queries reales de calcularEgresosPorRegimen para mostrar
|
|
* el aporte categoría por categoría.
|
|
*/
|
|
import { prisma, tenantDb } from '../src/config/database.js';
|
|
|
|
async function main() {
|
|
const t = await prisma.tenant.findFirst({ where: { rfc: 'DESPACHO_MO3NI6U8_B9VGG' } });
|
|
if (!t) { console.log('Patito tenant not found'); return; }
|
|
const pool = await tenantDb.getPool(t.id, t.databaseName);
|
|
|
|
const RFC = 'TOAH680201RA2';
|
|
const FI = '2025-08-01';
|
|
const FF = '2025-08-31';
|
|
const TARGET_UUID = '8ec2eaf3-7879-11f0-81a8-8daae9822b10';
|
|
|
|
// ───────────────────────────────────────────────────────────────────────────
|
|
// 0) Datos del CFDI target
|
|
// ───────────────────────────────────────────────────────────────────────────
|
|
const { rows: [cfdi] } = await pool.query(`
|
|
SELECT uuid, tipo_comprobante, metodo_pago, forma_pago, uso_cfdi,
|
|
total_mxn, monto_pago_mxn, iva_traslado_pago_mxn, ieps_traslado_pago_mxn,
|
|
uuid_relacionado, regimen_fiscal_receptor, fecha_pago_p
|
|
FROM cfdis WHERE uuid = $1
|
|
`, [TARGET_UUID]);
|
|
|
|
console.log('═══ CFDI target ═══');
|
|
console.log(` UUID: ${cfdi.uuid}`);
|
|
console.log(` Tipo: ${cfdi.tipo_comprobante} (${cfdi.uso_cfdi || '?'})`);
|
|
console.log(` monto_pago_mxn: $${cfdi.monto_pago_mxn}`);
|
|
console.log(` iva_traslado_pago: $${cfdi.iva_traslado_pago_mxn ?? 'null'}`);
|
|
console.log(` ieps_traslado_pago: $${cfdi.ieps_traslado_pago_mxn ?? 'null'}`);
|
|
console.log(` forma_pago: ${cfdi.forma_pago ?? 'NULL'}`);
|
|
console.log(` uuid_relacionado: ${cfdi.uuid_relacionado}`);
|
|
console.log(` fecha_pago_p: ${cfdi.fecha_pago_p}`);
|
|
|
|
// Net pagado según fórmula de deducciones (P)
|
|
const monto = Number(cfdi.monto_pago_mxn || 0);
|
|
const ivaPago = Number(cfdi.iva_traslado_pago_mxn || 0);
|
|
const iepsPago = Number(cfdi.ieps_traslado_pago_mxn || 0);
|
|
const ivaClamped = Math.min(ivaPago, monto * 0.16);
|
|
const netoP = monto - ivaClamped - iepsPago;
|
|
console.log(`\n → Aporte neto a deducciones (formula P): $${netoP.toFixed(2)}`);
|
|
console.log(` monto - LEAST(iva, monto*0.16) - ieps = ${monto} - ${ivaClamped.toFixed(2)} - ${iepsPago}`);
|
|
|
|
// CFDI relacionado
|
|
const { rows: [rel] } = await pool.query(`
|
|
SELECT uuid, tipo_comprobante, metodo_pago, uso_cfdi, total_mxn, cfdi_tipo_relacion
|
|
FROM cfdis WHERE LOWER(uuid) = LOWER($1)
|
|
`, [cfdi.uuid_relacionado]);
|
|
if (rel) {
|
|
console.log(`\n uuid_relacionado apunta a:`);
|
|
console.log(` ${rel.uuid} | ${rel.tipo_comprobante} ${rel.metodo_pago} | uso_cfdi=${rel.uso_cfdi} | total=$${rel.total_mxn}`);
|
|
console.log(` cfdi_tipo_relacion: ${rel.cfdi_tipo_relacion ?? 'null'}`);
|
|
console.log(` ¿es activo? uso_cfdi=${rel.uso_cfdi} → ${['I01','I02','I03','I04','I05','I06','I07','I08'].includes(rel.uso_cfdi) ? '🔴 SÍ' : '🟢 NO'}`);
|
|
}
|
|
|
|
// ───────────────────────────────────────────────────────────────────────────
|
|
// 1) Predicado de filtros
|
|
// ───────────────────────────────────────────────────────────────────────────
|
|
console.log('\n═══ Evaluación de predicados sobre el CFDI target ═══');
|
|
|
|
const ACTIVOS = "('I01','I02','I03','I04','I05','I06','I07','I08')";
|
|
const t1 = await pool.query(`
|
|
SELECT
|
|
(COALESCE(forma_pago, '') = '01' AND COALESCE(monto_pago_mxn, 0) > 2000) AS no_deducible_efectivo,
|
|
(tipo_comprobante = 'P' AND EXISTS (
|
|
SELECT 1 FROM cfdis i_act
|
|
WHERE LOWER(i_act.uuid) = LOWER(cfdis.uuid_relacionado)
|
|
AND i_act.tipo_comprobante = 'I'
|
|
AND i_act.uso_cfdi IN ${ACTIVOS}
|
|
)) AS p_paga_activo
|
|
FROM cfdis WHERE uuid = $1
|
|
`, [TARGET_UUID]);
|
|
console.log(` no_deducible_efectivo (forma_pago=01 AND >2k): ${t1.rows[0].no_deducible_efectivo ? '🔴 TRUE' : '🟢 FALSE'}`);
|
|
console.log(` p_paga_activo (regla activos): ${t1.rows[0].p_paga_activo ? '🔴 TRUE' : '🟢 FALSE'}`);
|
|
|
|
// ───────────────────────────────────────────────────────────────────────────
|
|
// 2) Total de deducciones de Husberto en agosto, con/sin filtro de activos
|
|
// ───────────────────────────────────────────────────────────────────────────
|
|
console.log('\n═══ Suma TOTAL de deducciones (régimen 612 — Husberto, agosto 2025) ═══');
|
|
|
|
const sumar = async (extraSQL: string) => {
|
|
// I PUE
|
|
const { rows: [iPUE] } = await pool.query(`
|
|
SELECT COALESCE(SUM(COALESCE(total_mxn,0) - COALESCE(iva_traslado_mxn,0) - COALESCE(ieps_traslado_mxn,0) - COALESCE(impuestos_locales_trasladado_mxn,0)),0)::numeric(14,2) as monto
|
|
FROM cfdis
|
|
WHERE UPPER(rfc_receptor) = $1 AND tipo_comprobante = 'I' AND metodo_pago = 'PUE'
|
|
AND status NOT IN ('Cancelado','0')
|
|
AND fecha_emision >= $2::date AND fecha_emision < ($3::date + interval '1 day')
|
|
AND NOT (COALESCE(forma_pago,'') = '01' AND COALESCE(total_mxn,0) > 2000)
|
|
${extraSQL}
|
|
`, [RFC, FI, FF]);
|
|
// P
|
|
const { rows: [pCfdis] } = await pool.query(`
|
|
SELECT COALESCE(SUM(COALESCE(monto_pago_mxn,0) - LEAST(COALESCE(iva_traslado_pago_mxn,0), COALESCE(monto_pago_mxn,0)*0.16) - COALESCE(ieps_traslado_pago_mxn,0)),0)::numeric(14,2) as monto
|
|
FROM cfdis
|
|
WHERE UPPER(rfc_receptor) = $1 AND tipo_comprobante = 'P'
|
|
AND status NOT IN ('Cancelado','0')
|
|
AND fecha_pago_p >= $2::date AND fecha_pago_p < ($3::date + interval '1 day')
|
|
AND NOT (COALESCE(forma_pago,'') = '01' AND COALESCE(monto_pago_mxn,0) > 2000)
|
|
${extraSQL}
|
|
`, [RFC, FI, FF]);
|
|
return { iPUE: Number(iPUE.monto), p: Number(pCfdis.monto) };
|
|
};
|
|
|
|
const ACTIVOS_FILTER = `
|
|
AND NOT (tipo_comprobante = 'I' AND uso_cfdi IN ${ACTIVOS})
|
|
AND NOT (tipo_comprobante = 'P' AND EXISTS (
|
|
SELECT 1 FROM cfdis i_act
|
|
WHERE LOWER(i_act.uuid) = LOWER(cfdis.uuid_relacionado)
|
|
AND i_act.tipo_comprobante = 'I'
|
|
AND i_act.uso_cfdi IN ${ACTIVOS}
|
|
))
|
|
AND NOT (tipo_comprobante = 'E' AND cfdis.cfdis_relacionados IS NOT NULL AND EXISTS (
|
|
SELECT 1 FROM cfdis r_act
|
|
WHERE LOWER(r_act.uuid) = ANY(string_to_array(LOWER(cfdis.cfdis_relacionados), '|'))
|
|
AND (
|
|
(r_act.tipo_comprobante = 'I' AND r_act.uso_cfdi IN ${ACTIVOS})
|
|
OR (r_act.tipo_comprobante = 'P' AND EXISTS (
|
|
SELECT 1 FROM cfdis pi_act
|
|
WHERE LOWER(pi_act.uuid) = LOWER(r_act.uuid_relacionado)
|
|
AND pi_act.tipo_comprobante = 'I'
|
|
AND pi_act.uso_cfdi IN ${ACTIVOS}
|
|
))
|
|
)
|
|
))
|
|
`;
|
|
|
|
const ON = await sumar('');
|
|
const OFF = await sumar(ACTIVOS_FILTER);
|
|
|
|
console.log(`\n┌──────────────────┬────────────────┬────────────────┬────────────────┐`);
|
|
console.log(`│ Categoría │ Activos ON │ Activos OFF │ Diferencia │`);
|
|
console.log(`├──────────────────┼────────────────┼────────────────┼────────────────┤`);
|
|
console.log(`│ I PUE recibidas │ $${String(ON.iPUE.toFixed(2)).padStart(13)} │ $${String(OFF.iPUE.toFixed(2)).padStart(13)} │ $${String((ON.iPUE-OFF.iPUE).toFixed(2)).padStart(13)} │`);
|
|
console.log(`│ P recibidos │ $${String(ON.p.toFixed(2)).padStart(13)} │ $${String(OFF.p.toFixed(2)).padStart(13)} │ $${String((ON.p-OFF.p).toFixed(2)).padStart(13)} │`);
|
|
console.log(`├──────────────────┼────────────────┼────────────────┼────────────────┤`);
|
|
const totON = ON.iPUE + ON.p;
|
|
const totOFF = OFF.iPUE + OFF.p;
|
|
console.log(`│ TOTAL deducción │ $${String(totON.toFixed(2)).padStart(13)} │ $${String(totOFF.toFixed(2)).padStart(13)} │ $${String((totON-totOFF).toFixed(2)).padStart(13)} │`);
|
|
console.log(`└──────────────────┴────────────────┴────────────────┴────────────────┘`);
|
|
|
|
console.log(`\n→ El CFDI ${TARGET_UUID.slice(0,8)} aporta $${netoP.toFixed(2)} a "P recibidos" cuando ON, $0 cuando OFF`);
|
|
console.log(` (Su exclusión por activos representa el ${((netoP / (ON.p - OFF.p)) * 100).toFixed(0)}% de la diferencia en P recibidos)`);
|
|
|
|
await prisma.$disconnect();
|
|
}
|
|
|
|
main().catch(e => { console.error(e); process.exit(1); });
|