Files
HoruxDespachos/apps/api/scripts/find-i07-ppd-cases.ts
2026-04-27 22:09:36 -06:00

76 lines
3.5 KiB
TypeScript

/**
* 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); });