# Capacidad: Validación fiscal local previa al envío

Reglas que el SIF aplica **antes** de generar el registro, para no remitir a la AEAT nada que vaya a
ser rechazado. Cada regla lleva identificador `VF-nn` y el **código de error AEAT que evita**.

Fuente: `docs/aeat-especificaciones/Validaciones_Errores_VERIFACTU_1.2.2.pdf` (v1.2.2, 08/04/2026),
`docs/aeat-esquemas/HALLAZGOS.md`, `docs/boe/BOE-A-2024-22138.pdf` (listas L8A/L9/L10).

> **Principio.** Estas validaciones son un filtro **preventivo**, no una reimplementación de la AEAT.
> Ante la duda entre rechazar en local o dejar pasar, se rechaza en local: un `422` a XTRF es
> reversible; un registro remitido no.

## ADDED Requirements

### Requirement: El choice CalificacionOperacion / OperacionExenta es obligatorio y excluyente

Cada `DetalleDesglose` DEBE informar **exactamente uno** de `CalificacionOperacion` u
`OperacionExenta`. Nunca los dos, nunca ninguno.

- **VF-01** — evita el código **1195** ("Al menos uno de los dos campos OperacionExenta o
  CalificacionOperacion deben estar informados").
- **VF-02** — evita el código **1196** ("no pueden ser ambos informados ya que son excluyentes entre
  sí").

#### Scenario: Detalle sin ninguno de los dos

- **WHEN** un `DetalleDesglose` no lleva ni `CalificacionOperacion` ni `OperacionExenta`
- **THEN** la validación falla con `VF-01` y el registro no se genera

#### Scenario: Detalle con los dos

- **WHEN** un `DetalleDesglose` lleva ambos
- **THEN** la validación falla con `VF-02`

---

### Requirement: N1 y N2 con IVA prohíben los cuatro campos de cuota

Cuando `CalificacionOperacion` sea `N1` o `N2` e `Impuesto` sea `01` (IVA) o esté vacío, los campos
`TipoImpositivo`, `CuotaRepercutida`, `TipoRecargoEquivalencia` y `CuotaRecargoEquivalencia` NO DEBEN
informarse. DEBEN estar **ausentes del XML**, no a cero.

- **VF-03** — `VAL` apdo. 15.4.

> **Esta es la regla más importante para AbroadLink.** El caso habitual de la agencia son servicios de
> traducción a empresas de otros países, que van a `N2` (no sujeta por reglas de localización). Un
> serializador que emita `<CuotaRepercutida>0</CuotaRepercutida>` en lugar de omitir el elemento
> provoca el rechazo del registro.

#### Scenario: Factura a cliente alemán con cuota cero

- **WHEN** el desglose lleva `CalificacionOperacion = N2` y el JSON trae `taxAmount = 0`
- **THEN** el XML generado **omite** los elementos `TipoImpositivo`, `CuotaRepercutida`,
  `TipoRecargoEquivalencia` y `CuotaRecargoEquivalencia`
- **AND** informa `BaseImponibleOimporteNoSujeto` con el importe correspondiente

#### Scenario: El serializador intenta emitir un cero

- **WHEN** el generador de XML recibe `CuotaRepercutida = 0` en un detalle con `N2`
- **THEN** la validación falla con `VF-03` antes de construir el XML
- **AND** el mensaje distingue explícitamente "ausente" de "cero"

---

### Requirement: OperacionExenta prohíbe los mismos cuatro campos

Si `OperacionExenta` está cumplimentado, `TipoImpositivo`, `CuotaRepercutida`,
`TipoRecargoEquivalencia` y `CuotaRecargoEquivalencia` NO DEBEN informarse.

- **VF-04** — `VAL` apdo. 15.5.

#### Scenario: Operación exenta con cuota informada

- **WHEN** un detalle lleva `OperacionExenta = E1` y `CuotaRepercutida = 0`
- **THEN** la validación falla con `VF-04`

---

### Requirement: S2 (inversión del sujeto pasivo) exige ceros explícitos

Si `CalificacionOperacion` es `S2`, `TipoImpositivo` DEBE valer `0` y `CuotaRepercutida` DEBE valer
`0`. No se admite que vayan vacíos ni que los elementos no existan.

- **VF-05** — `VAL` apdo. 15.4.

Además, con `S2` el `TipoFactura` solo puede ser `F1`, `F3`, `R1`, `R2`, `R3` o `R4` (**VF-06**).

#### Scenario: S2 con campos ausentes

- **WHEN** un detalle lleva `S2` y omite `TipoImpositivo`
- **THEN** la validación falla con `VF-05`

> Nótese la asimetría deliberada con VF-03: `N1`/`N2` exigen **ausencia**, `S2` exige **cero
> explícito**. Confundirlas es un error de implementación previsible; ambas tienen test propio.

---

### Requirement: Tipos impositivos de IVA admitidos

Si `Impuesto` es `01` o está vacío y `CalificacionOperacion` es `S1`, `TipoImpositivo` DEBE ser uno de
**0; 2; 4; 5; 7,5; 10; 21**.

- **VF-07** — `VAL` apdo. 15.1.

Los tipos 5, 2 y 7,5 solo son admisibles dentro de ventanas temporales cerradas, todas ellas
**anteriores al 31/12/2024**, evaluadas sobre `FechaOperacion` (o `FechaExpedicionFactura` si aquella
no se informa):

| Tipo | Ventana admisible |
|---|---|
| 5 | 01/07/2022 – 30/09/2024 |
| 2 | 01/10/2024 – 31/12/2024 |
| 7,5 | 01/10/2024 – 31/12/2024 |

- **VF-08** — validación de ventana temporal.

#### Scenario: Factura ordinaria al 21 %

- **WHEN** un detalle lleva `S1` con `TipoImpositivo = 21`
- **THEN** la validación pasa

#### Scenario: Tipo fuera de la lista

- **WHEN** un detalle lleva `S1` con `TipoImpositivo = 15`
- **THEN** la validación falla con `VF-07`

#### Scenario: Tipo con ventana caducada

- **WHEN** una factura de 2026 lleva `TipoImpositivo = 5`
- **THEN** la validación falla con `VF-08` indicando la ventana en la que ese tipo fue admisible

---

### Requirement: ClaveRegimen es obligatoria en la práctica

Aunque el XSD declara `ClaveRegimen` como `minOccurs="0"`, la validación de negocio la hace
**obligatoria** siempre que `Impuesto` sea `01`, `02`, `03` o esté vacío. Para AbroadLink (IVA
peninsular) eso significa **siempre**.

Con `Impuesto = 01` el valor DEBE pertenecer a la lista **L8A**.

- **VF-09** — evita el código **1245**.
- **VF-10** — evita el código **1246** ("El valor del campo ClaveRegimen es incorrecto").

Valores de L8A (`ORDEN`, pág. 137559): `01 02 03 04 05 06 07 08 09 10 11 14 15 17 18 19 20 21`.
**No existen 12, 13 ni 16** — los huecos son reales, no un error de transcripción.

El valor por defecto para la operativa de la agencia es **`01` — Operación de régimen general**,
configurable en `configuracion.desglose.clave_regimen_defecto`.

#### Scenario: Desglose sin ClaveRegimen

- **WHEN** un detalle con `Impuesto = 01` omite `ClaveRegimen`
- **THEN** la validación falla con `VF-09`

#### Scenario: ClaveRegimen en un hueco de la lista

- **WHEN** un detalle lleva `ClaveRegimen = 13`
- **THEN** la validación falla con `VF-10`

---

### Requirement: Identificación del destinatario

Si `TipoFactura` es `F1`, `F3`, `R1`, `R2`, `R3` o `R4`, la agrupación `Destinatarios` DEBE estar
cumplimentada con al menos un destinatario (**VF-11**). Si es `F2` o `R5`, `Destinatarios` NO DEBE
estar cumplimentada (**VF-12**).

Dentro de cada destinatario DEBE informarse `NIF` **o** `IDOtro`, nunca ambos y nunca ninguno
(**VF-13**).

Restricciones de `IDOtro` (`VAL` apdo. 13):

| Regla | Id | Contenido |
|---|---|---|
| `IDType = 07` (No censado) exige `CodigoPais = ES` | **VF-14** | |
| Con `CodigoPais = ES`, `IDType` solo puede ser `03` o `07` | **VF-15** | |
| Con `IDType = 02` (NIF-IVA), `TipoFactura` debe ser `F1`, `F3`, `R1`, `R2`, `R3` o `R4` | **VF-16** | En fase 1 esto significa: **`IDType=02` es incompatible con `F2`** |
| Con `IDType = 02`, el identificador debe ajustarse a la estructura de NIF-IVA de un Estado miembro | **VF-17** | Validación de formato en local; la existencia censal la comprueba la AEAT |

`IDType` admite **02–07**; **no existe el valor 01**.

#### Scenario: Factura F1 sin destinatario

- **WHEN** una factura `F1` no trae bloque de destinatario
- **THEN** la validación falla con `VF-11`

#### Scenario: Simplificada con destinatario

- **WHEN** una factura `F2` trae un destinatario identificado
- **THEN** la validación falla con `VF-12`

#### Scenario: Cliente intracomunitario en factura simplificada

- **WHEN** se intenta emitir una `F2` con un cliente identificado por `IDType = 02`
- **THEN** la validación falla con `VF-12` y `VF-16`
- **AND** el mensaje indica que un cliente con NIF-IVA exige factura completa `F1`

#### Scenario: Cliente español identificado con IDOtro incorrecto

- **WHEN** un destinatario lleva `CodigoPais = ES` con `IDType = 02`
- **THEN** la validación falla con `VF-15`, indicando que con `ES` solo caben `03` o `07`

---

### Requirement: Coherencia de totales antes del envío

El SIF DEBE comprobar en local, y **bloquear el envío** si no cuadran:

- **VF-18** — `ImporteTotal` = Σ(`BaseImponibleOimporteNoSujeto` + `CuotaRepercutida` +
  `CuotaRecargoEquivalencia`) de todas las líneas de desglose. Evita el código **2005**.
- **VF-19** — `CuotaTotal` = Σ(`CuotaRepercutida` + `CuotaRecargoEquivalencia`). Evita el código
  **2006**.

La AEAT admite un margen de ±10,00 €, pero el SIF DEBE exigir **cuadre exacto al céntimo**.

> **Por qué más estricto que la AEAT.** 2005 y 2006 son errores *admisibles*: el registro se acepta y
> queda marcado con error para siempre, obligando a subsanar (`VAL` apdo. 4.3.1). Tolerar hasta 10 €
> en local no evita nada y sí deja pasar descuadres reales. El coste de exigir cuadre exacto es que
> hay que corregir la factura en XTRF, que es donde está el error de verdad.

Estas dos validaciones NO se aplican cuando `ClaveRegimen` sea `03`, `05`, `06`, `08` o `09`
(**VF-20**), casos que la agencia no usa pero que el código debe contemplar.

#### Scenario: Descuadre de un céntimo

- **WHEN** `ImporteTotal` difiere en 0,01 € de la suma del desglose
- **THEN** la validación falla con `VF-18` mostrando ambos importes y la diferencia
- **AND** el registro no se genera

#### Scenario: Factura española correcta

- **WHEN** el desglose lleva base 482,55 € y cuota 101,33 € al 21 %, con `ImporteTotal = 583,88` y
  `CuotaTotal = 101,33`
- **THEN** VF-18 y VF-19 pasan

---

### Requirement: Validaciones de identificación y fechas

- **VF-21** — `IDEmisorFactura` DEBE ser idéntico al `NIF` de `Cabecera/ObligadoEmision`. Evita el
  código **1108**.
- **VF-22** — `FechaExpedicionFactura` NO DEBE ser posterior a la fecha actual.
- **VF-23** — `FechaExpedicionFactura` NO DEBE ser anterior al **28/10/2024** (entrada en vigor de la
  Orden). Una factura más antigua no puede registrarse.
- **VF-24** — `NumSerieFactura` solo puede contener ASCII 32–126, y NO DEBE contener
  `"` (34), `'` (39), `<` (60), `>` (62) ni `=` (61). Evita el código **1104**.
- **VF-25** — `NumSerieFactura` DEBE tener entre 1 y 60 caracteres.
- **VF-26** — `DescripcionOperacion` es obligatoria, máximo 500 caracteres.
- **VF-27** — `Macrodato` DEBE informarse con `S` si `|ImporteTotal| >= 100.000.000,00`.

#### Scenario: Número de factura con caracteres prohibidos

- **WHEN** el número de factura es `195=2026`
- **THEN** la validación falla con `VF-24` señalando el carácter `=`

#### Scenario: Número de factura con barra

- **WHEN** el número de factura es `195/2026`
- **THEN** la validación pasa: la barra es ASCII 47, permitida
- **AND** el mismo valor se usa sin alterar en la huella (`../registro-huella-cadena/spec.md`)

#### Scenario: Factura retroactiva anterior a la Orden

- **WHEN** se envía una factura con fecha 15/06/2024
- **THEN** la validación falla con `VF-23`

---

### Requirement: Límite de 12 detalles de desglose

`Desglose` es obligatorio y admite **entre 1 y 12** `DetalleDesglose`. El SIF DEBE agregar las líneas
de la factura por la combinación (`Impuesto`, `ClaveRegimen`, `CalificacionOperacion`/
`OperacionExenta`, `TipoImpositivo`) y DEBE fallar de forma explícita si la agregación excede 12.

- **VF-28** — evita el código **4113**.

#### Scenario: Factura de 19 páginas con un solo régimen fiscal

- **WHEN** una factura tiene 200 líneas, todas `N2` sin cuota
- **THEN** el desglose resultante tiene **un solo** `DetalleDesglose` con la base agregada
- **AND** la validación pasa

#### Scenario: Más de 12 combinaciones fiscales

- **WHEN** la agregación produce 13 combinaciones distintas
- **THEN** la validación falla con `VF-28` y enumera las combinaciones detectadas

> El `Desglose` es el **desglose fiscal**, no las líneas de la factura. Es una confusión frecuente y
> con consecuencias: un implementador que mapee línea a línea agotará el límite de 12 en la primera
> factura real de esta agencia.

---

### Requirement: Coherencia de las operativas de alta

- **VF-29** — `RechazoPrevio = X` solo si `Subsanacion = S`.
- **VF-30** — `RechazoPrevio = S` no puede informarse si `Subsanacion` está ausente o vale `N`.
- **VF-31** — En un alta ordinaria (`tipo_operacion = ALTA`), `Subsanacion` y `RechazoPrevio` DEBEN
  estar ausentes o valer `N`.

#### Scenario: Combinación imposible

- **WHEN** se construye un registro con `Subsanacion` ausente y `RechazoPrevio = X`
- **THEN** la validación falla con `VF-29`
- **AND** el registro no llega a generarse

---

### Requirement: El catálogo de errores se carga desde el fichero oficial

El SIF DEBE precargar `catalogo_errores_aeat` desde
`docs/aeat-especificaciones/errores.properties`, convirtiendo de **latin-1 a UTF-8** en la carga.

Los **10 códigos admisibles** (2000–2009) DEBEN quedar marcados con `categoria = 'ADMISIBLE'`, y con
`requiere_subsanacion = 0` **únicamente** para el **2004** y el **2009**, que están exceptuados de la
obligación de subsanar.

#### Scenario: Carga del catálogo

- **WHEN** se ejecuta la carga inicial del catálogo
- **THEN** se registran 247 códigos, con 44 de rechazo de envío, 193 de rechazo de registro y 10
  admisibles
- **AND** los acentos se muestran correctamente en la UI

#### Scenario: Código admisible exceptuado de subsanación

- **WHEN** la AEAT devuelve el código 2004
- **THEN** el registro queda `ACEPTADO_CON_ERRORES` con `requiere_subsanacion = 0`
- **AND** la UI no ofrece la acción de subsanar para ese registro
