se-panfilov/jsvat

View on GitHub
src/lib/countries/netherlands.ts

Summary

Maintainability
A
1 hr
Test Coverage
import { Country } from '../jsvat';

export const netherlands: Country = {
  name: 'Netherlands',
  codes: ['NL', 'NLD', '528'],
  calcFn: (input: string): boolean => {
    const vat = input.replace(/[\ \-\_]/g, '').toUpperCase();

    const { additional, multipliers } = netherlands.rules;
    if (!additional) return false;

    const match = vat.match(additional[0]);
    if (!match || !match[1]) return false;

    const numb = match[1];

    const characterValues = `NL${vat}`.split('').map(getCharValue);

    let total = 0;
    // Extract the next digit and multiply by the counter.
    for (let i = 0; i < 8; i++) {
      total += Number(numb.charAt(i)) * multipliers.common[i];
    }

    // Establish check digits by getting modulus 11.
    total = total % 11;
    if (total > 9) {
      total = 0;
    }

    // Compare it with the last character of the VAT number. If it's the same, then it's valid.
    const expect = Number(numb.slice(8, 9));

    // is either 11 proof or 97 mod proof.
    return total === expect || isNinetySevenMod(characterValues.join(''));
  },
  rules: {
    multipliers: {
      common: [9, 8, 7, 6, 5, 4, 3, 2]
    },
    regex: [/^(NL)(\d{9}B\d{2})$/],
    additional: [/^(\d{9})B\d{2}$/]
  }
};

function getCharValue(char: string): number {
  // if one of these set values
  if (char === '+') return 36;
  if (char === '*') return 37;

  // if A...Z return code VAL -55
  const code = char.charCodeAt(0) - 55;
  if (code > 9 && code < 91) return code;

  return parseInt(char, 10);
}

function isNinetySevenMod(value: string): boolean {
  const remainder = mod(value, 97);
  return remainder === 1;
}

// custom module function, to check module on values above Number limit
function mod(value: string, divisor: number): number {
  // Initialize result
  let res: number = 0;

  for (const char of value.split('')) {
    res = (res * 10 + +char) % divisor;
  }

  return res;
}