/** * Debug ingresos Horux 360 mayo-2025 post-Método A: * - Llama al KPI (calcularIngresosPorRegimen) * - Lista los CFDIs que entran al drill-down (mismos filtros del controller) * - Suma manualmente para ver dónde está la discrepancia */ process.env.METRICAS_BYPASS_CACHE = '1'; import { prisma, tenantDb } from '../src/config/database.js'; import { calcularIngresosPorRegimen, GRUPO_PF_EMPRESARIAL, GRUPO_PM_OTROS } from '../src/services/dashboard.service.js'; import { resolveContribuyenteContext } from '../src/utils/contribuyente-context.js'; const TENANT_RFC = 'DESPACHO_MO3NI6U8_B9VGG'; const CONTRIB_ID = 'b3761db6-0b8d-4251-8078-4ddc31e9c75b'; // Horux 360 const FI = '2025-05-01'; const FF = '2025-05-31'; async function main() { const tenant = await prisma.tenant.findFirst({ where: { rfc: TENANT_RFC }, select: { id: true, databaseName: true }, }); if (!tenant) return; const pool = await tenantDb.getPool(tenant.id, tenant.databaseName); const ctx = await resolveContribuyenteContext(pool, tenant.id, CONTRIB_ID); console.log(`\n=== KPI calcularIngresosPorRegimen ===`); const kpi = await calcularIngresosPorRegimen( pool, tenant.id, FI, FF, undefined, undefined, false, CONTRIB_ID, ); console.log(`Total KPI: ${kpi.total.toFixed(2)}`); for (const r of kpi.porRegimen) { console.log(` ${r.regimenClave} ${r.regimenDescripcion.substring(0, 40).padEnd(40)} ${r.monto.toFixed(2)}`); } // Replica de los filtros del drill-down bucket 'ingresos' (cfdi.controller.ts:163-187) 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 VIGENTE = `status NOT IN ('Cancelado', '0')`; const CLAVES = `('84121603','93161608','85101501','85121800')`; 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 ${CLAVES}),0)`; const g1 = GRUPO_PF_EMPRESARIAL.map(r => `'${r}'`).join(','); const g3 = GRUPO_PM_OTROS.map(r => `'${r}'`).join(','); const drillSql = ` SELECT id, uuid, type, tipo_comprobante, metodo_pago, regimen_fiscal_emisor, regimen_fiscal_receptor, rfc_emisor, rfc_receptor, nombre_receptor, total_mxn, iva_traslado_mxn, ieps_traslado_mxn, impuestos_locales_trasladado_mxn, monto_pago_mxn, iva_traslado_pago_mxn, ieps_traslado_pago_mxn, cfdi_tipo_relacion, fecha_emision, fecha_pago_p, source, -- neto (lo que "contribuye" a ingresos según grupo) CASE WHEN tipo_comprobante='I' THEN (COALESCE(total_mxn,0) - (${IMP_TRAS}) - (${EXCL_MONTO})) WHEN tipo_comprobante='E' THEN -(COALESCE(total_mxn,0) - (${IMP_TRAS}) - (${EXCL_MONTO})) WHEN tipo_comprobante='P' THEN (COALESCE(monto_pago_mxn,0) - (${IMP_TRAS_PAGO})) WHEN tipo_comprobante='N' THEN COALESCE(total_mxn,0) ELSE 0 END AS aporte FROM cfdis WHERE ${VIGENTE} AND fecha_emision >= $1::date AND fecha_emision < ($2::date + interval '1 day') AND ( (${ctx.esEmisor} AND regimen_fiscal_emisor IN (${g1}) AND ( (tipo_comprobante='I' AND metodo_pago='PUE') OR (tipo_comprobante='P') OR (tipo_comprobante='E' AND metodo_pago='PUE') )) OR (${ctx.esReceptor} AND tipo_comprobante='N' AND metodo_pago='PUE' AND regimen_fiscal_receptor='605') OR (${ctx.esEmisor} AND regimen_fiscal_emisor IN (${g3}) AND ( (tipo_comprobante='I' AND metodo_pago IN ('PUE','PPD')) OR (tipo_comprobante='E' AND metodo_pago='PUE') )) ) ORDER BY fecha_emision, tipo_comprobante, total_mxn DESC `; const { rows } = await pool.query(drillSql, [FI, FF]); console.log(`\n=== Drill-down (${rows.length} CFDIs) ===`); let sumDrill = 0; const perRegimen: Record = {}; for (const r of rows) { const aporte = Number(r.aporte || 0); sumDrill += aporte; const reg = r.regimen_fiscal_emisor || r.regimen_fiscal_receptor || '?'; perRegimen[reg] = (perRegimen[reg] || 0) + aporte; const fe = r.fecha_emision?.toISOString?.()?.slice(0, 10) || r.fecha_emision; const rel07 = r.cfdi_tipo_relacion === '07' ? ' [07]' : ''; const src = r.source === 'facturapi' ? ' [facturapi]' : ''; console.log( ` ${fe} ${r.tipo_comprobante}/${r.metodo_pago}${rel07}${src} ` + `reg=${reg} ${String(r.rfc_emisor).padEnd(14)}→${String(r.rfc_receptor).padEnd(14)} ` + `total=${Number(r.total_mxn || 0).toFixed(2).padStart(10)} ` + `aporte=${aporte.toFixed(2).padStart(10)} ${r.uuid.substring(0,8)}` ); } console.log(`\n=== Suma de aportes del drill-down: ${sumDrill.toFixed(2)} ===`); console.log(`Por régimen (drill-down):`); for (const [reg, monto] of Object.entries(perRegimen).sort()) { console.log(` ${reg}: ${monto.toFixed(2)}`); } console.log(`\n=== Diferencia KPI − drill: ${(kpi.total - sumDrill).toFixed(2)} ===`); await prisma.$disconnect(); } main().catch(async e => { console.error(e); await prisma.$disconnect().catch(() => {}); process.exit(1); });