# Arquitectura del módulo FacturacionElectronica

## Ubicación

```text
api-a-conta\app\Services\FacturacionElectronica\
```

## Propósito

Motor interno de facturación electrónica para el SII de Chile. Reemplaza la dependencia de SimpleAPI (servicio externo de pago) generando, firmando y enviando DTE directamente desde `api-a-conta`.

---

## Estructura de carpetas

```text
FacturacionElectronica\
├── Contracts\              Interfaces (abstracciones del dominio)
│   ├── DteEmitterInterface.php
│   ├── CertificateLoaderInterface.php
│   ├── CafRepositoryInterface.php
│   └── SiiClientInterface.php
│
├── DTO\                    Objetos de transferencia (inmutables)
│   ├── DteEmissionRequest.php   Entrada para emitir un DTE
│   ├── DteEmissionResult.php    Respuesta del proceso de emisión
│   └── SiiSendResult.php        Respuesta del envío al SII
│
├── Emitters\               Implementaciones del DteEmitterInterface
│   ├── SimpleApiDteEmitter.php  Adaptador temporal (llama a SimpleAPI)
│   └── InternalDteEmitter.php   Motor propio (delega a DteEmissionService)
│
├── Services\               Lógica de negocio
│   ├── DteEmissionService.php   Orquestador principal del motor interno
│   ├── CertificateService.php   Carga y valida certificados PFX/P12
│   ├── CafService.php           Parsea CAF y reserva folios atómicamente
│   ├── TedBuilder.php           Construye y firma el nodo TED (SHA1withRSA)
│   ├── DteXmlBuilder.php        Construye el XML DTE completo
│   ├── XmlSignatureService.php  Firma Documento y SetDTE con xmlseclibs
│   ├── SiiAuthService.php       Semilla → Token SII (cacheado 5 min)
│   ├── SiiDteClient.php         Envía XML al SII, consulta estado por track_id
│   └── DteStorageService.php    Guarda XML en disco y registra logs
│
├── Exceptions\
│   ├── DteEmissionException.php
│   ├── CertificateException.php
│   ├── CafException.php
│   └── SiiException.php
│
├── Mappers\
│   └── PosVentaToDteMapper.php  Convierte datos POS → DteEmissionRequest
│
└── docs\
    ├── PLAN_MIGRACION.md        Plan completo con progress de fases
    ├── ARQUITECTURA.md          Este archivo
    └── ERRORES.md               Errores conocidos y soluciones
```

---

## Flujo de emisión interno

```
DteEmitterInterface::emitir(DteEmissionRequest)
        │
        ▼
InternalDteEmitter
        │
        ▼
DteEmissionService::emitir(request)
        │
        ├── 1. CertificateService::load(empresaId)
        │         → lee PFX de storage, desencripta con password
        │         → retorna [private_key, certificate, metadata]
        │
        ├── 2. CafService::reservarFolio(empresaId, tipoDte)
        │         → DB::transaction + lockForUpdate en dte_cafs
        │         → inserta en dte_folio_usages (estado: reservado)
        │         → retorna int folio
        │
        ├── 3. CafService::parsearCaf(caf)
        │         → DOMDocument::loadXML sobre caf_xml
        │         → extrae rsask, idk, rango D-H, rut_emisor
        │
        ├── 4. DteDocumento::create (estado: folio_reservado)
        │
        ├── 5. TedBuilder::build(cafData, dteData)
        │         → buildDD: nodo con emisor, receptor, folio, monto, primer item
        │         → canonicalize: DOMDocument::C14N
        │         → firmarDD: phpseclib3 RSA SHA1withRSA con rsask del CAF
        │         → retorna string XML <TED version="1.0">...</TED>
        │
        ├── 6. DteXmlBuilder::build(dteData, tedXml)
        │         → encabezado: IdDoc, Emisor, Receptor, Totales
        │         → detalle: un <Detalle> por item del carrito
        │         → referencias opcionales
        │         → inserta TED y TmstFirma
        │         → retorna string XML completo del DTE
        │
        ├── 7. dte->update(estado: xml_generado)
        │
        ├── 8. XmlSignatureService::firmarDocumento(xmlDte, certData)
        │         → xmlseclibs XMLSecurityDSig sobre nodo Documento
        │         → agrega firma enveloped RSA-SHA1
        │         → retorna XML firmado
        │
        ├── 9. dte->update(estado: firmado, xml_firmado: ...)
        │
        ├── 10. CafService::marcarEmitido(folio, dteDocumentoId)
        │
        ├── 11. DteStorageService::guardarXml
        │         → Storage::put en dte/{empresa}/{tipo}/{folio}.xml
        │
        ├── 12. DB::commit
        │
        └── 13. [Si DTE_SEND_SYNC=true] enviarAlSii(dte, xmlFirmado, ...)
                    │
                    ├── SiiAuthService::obtenerToken(rutEmisor, certData)
                    │       → semilla de palena/maullin.sii.cl
                    │       → firma semilla con cert
                    │       → retorna TOKEN (cacheado 5 min en Redis/file)
                    │
                    ├── buildEnvio: <EnvioDTE><SetDTE>...</SetDTE></EnvioDTE>
                    │
                    ├── XmlSignatureService::firmarEnvio(xmlEnvio, certData)
                    │
                    ├── SiiDteClient::enviar(xmlEnvioFirmado, rutEmisor, token)
                    │       → HTTP multipart/form-data a DTEUpload
                    │       → extrae TRACKID de respuesta
                    │
                    └── DteEnvio::create + dte->update(estado: enviado, track_id: ...)
```

---

## Configuración

Archivo: `config/dte.php`

| Variable env | Config key | Valores | Por defecto |
|---|---|---|---|
| `DTE_EMITTER` | `dte.emitter` | `simpleapi`, `internal` | `simpleapi` |
| `DTE_SII_ENV` | `dte.sii_env` | `certificacion`, `produccion` | `certificacion` |
| `DTE_STORAGE_DISK` | `dte.storage_disk` | `local`, `s3`, etc. | `local` |
| `DTE_SEND_SYNC` | `dte.send_sync` | `true`, `false` | `true` |
| `DTE_FAIL_MODE` | `dte.fail_mode` | `abort`, `warn` | `warn` |

### Cómo activar el motor interno

```env
DTE_EMITTER=internal
DTE_SII_ENV=certificacion   # o produccion
DTE_SEND_SYNC=true
DTE_FAIL_MODE=warn
```

---

## Injection de dependencias

Registrado en `FacturacionElectronicaServiceProvider`:

```php
// Binding de la interfaz principal según DTE_EMITTER del .env
DteEmitterInterface → SimpleApiDteEmitter  (DTE_EMITTER=simpleapi)
DteEmitterInterface → InternalDteEmitter   (DTE_EMITTER=internal)

// Bindings fijos de implementaciones concretas
CertificateLoaderInterface → CertificateService
CafRepositoryInterface     → CafService
SiiClientInterface         → SiiDteClient
```

El `PosVentaController` recibe `DteEmitterInterface` por inyección. Cambiar el emitter activo solo requiere cambiar `DTE_EMITTER` en `.env`.

---

## Modelos involucrados

| Modelo | Tabla | Estado | Propósito |
|---|---|---|---|
| `DteDocumento` | `dte_documentos` | ✅ Operativa | Registro principal de cada DTE emitido |
| `DteDetalle` | `dte_detalles` | ✅ Operativa | Detalle por ítem |
| `DteCaf` | `dte_cafs` | ✅ Operativa | CAF por empresa y tipo de DTE |
| `DteCertificado` | `dte_certificados` | ✅ Operativa | Certificados PFX por empresa (path en disco) |
| `DteFolioUsage` | `dte_folio_usages` | ✅ Operativa | Control de folios atómico (creada 2026-05-13) |
| `DteEnvio` | `dte_envios` | ⚠️ Stub vacío | Pendiente: completar columnas (ver `TRAZABILIDAD.md`) |
| `DteLog` | `dte_logs` | ⚠️ Stub vacío | Pendiente: completar columnas (ver `TRAZABILIDAD.md`) |

---

## Endpoints API

Grupo: `Route::prefix('dte')` dentro del middleware `auth:sanctum` (o sin auth según `ROUTES_PROTECTED`).

| Método | Ruta | Controlador | Descripción |
|---|---|---|---|
| POST | `/api/dte/emitir` | `DteEmissionController::emitir` | Emite DTE directamente |
| POST | `/api/dte/{id}/reenviar` | `DteEmissionController::reenviar` | Reintenta envío al SII |
| GET | `/api/dte/{id}` | `DteStatusController::show` | Estado del DTE |
| GET | `/api/dte/{id}/xml` | `DteXmlController::download` | Descarga XML firmado |
| GET | `/api/dte/{id}/estado` | `DteStatusController::show` | Alias de show |
| POST | `/api/dte/{id}/consultar-sii` | `DteStatusController::consultarSii` | Consulta SII por track_id |

---

## Endpoints SII reales (externos)

Hosts del SII de Chile. Configurados en `SiiDteClient.php` y `SiiAuthService.php`.

### Envío de documentos

**Importante:** boletas (tipos 39, 41) usan **host distinto** al resto de DTEs.

| Operación | Certificación | Producción |
|---|---|---|
| DTEs facturas/notas/guías (33, 34, 52, 56, 61) | `https://maullin.sii.cl/cgi_dte/UPL/DTEUpload` | `https://palena.sii.cl/cgi_dte/UPL/DTEUpload` |
| **DTEs boletas (39, 41)** | `https://rahue.sii.cl/cgi_dte/UPL/DTEUpload` | `https://pangal.sii.cl/cgi_dte/UPL/DTEUpload` |
| Libros (IECV: Ventas, Compras, Guías) | `https://maullin.sii.cl/cgi_dte/UPL/IECVUpload` | `https://palena.sii.cl/cgi_dte/UPL/IECVUpload` |

**Estado de implementación:**
- `SiiDteClient::urlEnvio(int $tipoDte)` selecciona host correcto según tipo (commit nuevo).
- `SiiLibroClient` soporta endpoint configurable: `iecv` (oficial) o `dte` (experimental para probar). Ver `COMPARACION_DTENGINE.md` §9.
- Guías de despacho (tipo 52) van por `DTEUpload` host DTE, NO por `IECVUpload` ni boleta host.

### Autenticación y consulta (SOAP)

| Servicio | Certificación | Producción |
|---|---|---|
| Solicitar semilla | `https://maullin.sii.cl/DTEWS/CrSeed.jws` | `https://palena.sii.cl/DTEWS/CrSeed.jws` |
| Obtener token | `https://maullin.sii.cl/DTEWS/GetTokenFromSeed.jws` | `https://palena.sii.cl/DTEWS/GetTokenFromSeed.jws` |
| Consultar estado por track_id | `https://maullin.sii.cl/DTEWS/QueryEstUp.jws` | `https://palena.sii.cl/DTEWS/QueryEstUp.jws` |

**Notas técnicas:**
- Token SII tiene TTL real de **90 segundos** (no 5 minutos como se asumía inicialmente). Ajustar caché.
- En Windows, los WSDL remotos pueden fallar por SSL. DTEngine cachea WSDL local en `storage/wsdl/` — replicar este patrón si aparecen errores SSL.

---

## Tipos DTE soportados

### Fase 1 (implementados en DteXmlBuilder)

| Código | Tipo | Boleta/Factura |
|---|---|---|
| 33 | Factura electrónica | Factura |
| 34 | Factura exenta electrónica | Factura |
| 39 | Boleta electrónica | Boleta |
| 41 | Boleta no afecta o exenta electrónica | Boleta |

### Fase 2 (estructura preparada, pendiente validación)

| Código | Tipo |
|---|---|
| 52 | Guía de despacho |
| 56 | Nota de débito |
| 61 | Nota de crédito |

---

## Firma digital — dos capas

### Capa 1: TED (Timbre Electrónico DTE)

- **Qué firma:** el nodo `<DD>` con datos del documento
- **Cómo:** SHA1withRSA con la **llave RSASK del CAF** (llave privada del SII)
- **Librería:** `phpseclib3\Crypt\RSA` (evita incompatibilidades con OpenSSL moderno y llaves antiguas)
- **Resultado:** nodo `<TED><DD>...</DD><FRMT algoritmo="SHA1withRSA">...</FRMT></TED>`

### Capa 2: Firma XML del Documento

- **Qué firma:** el nodo completo `<Documento ID="...">` del DTE
- **Cómo:** RSA-SHA1 enveloped signature con el **certificado digital del emisor**
- **Librería:** `robrichards/xmlseclibs` (XMLSecurityDSig)
- **Resultado:** nodo `<Signature>` insertado dentro de `<Documento>`

### Capa 3: Firma del Envío

- **Qué firma:** el nodo `<SetDTE>` del sobre de envío
- **Cómo:** RSA-SHA1 enveloped signature con el **certificado digital del emisor**
- **Librería:** `robrichards/xmlseclibs` (XMLSecurityDSig)
- **Resultado:** nodo `<Signature>` en `<SetDTE>`

---

## Storage de PFX y CAF

Convención de paths bajo `storage/app/dte/` (todos relativos al `local` disk).

```text
storage/app/dte/
└── empresas/
    └── {par_empresa_id}/
        ├── certificados/
        │   └── {archivo}.pfx                ← path en dte_certificados.ruta_pfx
        ├── cafs/
        │   └── {Tipo}_{folio_desde}_{folio_hasta}.xml   ← opcional, path en dte_cafs.caf_path
        ├── xml_generados/
        │   └── {tipo_dte}/{YYYY}/{MM}/{folio}.xml       ← generado por DteStorageService
        └── xml_enviados/
            └── {tipo_dte}/{YYYY}/{MM}/envio_{track_id}.xml
```

Reglas:
- `dte_certificados.ruta_pfx` y `dte_cafs.caf_path` son **rutas relativas a `storage/app/`**, no absolutas.
- El CAF se almacena duplicado: en `dte_cafs.caf_xml` (longtext, fuente de verdad) y opcionalmente en `caf_path` (cache para inspección manual).
- El XML emitido se guarda en BD (`dte_documentos.xml_firmado`) y en disco (`xml_generados/`) para auditoría.
- **Pendiente:** cifrar `dte_certificados.password_pfx` con `Crypt::encryptString` (hoy se guarda plano). Ver `TRAZABILIDAD.md` §4.1.

Para el flujo paso-a-paso de carga de archivos y primera emisión, ver `FLUJO_PRUEBAS.md`.

---

## Documentos relacionados

| Documento | Propósito |
|---|---|
| `PLAN_MIGRACION.md` | Plan completo con progreso de fases (0-14) |
| `FLUJO_PRUEBAS.md` | Cómo dejar archivos, cargar datos, emitir primera boleta y comparar contra DTEngine |
| `TRAZABILIDAD.md` | Modelo de datos comparado vs DTEngine, gaps y plan de cierre |
| `ERRORES.md` | Catálogo de errores conocidos con síntoma/causa/solución |

---

## Referencia técnica externa

| Componente | Fuente de referencia |
|---|---|
| Estructura XML DTE | `C:\xampp\htdocs\DTEngine` (fuente operativa) |
| Firma TED | `DTEngine\app\Services\SII\TedBuilder.php` |
| Envío sobre | `DTEngine\app\Services\SII\EnvelopeService.php` |
| Fixtures aceptados por SII | `C:\xampp\htdocs\DTEngine\fuente-verdad\` (certificación, simulación, muestras, intercambio) |
| Conceptos y fixtures académicos | `C:\Users\baezs\OneDrive\Escritorio\libredte-lib-core` (solo referencia, no copiar) |
