Skip to content

Validations

The SDK enforces locally every validation listed in Validaciones y errores del servicio v1.2.2. This lets you catch malformed records before spending a SOAP call — the AEAT returns the same diagnostics it would have returned, but in microseconds.

Calling the validator

ts
import {
  validateInvoiceForRegister,
  validateInvoiceForCancel,
} from 'verifactu-sdk/validators';

const issues = validateInvoiceForRegister(invoice);
if (issues.some((i) => i.severity === 'rejection')) {
  throw new BusinessValidationError('Invoice rejected', { code: issues[0]!.code });
}

Each returned ValidationResult carries:

  • code — the closest matching AEAT error code from the catalog.
  • field — dotted path of the offending field (e.g. breakdown.0.taxRate).
  • severity'rejection' rejects the record, 'admissible' accepts it with a warning.
  • message — short English description of the violation.

VerifactuClient.registerInvoice runs the validator automatically and throws BusinessValidationError on rejection; you only call the validator directly when you want to inspect every issue at once.

The 23 rules

The validator implements one or more checks per AEAT rule. The mapping is:

RuleField groupWhat it checksSeverity
1IDFacturaNIF format, NumSerieFactura allowed chars, FechaExpedicion ≥ 28-10-2024 and ±20 years from today.Rejection
2RechazoPrevio + SubsanacionCoherent combinations only (N, S, X).Rejection
3TipoRectificativaMandatory iff TipoFactura starts with R.Rejection
4FacturasRectificadasOnly allowed on R-type invoices.Rejection
5FacturasSustituidasOnly allowed on F3 invoices.Rejection
6ImporteRectificacionRequired when TipoRectificativa = S.Rejection
7FechaOperacion±20 years vs. issue date, special rules for ClaveRegimen 14/15.Rejection
8FacturaSimplificadaArt7273Only on F1/F3/R1/R2/R3/R4.Rejection
9FacturaSinIdentifDestinatarioArt61dOnly on F2/R5.Rejection
10MacrodatoMandatory when ImporteTotal ≥ 100 000 000 €.Rejection
11EmitidaPorTerceroODestinatarioCoherence with Tercero / Destinatarios.Rejection
12TerceroNIF format, distinct from emitter, NIF-IVA per country structure.Rejection
13DestinatariosRequired for F1/F3/R1/R2/R3/R4; forbidden for F2/R5; NIF format; NIF-IVA structure.Rejection
14CuponOnly allowed when TipoFactura is R1 or R5.Rejection
15.1Desglose.TipoImpositivoAllowed rate by date (per AEAT historic-rate table).Rejection
15.2BaseImponibleACosteOnly allowed when ClaveRegimen = 06 or Impuesto in 02/05.Rejection
15.3TipoRecargoEquivalenciaValid pair with TipoImpositivo (21/5.2, 10/1.4, …).Rejection
15.4CalificacionOperacionS1/S2/N1/N2; mutual exclusion with OperacionExenta.Rejection
15.5OperacionExentaE1–E6 (IVA); E7/E8 only for IGIC; coherence with TipoFactura.Rejection
15.6ClaveRegimen01–21 with sub-rules for 02/03/04/06/07/08/10/11/14/20/21.Rejection
15.7CuotaRepercutidabase × rate with ±10 € tolerance (≤ 3 000 € for simplified).Rejection
16CuotaTotalSum of repercussed amounts with ±10 € tolerance.Rejection
17ImporteTotalSum of bases + tax + surcharge with ±10 € tolerance.Rejection
18Huella anterior64 uppercase hex chars when present.Admissible (2003)
19SistemaInformaticoSystemId 2 chars [A-Z0-9] excluding Ñ; TipoUsoPosibleSoloVerifactu / MultiOT coherence.Rejection
20FechaHoraHusoGenRegistroValid ISO 8601 with offset; not in the far future.Rejection / Admissible
21NumRegistroAcuerdoFacturacion≤ 15 chars, allowed character set.Rejection
22IdAcuerdoSistemaInformatico≤ 16 chars, allowed character set.Rejection
23Huella64 uppercase hex chars.Admissible (1292)

NIF, NIE, CIF

The local validator covers the three Spanish identifier shapes and the check digit:

  • DNI: 8 digits + control letter computed from the modulo-23 table.
  • NIE: X|Y|Z prefix + 7 digits + control letter, same table.
  • CIF: letter + 7 digits + control digit/letter (mixed modulo-10 and modulo-11 algorithm per AEAT spec).

NIF-IVA (EU)

The validator covers the 28 historic EU member states plus Northern Ireland (GB/XI) with Brexit-aware date handling. The structures and the per-country rules are loaded from validators/nifIva.ts. Examples:

CountryCodeStructure
GermanyDE9 digits
FranceFR11 chars (2 alphanumeric + 9 digits)
ItalyIT11 digits
PortugalPT9 digits
NetherlandsNL12 chars
UKGB9 / 12 digits; valid only ≤ 31-12-2020 (post-Brexit, use XI)
Northern IrelandXI9 / 12 digits; valid only ≥ 01-01-2021

The validator accepts country=ES for completeness, but Spanish NIFs go through the local NIF/CIF path instead.

Severity

The AEAT distinguishes three severities:

  • envelope — the entire submission is rejected (codes 4xxx, set by the service after parsing the SOAP envelope).
  • record — only the offending record is rejected (codes 1xxx and 3xxx).
  • admissible — the record is accepted but flagged for subsanación (codes 2xxx).

The local validator returns the first two as severity: 'rejection' and the third as severity: 'admissible'. Envelope errors come from the wire layer and never appear in the local result.

Next

Released under the MIT license.