# Sesión de cambios: 2026-05-22 ## Resumen Tres líneas de trabajo: (1) implementación completa de facturas globales (`InformacionGlobal`) con `fecha_efectiva`, (2) fallback robusto de datos fiscales del tenant a contribuyentes con RFC coincidente, y (3) corrección crítica de typo `anio_global` → `año_global` en sincronización SAT. --- ## 1. Facturas Globales — `InformacionGlobal` y `fecha_efectiva` ### Contexto Las facturas globales del SAT usan el nodo `` que indica la periodicidad, meses y año al que realmente corresponden los ingresos. Antes del cambio, todos los CFDIs se agrupaban por `fecha_emision`, lo que desplazaba facturas globales emitidas al cierre de un período (ej. 31 de marzo) al mes equivocado. ### Cambios **Base de datos** - Nueva migración `apps/api/src/migrations/tenant/045_factura_global.sql`: - `periodicidad VARCHAR(2)` - `meses_global VARCHAR(10)` - `año_global VARCHAR(4)` - `fecha_efectiva DATE` + índice `idx_cfdis_fecha_efectiva` - Aplicada a todos los tenants activos vía script de migración. **Parser SAT** - `apps/api/src/services/sat/sat-parser.service.ts` - `CfdiParsed` ahora incluye `periodicidad`, `mesesGlobal`, `añoGlobal`. - Extraídos del XML desde `comprobante.InformacionGlobal`. **Cálculo de `fecha_efectiva`** - `apps/api/src/services/sat/sat.service.ts` - `calcFechaEfectiva(cfdi)`: devuelve `new Date(año, mes-1, 1)` para facturas globales. - Soporta periodicidad bimestral (`05`): códigos `13-18` → meses `2,4,6,8,10,12`. **Queries de métricas/reportes** Reemplazado `fecha_emision - interval '1 hour'` por `COALESCE(fecha_efectiva, fecha_emision - interval '1 hour')` en: - `metricas-compute.service.ts` (counts, min_anio, monthly compute) - `reportes.service.ts` (flujo efectivo, comparativos) - `dashboard.service.ts` (KPIs, neteo PPD/07) - `impuestos.service.ts` (IVA mensual) - `alertas-auto.service.ts` (alertas RESICO y régimen desconocido) - `cfdi.service.ts` (list filters, `getResumenCfdis`) - `export.service.ts` - `conciliacion.service.ts` - `alertas.controller.ts` **Backfill** - Ejecutado en todos los tenants activos: - `horux_hts240708lja`: 24 registros - `horux_roem691011ez4`: 2,238 registros - `horux_auza640701ti9`: 6 registros - `horux_momc8311199va`: 14 registros - Métricas recalculadas para TORC9611214CA (enero-marzo 2026). --- ## 2. Fallback de datos fiscales del tenant al contribuyente ### Problema El tenant HORUX 360 (`HTS240708LJA`) tiene su régimen fiscal y domicilio en la base central (`tenants`), pero al existir como contribuyente dentro de su propia BD (`contribuyentes`), esos campos estaban vacíos. El frontend mostraba "Sin régimen" y "Sin domicilio" al seleccionar ese contribuyente. ### Solución robusta Cuando un contribuyente tiene el mismo RFC que su tenant, el backend ahora mezcla automáticamente los datos faltantes desde la base central. **Archivos** - `apps/api/src/services/contribuyente.service.ts` - `fetchTenantFiscalData(tenantId)`: consulta `tenants` + `tenant_regimenes_activos` para obtener régimen (CSV de claves), CP y domicilio JSON. - `mergeContribuyenteWithTenant()`: rellena `regimenFiscal`, `codigoPostal` y `domicilio` si están vacíos en el contribuyente. - `listContribuyentes()` y `getContribuyenteById()` aceptan `tenantId` opcional. - `apps/api/src/controllers/contribuyente.controller.ts` - Pasa `req.user!.tenantId` a `listContribuyentes` y `getContribuyenteById`. - `apps/api/src/controllers/contribuyente-config.controller.ts` - Pasa `req.user!.tenantId` a `getContribuyenteById` en `uploadFiel` y `createOrg`. --- ## 3. Fix crítico: `anio_global` → `año_global` ### Problema La migración `045_factura_global.sql` creó la columna `año_global` (con tilde), pero `sat.service.ts` usaba `anio_global` (sin tilde) en las queries `INSERT`/`UPDATE` de `saveCfdis`. Esto causaba que **cada inserción de CFDI fallara** con: ``` column "anio_global" of relation "cfdis" does not exist ``` Esto explicaba por qué el sync inicial del tenant DESPACHO_MPG95QP7_XZVFF insertó solo **174 de 8,284 CFDIs** descargados. ### Fix - `apps/api/src/services/sat/sat.service.ts` - Líneas 297 y 347: `anio_global` → `año_global`. ### Optimización adicional en SAT sync - `determineChunkMonths()`: ahora detecta si existe un job previo completado con `satRequestIds` y salta el sondeo `metadata` lento, reutilizando directamente el tamaño de chunk (3 o 6 meses). - `MAX_POLL_ATTEMPTS`: aumentado de 45 a 500 (~8 horas) para syncs iniciales grandes donde el SAT tarda horas en preparar paquetes. ### Re-sync validado Re-lanzado el sync inicial para DESPACHO_MPG95QP7_XZVFF tras el fix: - **Found:** 8,284 | **Downloaded:** 7,781 | **Inserted:** 8,266 - Duración: ~7 minutos (vs. ~3.5 horas del intento anterior con el bug). --- ## Archivos modificados | Archivo | Cambio | |---|---| | `apps/api/src/migrations/tenant/045_factura_global.sql` | Nueva migración (untracked → added) | | `apps/api/src/services/sat/sat-parser.service.ts` | Extrae `periodicidad`, `mesesGlobal`, `añoGlobal` | | `apps/api/src/services/sat/sat.service.ts` | `calcFechaEfectiva`, fix `año_global`, `determineChunkMonths` optimizado, `MAX_POLL_ATTEMPTS` | | `apps/api/src/services/metricas-compute.service.ts` | Usa `COALESCE(fecha_efectiva, ...)` | | `apps/api/src/services/dashboard.service.ts` | Usa `fecha_efectiva` en KPIs y neteo | | `apps/api/src/services/impuestos.service.ts` | Usa `fecha_efectiva` en IVA mensual | | `apps/api/src/services/cfdi.service.ts` | Filtros y resumen por `fecha_efectiva` | | `apps/api/src/services/export.service.ts` | Usa `fecha_efectiva` | | `apps/api/src/services/conciliacion.service.ts` | Usa `fecha_efectiva` | | `apps/api/src/services/alertas-auto.service.ts` | Usa `fecha_efectiva` en alertas | | `apps/api/src/controllers/alertas.controller.ts` | Usa `fecha_efectiva` en queries de alertas | | `apps/api/src/services/contribuyente.service.ts` | Fallback de datos fiscales del tenant | | `apps/api/src/controllers/contribuyente.controller.ts` | Pasa `tenantId` al servicio | | `apps/api/src/controllers/contribuyente-config.controller.ts` | Pasa `tenantId` al servicio | | `apps/api/src/scripts/recalc-metricas.ts` | Script de recálculo manual (untracked → added) | | `apps/web/...` | Múltiples ajustes frontend relacionados (fechas, alertas, drill-down) |