All files / src vat.ts

100% Statements 4/4
100% Branches 8/8
100% Functions 1/1
100% Lines 3/3

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57                                                                                        9037x     6972x     9037x            
/**
 * Spanish VAT numbers for intra-EU operations (VAT-1): `ES` + NIF
 * (RD 1065/2007 art. 25.1).
 *
 * Format only. A valid format does not mean the number is registered in
 * VIES, the EU's VAT Information Exchange System.
 *
 * Rule IDs refer to SPEC.md.
 */
import { isValidNif } from "./nif";
import { removeSeparators } from "./normalize";
import { NO_OPTIONS, toUpperAsciiLetter } from "./shared";
import type { IsValidOptions } from "./types";
 
/**
 * Checks the format of a Spanish VAT number: `ES` followed by a valid NIF
 * (DNI, K/L/M NIF, NIE or NIF of a legal person or entity), as RD 1065/2007 art. 25.1
 * defines it (VAT-1). The `ES` prefix is required.
 *
 * **A valid format is not the same as a registered number**: this does not
 * query VIES. Use the European Commission's VIES service to check that a
 * number is registered.
 *
 * Takes the same options as the boolean validators. The input is
 * normalized first (NORM-1 to NORM-4), so `"es b-1234567-4"` is valid.
 * Never throws: any value that is not a string returns `false`.
 *
 * @param value The value to check.
 * @param opts `normalize` (default `true`), `cifControl` (default
 * `"official"`), `rejectPlaceholders` (default `false`).
 * @returns true if the value is `ES` + a valid NIF.
 * @example
 * isValidSpanishVat("ES12345678Z");    // true
 * isValidSpanishVat("es b-1234567-4"); // true
 * @example
 * isValidSpanishVat("12345678Z");   // false: no ES prefix
 * isValidSpanishVat("ES12345678A"); // false: the NIF is invalid (DNI-2)
 * @see {@link https://github.com/josegoval/nif-dni-nie-cif-validation/blob/master/SPEC.md#vat-1 SPEC.md#vat-1}
 * @since 2.0.0
 */
export function isValidSpanishVat(
  value: unknown,
  opts: IsValidOptions = NO_OPTIONS
): boolean {
  if (typeof value !== "string") return false;
  // NORM-2 / NORM-3 come first, so a separator inside the prefix goes too
  // ("E-S..."), as in validate().
  const clean = opts?.normalize === false ? value : removeSeparators(value);
  // VAT-1: the prefix is required here (NORM-1: either case), then the rest
  // is a NIF with the same options.
  return (
    toUpperAsciiLetter(clean.charCodeAt(0)) === 69 &&
    toUpperAsciiLetter(clean.charCodeAt(1)) === 83 &&
    isValidNif(clean.slice(2), opts)
  );
}