Update: nueva version Horux Despachos
This commit is contained in:
115
apps/api/scripts/validate-gastos.ts
Normal file
115
apps/api/scripts/validate-gastos.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Compara Gastos del Dashboard vs Drill-down para un mes/contribuyente.
|
||||
* Identifica discrepancias y rompe el detalle por lado (factura/pago/NC).
|
||||
*
|
||||
* Uso: tsx scripts/validate-gastos.ts <tenantRfc> <entidadId> <añoMes>
|
||||
*/
|
||||
import { prisma, tenantDb } from '../src/config/database.js';
|
||||
import { calcularEgresosPorRegimen } from '../src/services/dashboard.service.js';
|
||||
|
||||
const tenantRfcArg = process.argv[2] || 'DESPACHO_MO3NI6U8_B9VGG';
|
||||
const contribuyenteId = process.argv[3] || 'd745a915-6a23-4818-944b-a7e1e18e536a';
|
||||
const yearMonth = process.argv[4] || '2025-02';
|
||||
|
||||
async function main() {
|
||||
const tenant = await prisma.tenant.findFirst({
|
||||
where: { rfc: tenantRfcArg, active: true },
|
||||
select: { id: true, rfc: true, databaseName: true },
|
||||
});
|
||||
if (!tenant) { console.error('Tenant not found'); process.exit(1); }
|
||||
|
||||
const pool = await tenantDb.getPool(tenant.id, tenant.databaseName);
|
||||
|
||||
const [anio, mes] = yearMonth.split('-').map(Number);
|
||||
const lastDay = new Date(anio, mes, 0).getDate();
|
||||
const fi = `${yearMonth}-01`;
|
||||
const ff = `${yearMonth}-${String(lastDay).padStart(2, '0')}`;
|
||||
|
||||
console.log(`\n=== Contribuyente ${contribuyenteId} — ${fi} a ${ff} ===\n`);
|
||||
|
||||
// 1. Dashboard (calcularEgresosPorRegimen)
|
||||
const dashboard = await calcularEgresosPorRegimen(
|
||||
pool, tenant.id, fi, ff, undefined, undefined, false, contribuyenteId,
|
||||
);
|
||||
console.log('DASHBOARD calcularEgresosPorRegimen:');
|
||||
console.log(` total: ${dashboard.total.toFixed(2)}`);
|
||||
for (const r of dashboard.porRegimen) {
|
||||
console.log(` ${r.regimenClave} (${r.regimenDescripcion}): ${r.monto.toFixed(2)}`);
|
||||
}
|
||||
|
||||
// 2. Drill-down query (simulated — bucket=gastos uniforme)
|
||||
const IMP_TRAS = `COALESCE(iva_traslado_mxn,0) + COALESCE(ieps_traslado_mxn,0) + COALESCE(impuestos_locales_trasladado_mxn,0)`;
|
||||
const IMP_TRAS_PAGO = `COALESCE(iva_traslado_pago_mxn,0) + COALESCE(ieps_traslado_pago_mxn,0)`;
|
||||
const EXCL_MONTO = `COALESCE((SELECT SUM(COALESCE(cc.importe_mxn,0) - COALESCE(cc.descuento_mxn,0)) FROM cfdi_conceptos cc WHERE cc.cfdi_id = cfdis.id AND cc.clave_prod_serv IN ('84121603','93161608','85101501','85121800')), 0)`;
|
||||
|
||||
// bucket=gastos: RECIBIDO I PUE + RECIBIDO P + RECIBIDO E PUE (excl 07)
|
||||
// Sumamos tomando en cuenta el signo (E resta)
|
||||
const { rows: drillRows } = await pool.query(
|
||||
`SELECT
|
||||
type, tipo_comprobante, metodo_pago,
|
||||
COALESCE(cfdi_tipo_relacion, '') AS tipo_rel,
|
||||
COUNT(*)::int AS n,
|
||||
SUM(total_mxn) AS total_bruto,
|
||||
SUM(COALESCE(total_mxn,0) - (${IMP_TRAS}) - (${EXCL_MONTO})) AS total_neto,
|
||||
SUM(COALESCE(monto_pago_mxn,0) - (${IMP_TRAS_PAGO})) AS pago_neto
|
||||
FROM cfdis
|
||||
WHERE (
|
||||
(type = 'RECIBIDO' AND tipo_comprobante = 'I' AND metodo_pago = 'PUE'
|
||||
AND COALESCE(cfdi_tipo_relacion, '') <> '07')
|
||||
OR (type = 'RECIBIDO' AND tipo_comprobante = 'P')
|
||||
OR (type = 'RECIBIDO' AND tipo_comprobante = 'E' AND metodo_pago = 'PUE'
|
||||
AND COALESCE(cfdi_tipo_relacion, '') <> '07')
|
||||
)
|
||||
AND regimen_fiscal_receptor IN ('605','606','612','621','625','626','601','603','607','608','610','611','614','615','620','622','623','624')
|
||||
AND status NOT IN ('Cancelado','0')
|
||||
AND ((tipo_comprobante='P' AND fecha_pago_p >= $1::date AND fecha_pago_p < ($2::date + interval '1 day'))
|
||||
OR (tipo_comprobante!='P' AND fecha_emision >= $1::date AND fecha_emision < ($2::date + interval '1 day')))
|
||||
AND contribuyente_id = $3
|
||||
GROUP BY type, tipo_comprobante, metodo_pago, tipo_rel
|
||||
ORDER BY tipo_comprobante, metodo_pago`,
|
||||
[fi, ff, contribuyenteId],
|
||||
);
|
||||
|
||||
console.log(`\nDRILL-DOWN bucket=gastos (filas del drill por bucket):`);
|
||||
let drillSumaFacturas = 0, drillSumaPagos = 0, drillSumaNC = 0;
|
||||
for (const r of drillRows) {
|
||||
const tc = r.tipo_comprobante;
|
||||
const valor = tc === 'P' ? Number(r.pago_neto) : Number(r.total_neto);
|
||||
console.log(` ${r.type} ${tc} ${r.metodo_pago || '-'} rel=${r.tipo_rel || '-'} n=${r.n} total_bruto=${Number(r.total_bruto).toFixed(2)} valor_neto=${valor.toFixed(2)}`);
|
||||
if (tc === 'I') drillSumaFacturas += valor;
|
||||
else if (tc === 'P') drillSumaPagos += valor;
|
||||
else if (tc === 'E') drillSumaNC += valor;
|
||||
}
|
||||
const drillTotal = drillSumaFacturas + drillSumaPagos - drillSumaNC;
|
||||
console.log(` → facturas=${drillSumaFacturas.toFixed(2)} pagos=${drillSumaPagos.toFixed(2)} NC=${drillSumaNC.toFixed(2)}`);
|
||||
console.log(` → drill total = ${drillTotal.toFixed(2)}`);
|
||||
|
||||
// 3. Comparación
|
||||
const delta = dashboard.total - drillTotal;
|
||||
console.log(`\n=== COMPARATIVA ===`);
|
||||
console.log(` Dashboard: ${dashboard.total.toFixed(2)}`);
|
||||
console.log(` Drill-down: ${drillTotal.toFixed(2)}`);
|
||||
console.log(` Delta: ${delta.toFixed(2)}`);
|
||||
|
||||
if (Math.abs(delta) < 0.01) {
|
||||
console.log(` ✓ CUADRAN`);
|
||||
} else {
|
||||
console.log(` ✗ NO CUADRAN — investigar`);
|
||||
}
|
||||
|
||||
// 4. Régimenes del receptor que aparecen vs los ignorados
|
||||
const { rows: regsReceptor } = await pool.query(
|
||||
`SELECT DISTINCT regimen_fiscal_receptor
|
||||
FROM cfdis
|
||||
WHERE contribuyente_id = $1
|
||||
AND type = 'RECIBIDO'
|
||||
AND fecha_emision >= $2::date AND fecha_emision < ($3::date + interval '1 day')
|
||||
ORDER BY regimen_fiscal_receptor`,
|
||||
[contribuyenteId, fi, ff],
|
||||
);
|
||||
console.log(`\nRegímenes en CFDIs RECIBIDOS del periodo:`, regsReceptor.map(r => r.regimen_fiscal_receptor).join(', '));
|
||||
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
main().catch(async e => { console.error(e); await prisma.$disconnect().catch(() => {}); process.exit(1); });
|
||||
Reference in New Issue
Block a user