Initial commit - Horux Despachos NL
This commit is contained in:
169
apps/api/scripts/debug-i07-ppd.ts
Normal file
169
apps/api/scripts/debug-i07-ppd.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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); });
|
||||
Reference in New Issue
Block a user