# Capacidad: Devolución de resultado a XTRF

Cierre del circuito: el SIF cambia la categoría de la factura en XTRF vía su Home API REST.
Decisiones de referencia: **D21** (el éxito exige AEAT + PDF), **D17** (idempotencia).

**Dependencia pendiente: DEP-4** (URL base, token y los dos identificadores de categoría).

## ADDED Requirements

### Requirement: Cliente de la Home API de XTRF

El SIF DEBE encapsular la llamada a XTRF tras una interfaz `XtrfClient` con una única operación
relevante en fase 1: **fijar la categoría de una factura**.

Toda la parametrización DEBE venir de configuración, editable desde la UI:

| Clave | Contenido |
|---|---|
| `xtrf.base_url` | URL base de la Home API |
| `xtrf.token` | Token de autenticación (marcado como secreto) |
| `xtrf.categoria_exito` | Identificador de la categoría de éxito |
| `xtrf.categoria_fallo` | Identificador de la categoría de fallo |
| `xtrf.timeout_s` | Timeout de la llamada (por defecto 20) |
| `xtrf.habilitado` | Interruptor general (por defecto `false` hasta DEP-4) |

#### Scenario: Configuración incompleta

- **WHEN** `xtrf.habilitado = true` y falta la URL, el token o alguna de las categorías
- **THEN** el sistema no intenta la llamada
- **AND** abre una incidencia indicando exactamente qué parámetro falta

#### Scenario: Integración desactivada

- **WHEN** `xtrf.habilitado = false`
- **THEN** el flujo completo funciona igual: registro, PDF y envío a la AEAT
- **AND** las facturas quedan con `xtrf_estado = PENDIENTE` sin generar incidencia
- **AND** al activar el interruptor, las pendientes se notifican en el siguiente ciclo

> Esto permite implementar y probar todo el circuito antes de que llegue DEP-4.

---

### Requirement: El éxito exige resultado AEAT **y** PDF desplegado

El SIF DEBE llamar con la **categoría de éxito** únicamente cuando se cumplan **ambas** condiciones:

- estado AEAT ∈ {`CORRECTO`, o `ACEPTADO_CON_ERRORES` con `requiere_subsanacion = 0`}, **y**
- `pdf_estado = DESPLEGADO`.

En cualquier otro desenlace terminal DEBE llamar con la **categoría de fallo**.

Mientras no haya desenlace, NO DEBE llamar.

#### Scenario: Circuito completo correcto

- **WHEN** el registro queda `CORRECTO` y el PDF está desplegado
- **THEN** se llama a XTRF con `xtrf.categoria_exito`
- **AND** `xtrf_estado` pasa a `NOTIFICADO_OK`

#### Scenario: AEAT correcto pero PDF pendiente

- **WHEN** el registro está `CORRECTO` y `pdf_estado = PENDIENTE`
- **THEN** **no** se llama a XTRF
- **AND** la notificación queda a la espera de que el PDF se despliegue

#### Scenario: Registro rechazado

- **WHEN** el registro queda `RECHAZADO` o `RECHAZADO_ESTRUCTURA`
- **THEN** se llama a XTRF con `xtrf.categoria_fallo`
- **AND** `xtrf_estado` pasa a `NOTIFICADO_FALLO`

#### Scenario: Aceptado con errores subsanables

- **WHEN** el registro queda `ACEPTADO_CON_ERRORES` con `requiere_subsanacion = 1`
- **THEN** se llama con la **categoría de fallo**
- **AND** la UI explica que el registro está aceptado en la AEAT pero pendiente de subsanar

> Se notifica fallo porque la factura requiere intervención humana. Notificar éxito ocultaría en XTRF
> una obligación pendiente de subsanación.

#### Scenario: Estado indeterminado

- **WHEN** el registro está en `ESTADO_INDETERMINADO`
- **THEN** no se llama a XTRF en ninguna de las dos categorías
- **AND** se espera a que la consulta a la AEAT resuelva el estado real

---

### Requirement: Comportamiento cuando XTRF no responde

La indisponibilidad de XTRF NO DEBE bloquear ni alterar el flujo hacia la AEAT. El estado en la AEAT
es el autoritativo; la categoría en XTRF es un espejo de conveniencia.

Ante fallo, el SIF DEBE reintentar con retardo creciente: **5 intentos**, a 1, 5, 30, 180 y 1440
minutos. Agotados, `xtrf_estado` pasa a `FALLIDA`, se abre incidencia y se ofrece reintento manual.

#### Scenario: XTRF caído temporalmente

- **WHEN** la llamada falla por timeout o error de conexión
- **THEN** se reintenta según la política de retardos
- **AND** el registro de facturación conserva su estado AEAT sin cambios
- **AND** el PDF permanece desplegado

#### Scenario: Se agotan los reintentos

- **WHEN** los 5 intentos fallan
- **THEN** `xtrf_estado = FALLIDA`
- **AND** se abre una incidencia de severidad media
- **AND** la UI ofrece un botón de reintento manual

#### Scenario: XTRF responde con error de aplicación

- **WHEN** XTRF responde 4xx (token inválido, factura inexistente, categoría desconocida)
- **THEN** **no** se reintenta: un 4xx no se arregla repitiendo
- **AND** se abre incidencia con el cuerpo de la respuesta
- **AND** `xtrf_estado = FALLIDA`

#### Scenario: XTRF responde 5xx

- **WHEN** XTRF responde 5xx
- **THEN** sí se reintenta según la política

---

### Requirement: Idempotencia de la notificación

Cada factura DEBE notificarse **una sola vez** por desenlace. El SIF NO DEBE reenviar la misma
notificación si ya consta como entregada.

#### Scenario: Worker ejecutado dos veces

- **WHEN** el timer dispara mientras una notificación ya se completó
- **THEN** no se repite la llamada
- **AND** `xtrf_estado` permanece `NOTIFICADO_OK`

#### Scenario: Cambio de desenlace tras una subsanación

- **WHEN** una factura notificada como fallo se subsana con éxito
- **THEN** se emite una **nueva** notificación, esta vez de éxito
- **AND** ambas quedan trazadas en `auditoria`

---

### Requirement: Trazabilidad de las llamadas

De cada intento DEBEN registrarse: momento, URL invocada (**sin el token**), código HTTP, cuerpo de la
respuesta truncado y resultado.

El token NO DEBE aparecer nunca en logs, en la UI ni en mensajes de error.

#### Scenario: Token protegido

- **WHEN** se registra una llamada a XTRF
- **THEN** el token no aparece en `auditoria`, ni en los logs, ni en la incidencia
- **AND** en la pantalla de configuración se muestra enmascarado

#### Scenario: Diagnóstico de un fallo

- **WHEN** un operador investiga una notificación fallida
- **THEN** puede ver los 5 intentos con sus códigos HTTP y respuestas
- **AND** puede reintentar manualmente desde esa misma pantalla

---

### Requirement: Contrato provisional de la llamada

Pendiente de **DEP-4**, el cliente DEBE implementarse contra este contrato supuesto, aislado en una
única clase para que ajustarlo sea un cambio local:

```
PUT  {xtrf.base_url}/invoices/{idFacturaXtrf}/category
Headers: Authorization: Bearer {xtrf.token}
         Content-Type: application/json
Body:    { "categoryId": "{categoria_exito|categoria_fallo}" }
```

**[A VERIFICAR contra la documentación real de la Home API de XTRF: verbo, ruta, forma del token y
nombre de los campos.]**

#### Scenario: Ajuste del contrato al llegar DEP-4

- **WHEN** se conoce el contrato real de la Home API
- **THEN** el cambio se limita a la clase `XtrfClient` y a la configuración
- **AND** no afecta al motor de registro, a la huella, al envío ni a la generación del PDF
