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.