Update: nueva version Horux Despachos

This commit is contained in:
consultoria-as
2026-04-27 22:09:36 -06:00
commit 6b36db1403
614 changed files with 125926 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
/**
* 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);
});