Files
HoruxDespachosNuevo/apps/api/scripts/validate-dashboard-impuestos.ts

98 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Valida la alineación dashboard ≡ impuestos tras refactor de getResumenIva.
* Para 5 muestras aleatorias por contribuyente, compara:
* dashboard.calcularIvaBalancePorRegimen().total vs
* impuestos.getResumenIva().resultado
*
* Deben coincidir céntimo por céntimo (Resultado = Trasladado Acreditable Retenido,
* usando los mismos 6 buckets del dashboard).
*
* Uso:
* pnpm --filter @horux/api exec tsx scripts/validate-dashboard-impuestos.ts
* METRICAS_BYPASS_CACHE=1 pnpm --filter @horux/api exec tsx scripts/validate-dashboard-impuestos.ts
*/
import { prisma, tenantDb } from '../src/config/database.js';
import * as dashboard from '../src/services/dashboard.service.js';
import { getResumenIva } from '../src/services/impuestos.service.js';
const TOL = 0.01;
function cmp(a: number, b: number): boolean { return Math.abs(a - b) <= TOL; }
function fmt(n: number): string {
return n.toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
async function main() {
console.log('=== Validación dashboard.balance ≡ impuestos.resultado ===');
console.log(` BYPASS_CACHE=${process.env.METRICAS_BYPASS_CACHE === '1' ? 'YES' : 'no'}\n`);
const tenants = await prisma.tenant.findMany({
where: { active: true },
select: { id: true, rfc: true, databaseName: true },
});
let total = 0;
let pass = 0;
let fail = 0;
for (const t of tenants) {
const pool = await tenantDb.getPool(t.id, t.databaseName);
const { rows: contribs } = await pool.query<{ entidad_id: string; nombre: string }>(
`SELECT c.entidad_id, eg.nombre
FROM contribuyentes c
JOIN entidades_gestionadas eg ON eg.id = c.entidad_id
WHERE EXISTS (SELECT 1 FROM metricas_mensuales m WHERE m.contribuyente_id = c.entidad_id)`,
);
if (contribs.length === 0) continue;
console.log(`[${t.rfc}] ${contribs.length} contribuyentes`);
for (const c of contribs) {
const { rows: samples } = await pool.query<{ anio: number; mes: number }>(
`SELECT anio, mes FROM (
SELECT DISTINCT anio, mes FROM metricas_mensuales WHERE contribuyente_id = $1
) t
ORDER BY random() LIMIT 5`,
[c.entidad_id],
);
console.log(` ${c.nombre}:`);
for (const s of samples) {
total++;
const fi = `${s.anio}-${String(s.mes).padStart(2, '0')}-01`;
const lastDay = new Date(s.anio, s.mes, 0).getDate();
const ff = `${s.anio}-${String(s.mes).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`;
const bal = await dashboard.calcularIvaBalancePorRegimen(
pool, t.id, fi, ff, [], undefined, false, c.entidad_id,
);
const resumen = await getResumenIva(pool, fi, ff, t.id, false, c.entidad_id);
const mesLabel = `${s.anio}-${String(s.mes).padStart(2, '0')}`;
if (cmp(bal.total, resumen.resultado)) {
pass++;
console.log(`${mesLabel} balance=$${fmt(bal.total)} resultado=$${fmt(resumen.resultado)}`);
} else {
fail++;
const delta = bal.total - resumen.resultado;
console.log(`${mesLabel} balance=$${fmt(bal.total)} resultado=$${fmt(resumen.resultado)} Δ=$${fmt(delta)}`);
console.log(` T=$${fmt(resumen.trasladado)} A=$${fmt(resumen.acreditable)} R=$${fmt(resumen.retenido)}`);
}
}
}
}
console.log(`\n=== Resumen ===`);
console.log(` Muestras: ${total}`);
console.log(` PASS: ${pass}`);
console.log(` FAIL: ${fail}`);
await prisma.$disconnect();
process.exit(fail > 0 ? 1 : 0);
}
main().catch(async (err) => {
console.error('Fatal:', err);
await prisma.$disconnect().catch(() => {});
process.exit(1);
});