/** * Encuentra E que referencien directamente a una I/07 PPD vía * `cfdis_relacionados`. Patrón real observado: la E "ajusta" la I/07 PPD, * no al anticipo original. La I/07 PPD apunta al anticipo, la E apunta a * la I/07 PPD. */ import { prisma, tenantDb } from '../src/config/database.js'; const TARGET_RFC = process.argv[2]; async function main() { const tenants = await prisma.tenant.findMany({ select: { id: true, rfc: true, databaseName: true } }); for (const t of tenants) { let pool; try { pool = await tenantDb.getPool(t.id, t.databaseName); } catch { continue; } console.log(`\n=== ${t.rfc}${TARGET_RFC ? ` (RFC=${TARGET_RFC})` : ''} ===`); const rfcFilter = TARGET_RFC ? `AND (UPPER(i.rfc_emisor) = UPPER('${TARGET_RFC}') OR UPPER(i.rfc_receptor) = UPPER('${TARGET_RFC}'))` : ''; const { rows } = await pool.query(` SELECT i.uuid AS i_uuid, i.fecha_emision AS i_fecha, i.total_mxn AS i_total, i.iva_traslado_mxn AS i_iva, i.rfc_emisor AS i_emisor, i.rfc_receptor AS i_receptor, i.type AS i_type, e.uuid AS e_uuid, e.cfdi_tipo_relacion AS e_rel, e.metodo_pago AS e_mp, e.fecha_emision AS e_fecha, e.total_mxn AS e_total, e.iva_traslado_mxn AS e_iva, ABS(EXTRACT(EPOCH FROM (e.fecha_emision - i.fecha_emision)) / 86400)::int AS diff_dias, EXTRACT(YEAR FROM i.fecha_emision)::int * 12 + EXTRACT(MONTH FROM i.fecha_emision)::int AS i_periodo, EXTRACT(YEAR FROM e.fecha_emision)::int * 12 + EXTRACT(MONTH FROM e.fecha_emision)::int AS e_periodo FROM cfdis i JOIN cfdis e ON LOWER(i.uuid) = ANY(string_to_array(LOWER(e.cfdis_relacionados), '|')) WHERE i.cfdi_tipo_relacion = '07' AND i.tipo_comprobante = 'I' AND i.metodo_pago = 'PPD' AND i.status NOT IN ('Cancelado','0') AND e.tipo_comprobante = 'E' AND e.status NOT IN ('Cancelado','0') ${rfcFilter} ORDER BY i.fecha_emision DESC `); console.log(`Total pares: ${rows.length}`); const buckets = { mismoMes: 0, eDespues1: 0, eDespuesMas: 0, eAntes: 0 }; for (const r of rows) { const diff = Number(r.e_periodo) - Number(r.i_periodo); if (diff < 0) buckets.eAntes++; else if (diff === 0) buckets.mismoMes++; else if (diff === 1) buckets.eDespues1++; else buckets.eDespuesMas++; } console.log(` Mismo mes: ${buckets.mismoMes}`); console.log(` E 1 mes después: ${buckets.eDespues1}`); console.log(` E ≥2 meses después: ${buckets.eDespuesMas}`); console.log(` E antes: ${buckets.eAntes}`); if (rows.length > 0) { console.log(`\n Detalle (top ${Math.min(rows.length, 10)}):`); for (const r of rows.slice(0, 10)) { const fi = new Date(r.i_fecha).toISOString().slice(0, 10); const fe = new Date(r.e_fecha).toISOString().slice(0, 10); const i_base = Number(r.i_total) - Number(r.i_iva || 0); const e_base = Number(r.e_total) - Number(r.e_iva || 0); const diff = Number(r.e_periodo) - Number(r.i_periodo); console.log(` I/07 PPD ${r.i_uuid.substring(0,8)} ${fi} base=${i_base.toFixed(2)} ${r.i_emisor}→${r.i_receptor} (${r.i_type})`); console.log(` E/${r.e_rel ?? 'null'}/${r.e_mp || '?'} ${r.e_uuid.substring(0,8)} ${fe} base=${e_base.toFixed(2)} diffMeses=${diff} (${r.diff_dias}d)`); } } } await prisma.$disconnect(); } main().catch(async e => { console.error(e); await prisma.$disconnect().catch(() => {}); process.exit(1); });