Files
Horux360/docs/SAT-SYNC-IMPLEMENTATION.md
Consultoria AS ba012254db docs: add current state and next steps for SAT sync
- Document current implementation status
- Add pending items to verify after SAT rate limit resets
- Include test tenant info and verification commands
- List known issues and workarounds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 04:38:43 +00:00

8.9 KiB

Implementación de Sincronización SAT

Resumen

Sistema de sincronización automática de CFDIs con el SAT (Servicio de Administración Tributaria de México) para Horux360.

Componentes Implementados

1. Backend (API)

Servicios

Archivo Descripción
src/services/fiel.service.ts Gestión de credenciales FIEL (e.firma)
src/services/sat/sat-client.service.ts Cliente para el servicio web del SAT
src/services/sat/sat.service.ts Lógica principal de sincronización
src/services/sat/sat-crypto.service.ts Encriptación AES-256-GCM para credenciales
src/services/sat/sat-parser.service.ts Parser de XMLs de CFDI

Controladores

Archivo Descripción
src/controllers/fiel.controller.ts Endpoints para gestión de FIEL
src/controllers/sat.controller.ts Endpoints para sincronización SAT

Job Programado

Archivo Descripción
src/jobs/sat-sync.job.ts Cron job para sincronización diaria (3:00 AM)

2. Frontend (Web)

Componentes

Archivo Descripción
components/sat/FielUploadModal.tsx Modal para subir certificado y llave FIEL
components/sat/SyncStatus.tsx Estado de sincronización con selector de fechas
components/sat/SyncHistory.tsx Historial de sincronizaciones

Página

Archivo Descripción
app/(dashboard)/configuracion/sat/page.tsx Página de configuración SAT

3. Base de Datos

Tabla Principal (schema public)

-- sat_sync_jobs: Almacena los trabajos de sincronización
CREATE TABLE sat_sync_jobs (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  type VARCHAR(20) NOT NULL,  -- 'initial' | 'daily'
  status VARCHAR(20) NOT NULL, -- 'pending' | 'running' | 'completed' | 'failed'
  date_from TIMESTAMP NOT NULL,
  date_to TIMESTAMP NOT NULL,
  cfdi_type VARCHAR(20),
  sat_request_id VARCHAR(100),
  sat_package_ids TEXT[],
  cfdis_found INTEGER DEFAULT 0,
  cfdis_downloaded INTEGER DEFAULT 0,
  cfdis_inserted INTEGER DEFAULT 0,
  cfdis_updated INTEGER DEFAULT 0,
  progress_percent INTEGER DEFAULT 0,
  error_message TEXT,
  started_at TIMESTAMP,
  completed_at TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW(),
  retry_count INTEGER DEFAULT 0
);

-- fiel_credentials: Almacena las credenciales FIEL encriptadas
CREATE TABLE fiel_credentials (
  id UUID PRIMARY KEY,
  tenant_id UUID UNIQUE NOT NULL,
  rfc VARCHAR(13) NOT NULL,
  cer_data BYTEA NOT NULL,
  key_data BYTEA NOT NULL,
  key_password_encrypted BYTEA NOT NULL,
  encryption_iv BYTEA NOT NULL,
  encryption_tag BYTEA NOT NULL,
  serial_number VARCHAR(100),
  valid_from TIMESTAMP NOT NULL,
  valid_until TIMESTAMP NOT NULL,
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Columnas agregadas a tabla cfdis (por tenant)

ALTER TABLE tenant_xxx.cfdis ADD COLUMN xml_original TEXT;
ALTER TABLE tenant_xxx.cfdis ADD COLUMN updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE tenant_xxx.cfdis ADD COLUMN last_sat_sync TIMESTAMP;
ALTER TABLE tenant_xxx.cfdis ADD COLUMN sat_sync_job_id UUID;
ALTER TABLE tenant_xxx.cfdis ADD COLUMN source VARCHAR(20) DEFAULT 'manual';

Dependencias

{
  "@nodecfdi/sat-ws-descarga-masiva": "^2.0.0",
  "@nodecfdi/credentials": "^2.0.0",
  "@nodecfdi/cfdi-core": "^1.0.1"
}

Flujo de Sincronización

1. Usuario configura FIEL (certificado .cer + llave .key + contraseña)
   ↓
2. Sistema valida y encripta credenciales (AES-256-GCM)
   ↓
3. Usuario inicia sincronización (manual o automática 3:00 AM)
   ↓
4. Sistema desencripta FIEL y crea cliente SAT
   ↓
5. Por cada mes en el rango:
   a. Solicitar CFDIs emitidos al SAT
   b. Esperar respuesta (polling cada 30s)
   c. Descargar paquetes ZIP
   d. Extraer y parsear XMLs
   e. Guardar en BD del tenant
   f. Repetir para CFDIs recibidos
   ↓
6. Marcar job como completado

API Endpoints

FIEL

Método Ruta Descripción
GET /api/fiel/status Estado de la FIEL configurada
POST /api/fiel/upload Subir nueva FIEL
DELETE /api/fiel Eliminar FIEL

Sincronización SAT

Método Ruta Descripción
POST /api/sat/sync Iniciar sincronización
GET /api/sat/sync/status Estado actual
GET /api/sat/sync/history Historial de syncs
GET /api/sat/sync/:id Detalle de un job
POST /api/sat/sync/:id/retry Reintentar job fallido

Parámetros de sincronización

interface StartSyncRequest {
  type?: 'initial' | 'daily';  // default: 'daily'
  dateFrom?: string;           // ISO date, ej: "2025-01-01T00:00:00"
  dateTo?: string;             // ISO date, ej: "2025-12-31T23:59:59"
}

Configuración

Variables de entorno

# Clave para encriptar credenciales FIEL (32 bytes hex)
FIEL_ENCRYPTION_KEY=tu_clave_de_32_bytes_en_hexadecimal

# Zona horaria para el cron
TZ=America/Mexico_City

Límites del SAT

  • Antigüedad máxima: 6 años
  • Solicitudes por día: Limitadas (se reinicia cada 24h)
  • Tamaño de paquete: Variable

Errores Comunes del SAT

Código Mensaje Solución
5000 Solicitud Aceptada OK - esperar verificación
5002 Límite de solicitudes agotado Esperar 24 horas
5004 No se encontraron CFDIs Normal si no hay facturas en el rango
5005 Solicitud duplicada Ya existe una solicitud pendiente
- Información mayor a 6 años Ajustar rango de fechas
- No se permite descarga de cancelados Facturas canceladas no disponibles

Seguridad

  1. Encriptación de credenciales: AES-256-GCM con IV único
  2. Almacenamiento seguro: Certificado, llave y contraseña encriptados
  3. Autenticación: JWT con tenantId embebido
  4. Aislamiento: Cada tenant tiene su propio schema en PostgreSQL

Servicios Systemd

# API Backend
systemctl status horux-api

# Web Frontend
systemctl status horux-web

Comandos Útiles

# Ver logs de sincronización SAT
journalctl -u horux-api -f | grep "\[SAT\]"

# Estado de jobs
psql -U postgres -d horux360 -c "SELECT * FROM sat_sync_jobs ORDER BY created_at DESC LIMIT 5;"

# CFDIs sincronizados por tenant
psql -U postgres -d horux360 -c "SELECT COUNT(*) FROM tenant_xxx.cfdis WHERE source = 'sat';"

Changelog

2026-01-25

  • Implementación inicial de sincronización SAT
  • Integración con librería @nodecfdi/sat-ws-descarga-masiva
  • Soporte para fechas personalizadas en sincronización
  • Corrección de cast UUID en queries SQL
  • Agregadas columnas faltantes a tabla cfdis
  • UI para selección de periodo personalizado
  • Cambio de servicio web a modo producción (next start)

Estado Actual (2026-01-25)

Completado

  • Servicio de encriptación de credenciales FIEL
  • Integración con @nodecfdi/sat-ws-descarga-masiva
  • Parser de XMLs de CFDI
  • UI para subir FIEL
  • UI para ver estado de sincronización
  • UI para seleccionar periodo personalizado
  • Cron job para sincronización diaria (3:00 AM)
  • Soporte para fechas personalizadas
  • Corrección de cast UUID en queries
  • Columnas adicionales en tabla cfdis de todos los tenants

Pendiente por probar

El SAT bloqueó las solicitudes por exceso de pruebas. Esperar 24 horas y luego:

  1. Ir a Configuración > SAT
  2. Clic en "Periodo personalizado"
  3. Seleccionar: 2025-01-01 a 2025-12-31
  4. Clic en "Sincronizar periodo"

Tenant de prueba

  • RFC: CAS2408138W2
  • Schema: tenant_cas2408138w2
  • Nota: Los CFDIs "recibidos" de este tenant están cancelados (SAT no permite descargarlos)

Comandos para verificar después de 24h

# Ver estado del sync
PGPASSWORD=postgres psql -h localhost -U postgres -d horux360 -c \
  "SELECT status, cfdis_found, cfdis_downloaded, cfdis_inserted FROM sat_sync_jobs ORDER BY created_at DESC LIMIT 1;"

# Ver logs en tiempo real
journalctl -u horux-api -f | grep "\[SAT\]"

# Contar CFDIs sincronizados
PGPASSWORD=postgres psql -h localhost -U postgres -d horux360 -c \
  "SELECT COUNT(*) as total FROM tenant_cas2408138w2.cfdis WHERE source = 'sat';"

Problemas conocidos

  1. "Se han agotado las solicitudes de por vida": Límite de SAT alcanzado, esperar 24h
  2. "No se permite la descarga de xml que se encuentren cancelados": Normal para facturas canceladas
  3. "Información mayor a 6 años": SAT solo permite descargar últimos 6 años

Próximos Pasos

  • Probar sincronización completa después de 24h
  • Verificar que los CFDIs se guarden correctamente
  • Implementar reintentos automáticos para errores temporales
  • Notificaciones por email al completar sincronización
  • Dashboard con estadísticas de CFDIs por periodo
  • Soporte para filtros adicionales (RFC emisor/receptor, tipo de comprobante)