Files
HoruxDespachosNuevo/docs/sessions/2026-05-22-factura-global-contribuyente-fallback.md
Horux Dev 46846200da feat(sat): factura global + fecha_efectiva, fallback tenant-contribuyente, fix anio_global typo
Factura Global & fecha_efectiva:
- Migracion 045_factura_global.sql: periodicidad, meses_global, año_global, fecha_efectiva
- sat-parser.service.ts: extrae InformacionGlobal del XML
- sat.service.ts: calcFechaEfectiva con soporte bimestral (periodicidad 05)
- metricas-compute, dashboard, impuestos, cfdi, export, conciliacion, alertas:
  reemplaza fecha_emision-1h por COALESCE(fecha_efectiva, fecha_emision-1h)
- Script recalc-metricas.ts para recalculo manual

Fallback datos fiscales tenant → contribuyente:
- contribuyente.service.ts: fetchTenantFiscalData + mergeContribuyenteWithTenant
  rellena regimenFiscal, codigoPostal y domicilio cuando el contribuyente
  tiene el mismo RFC que el tenant y sus campos estan vacios
- contribuyente.controller.ts y contribuyente-config.controller.ts:
  pasan req.user!.tenantId al servicio

Fix critico SAT sync:
- sat.service.ts: anio_global → año_global en INSERT/UPDATE de CFDIs
  (la migracion creo 'año_global' con tilde; el codigo usaba 'anio_global',
   causando fallo en 100% de inserciones de CFDI)
- determineChunkMonths: salta sondeo si existe job previo con requestIds
- MAX_POLL_ATTEMPTS: 45 → 500 (~8h) para syncs iniciales grandes

Docs:
- docs/sessions/2026-05-22-factura-global-contribuyente-fallback.md
2026-05-22 15:52:10 +00:00

122 lines
6.3 KiB
Markdown

# 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 `<cfdi:InformacionGlobal>` 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) |