# 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)

```sql
-- 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)

```sql
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

```json
{
  "@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

```typescript
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

```env
# 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

```bash
# API Backend
systemctl status horux-api

# Web Frontend
systemctl status horux-web
```

## Comandos Útiles

```bash
# 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

- [x] Servicio de encriptación de credenciales FIEL
- [x] Integración con @nodecfdi/sat-ws-descarga-masiva
- [x] Parser de XMLs de CFDI
- [x] UI para subir FIEL
- [x] UI para ver estado de sincronización
- [x] UI para seleccionar periodo personalizado
- [x] Cron job para sincronización diaria (3:00 AM)
- [x] Soporte para fechas personalizadas
- [x] Corrección de cast UUID en queries
- [x] 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**: HTS240708LJA
- **Schema**: `tenant_cas2408138w2`
- **Nota**: Los CFDIs "recibidos" de este tenant están cancelados (SAT no permite descargarlos)

### Comandos para verificar después de 24h

```bash
# 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)
