/** * Diseca cómo se compensa la I PPD con cfdi_tipo_relacion='07' (aplicación * de anticipo) en el cálculo de deducciones, evaluando: * - Si NO entra al sumatorio normal (I PUE / P) por ser PPD * - Si entra a la compensación I/07 PPD ↔ E del mismo mes * - Si tiene cfdis_relacionados (qué referencia hacia atrás) * - Si es referenciada por algún CFDI hacia adelante (P, E, otra I) * - Cómo afecta con considerarActivos ON vs OFF */ 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 TARGET = '5c874749-748f-11f0-96b1-2b9310891836'; const RFC = 'TOAH680201RA2'; // ─────────────────────────────────────────────────────────────────────────── // 0) Datos del CFDI // ─────────────────────────────────────────────────────────────────────────── const { rows: [c] } = await pool.query(` SELECT uuid, type, tipo_comprobante, metodo_pago, forma_pago, uso_cfdi, cfdi_tipo_relacion, cfdis_relacionados, total_mxn, iva_traslado_mxn, ieps_traslado_mxn, rfc_emisor, nombre_emisor, regimen_fiscal_emisor, rfc_receptor, nombre_receptor, regimen_fiscal_receptor, fecha_emision, fecha_pago_p, status, saldo_pendiente_mxn FROM cfdis WHERE LOWER(uuid) = LOWER($1) `, [TARGET]); console.log('═══ CFDI ═══'); for (const [k, v] of Object.entries(c)) { console.log(` ${k.padEnd(28)} ${v}`); } // ─────────────────────────────────────────────────────────────────────────── // 1) ¿Hace referencia hacia atrás (vía cfdis_relacionados)? // ─────────────────────────────────────────────────────────────────────────── console.log('\n═══ Referencias hacia atrás (cfdis_relacionados) ═══'); if (!c.cfdis_relacionados) { console.log(' (ninguna — cfdis_relacionados es NULL)'); } else { const uuids = String(c.cfdis_relacionados).split('|').map((s: string) => s.trim()).filter(Boolean); for (const u of uuids) { const { rows: [rel] } = await pool.query(` SELECT uuid, tipo_comprobante, metodo_pago, total_mxn, fecha_emision, cfdi_tipo_relacion FROM cfdis WHERE LOWER(uuid) = LOWER($1) `, [u]); console.log(` ${u} →`, rel ?? '(no encontrado)'); } } // ─────────────────────────────────────────────────────────────────────────── // 2) ¿Es referenciada hacia adelante? (P que la pague, E que la cancele, otra I tipo_relacion=07 que sustituya) // ─────────────────────────────────────────────────────────────────────────── console.log('\n═══ CFDIs que referencian a este (hacia adelante) ═══'); // P que la pagan vía uuid_relacionado const { rows: pagos } = await pool.query(` SELECT uuid, monto_pago_mxn, fecha_pago_p FROM cfdis WHERE tipo_comprobante = 'P' AND LOWER(uuid_relacionado) = LOWER($1) AND status NOT IN ('Cancelado','0') ORDER BY fecha_pago_p `, [TARGET]); console.log(` P que la pagan (${pagos.length}):`); let totalPagado = 0; for (const p of pagos) { totalPagado += Number(p.monto_pago_mxn || 0); console.log(` ${p.uuid} | $${p.monto_pago_mxn} | ${p.fecha_pago_p}`); } console.log(` → Total pagado vía P: $${totalPagado.toLocaleString('es-MX')}`); console.log(` Total CFDI original: $${c.total_mxn}`); console.log(` Saldo pendiente: $${c.saldo_pendiente_mxn ?? '?'}`); // E que la cancelan vía cfdis_relacionados const { rows: ecanc } = await pool.query(` SELECT uuid, tipo_comprobante, metodo_pago, total_mxn, cfdi_tipo_relacion, fecha_emision FROM cfdis WHERE tipo_comprobante = 'E' AND cfdis_relacionados IS NOT NULL AND LOWER($1) = ANY(string_to_array(LOWER(cfdis_relacionados), '|')) AND status NOT IN ('Cancelado','0') `, [TARGET]); console.log(`\n E que la referencian (${ecanc.length}):`); for (const e of ecanc) { console.log(` ${e.uuid} | total=$${e.total_mxn} | tipo_rel=${e.cfdi_tipo_relacion} | ${e.fecha_emision}`); } // ─────────────────────────────────────────────────────────────────────────── // 3) Compensación I/07 PPD ↔ E lado RECEPTOR (mismo mes) // ─────────────────────────────────────────────────────────────────────────── console.log('\n═══ ¿Entra en compensación I/07 PPD ↔ E (mes/año del CFDI)? ═══'); const fecha = new Date(c.fecha_emision); const mesAnio = `${fecha.getFullYear()}-${String(fecha.getMonth() + 1).padStart(2, '0')}`; console.log(` CFDI mes/año: ${mesAnio}`); console.log(` cfdi_tipo_relacion='07': ${c.cfdi_tipo_relacion === '07' ? '✓ SÍ' : '✗ NO'}`); console.log(` metodo_pago='PPD': ${c.metodo_pago === 'PPD' ? '✓ SÍ' : '✗ NO'}`); if (c.cfdi_tipo_relacion === '07' && c.metodo_pago === 'PPD') { // Calcular el aporte que tendría a la compensación (suma de E del mismo mes) const { rows: comp } = await pool.query(` SELECT COALESCE(SUM( COALESCE(e.total_mxn, 0) - COALESCE(e.iva_traslado_mxn, 0) - COALESCE(e.ieps_traslado_mxn, 0) - COALESCE(e.impuestos_locales_trasladado_mxn, 0) ), 0)::numeric(14,2) AS aporte FROM cfdis e WHERE e.tipo_comprobante = 'E' AND e.metodo_pago = 'PUE' AND e.status NOT IN ('Cancelado','0') AND UPPER(e.rfc_receptor) = $1 AND LOWER($2) = ANY(string_to_array(LOWER(e.cfdis_relacionados), '|')) AND date_trunc('month', e.fecha_emision) = date_trunc('month', $3::timestamp) `, [RFC, TARGET, c.fecha_emision]); console.log(`\n Aporte a la compensación (suma E mismo mes): $${comp[0].aporte}`); if (Number(comp[0].aporte) > 0) { console.log(` → SÍ entra en compensación`); } else { console.log(` → NO entra (no hay E en mismo mes que la referencien)`); } } // ─────────────────────────────────────────────────────────────────────────── // 4) ¿Aparece en el cálculo "I PUE recibidas" o "P recibidos"? // ─────────────────────────────────────────────────────────────────────────── console.log('\n═══ ¿Aparece en el cálculo directo? ═══'); console.log(` I PUE recibidas requiere: tipo_comprobante='I' AND metodo_pago='PUE'`); console.log(` Este CFDI: tipo_comprobante='${c.tipo_comprobante}', metodo_pago='${c.metodo_pago}'`); console.log(` → ${c.tipo_comprobante === 'I' && c.metodo_pago === 'PUE' ? '✓ SÍ entra' : '✗ NO entra'} (es ${c.tipo_comprobante} ${c.metodo_pago})`); // ─────────────────────────────────────────────────────────────────────────── // 5) Predicado de filtro de activos // ─────────────────────────────────────────────────────────────────────────── console.log('\n═══ Predicado de filtro de activos sobre este CFDI ═══'); const ACTIVOS = "('I01','I02','I03','I04','I05','I06','I07','I08')"; const t1 = await pool.query(` SELECT (tipo_comprobante = 'I' AND uso_cfdi IN ${ACTIVOS}) AS regla1_directo, (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 regla2, (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}) )) AS regla3 FROM cfdis WHERE LOWER(uuid) = LOWER($1) `, [TARGET]); console.log(` regla1 (I directo activo): ${t1.rows[0].regla1_directo ? '🔴 TRUE' : '🟢 FALSE'}`); console.log(` regla2 (P paga I activo): ${t1.rows[0].regla2 ? '🔴 TRUE' : '🟢 FALSE'}`); console.log(` regla3 (E ref. I/P activo): ${t1.rows[0].regla3 ? '🔴 TRUE' : '🟢 FALSE'}`); const filtrado = t1.rows[0].regla1_directo || t1.rows[0].regla2 || t1.rows[0].regla3; console.log(` → Si "Considerar activos" OFF → ${filtrado ? '🔴 EXCLUIDO' : '🟢 PASA'}`); await prisma.$disconnect(); } main().catch(e => { console.error(e); process.exit(1); });