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