validatorjs / validator.js

String validation
MIT License
23.02k stars 2.29k forks source link

isVAT add suport to rest of countries #1960

Closed Dev1lDragon closed 1 year ago

Dev1lDragon commented 2 years ago

I managed to add basic regex support to all countries listed in VAT identification number - Wikipedia;

More extended validation for PT(Portugal).

Here is my code:

import assertString from './util/assertString';
import * as algorithms from './util/algorithms';

const PT = (str) => {
    const match = (str || '').match(/^(PT)?(\d{9})$/);
    if (!match) {
        return false;
    }

    const tin = match[3];

    const checksum =
        11 -
        (algorithms.reverseMultiplyAndSum(
            tin
                .split('')
                .slice(0, 8)
                .map((a) => parseInt(a, 10)),
            9
        ) %
            11);
    if (checksum > 9) {
        return parseInt(tin[8], 10) === 0;
    }
    return checksum === parseInt(tin[8], 10);
};

export const vatMatchers = {
    /*European Union VAT identification numbers*/
    AT: (str) => /^(AT)?U\d{8}$/.test(str),
    BE: (str) => /^(BE)?\d{10}$/.test(str),
    BG: (str) => /^(BG)?\d{9,10}$/.test(str),
    HR: (str) => /^(HR)?\d{11}$/.test(str),
    CY: (str) => /^(CY)?\w{9}$/.test(str),
    CZ: (str) => /^(CZ)?\d{8,10}$/.test(str),
    DK: (str) => /^(DK)?\d{8}$/.test(str),
    EE: (str) => /^(EE)?\d{9}$/.test(str),
    FI: (str) => /^(FI)?\d{8}$/.test(str),
    FR: (str) => /^(FR)?\w{2}\d{9}$/.test(str),
    DE: (str) => /^(DE)?\d{9}$/.test(str),
    EL: (str) => /^(EL)?\d{9}$/.test(str),
    HU: (str) => /^(HU)?\d{8}$/.test(str),
    IE: (str) => /^(IE)?\d{7}\w{1}(W)?$/.test(str),
    IT: (str) => /^(IT)?\d{11}$/.test(str),
    LV: (str) => /^(LV)?\d{11}$/.test(str),
    LT: (str) => /^(LT)?\d{9,12}$/.test(str),
    LU: (str) => /^(LU)?\d{8}$/.test(str),
    MT: (str) => /^(MT)?\d{8}$/.test(str),
    NL: (str) => /^(NL)?\d{9}B\d{2}$/.test(str),
    PL: (str) =>
        /^(PL)?(\d{10}|(\d{3}-\d{3}-\d{2}-\d{2})|(\d{3}-\d{2}-\d{2}-\d{3}))$/.test(
            str
        ),
    PT,
    RO: (str) => /^(RO)?\d{2,10}$/.test(str),
    SK: (str) => /^(SK)?\d{10}$/.test(str),
    SI: (str) => /^(SI)?\d{8}$/.test(str),
    ES: (str) => /^(ES)?\w\d{7}[A-Z]$/.test(str),
    SE: (str) => /^(SE)?\d{12}$/.test(str),

    /*VAT numbers of non-EU countries*/
    AL: (str) => /^(AL)?\w{9}[A-Z]$/.test(str),
    MK: (str) => /^(MK)?\d{13}$/.test(str),
    AU: (str) => /^(AU)?\d{11}$/.test(str),
    BY: (str) => /^(УНП )?\d{9}$/.test(str),
    CA: (str) => /^(CA)?\d{9}$/.test(str),
    IS: (str) => /^(IS)?\d{5,6}$/.test(str),
    IN: (str) => /^(IN)?\d{15}$/.test(str),
    ID: (str) =>
        /^(ID)?(\d{15}|(\d{2}.\d{3}.\d{3}.\d{1}-\d{3}.\d{3}))$/.test(str),
    IL: (str) => /^(IL)?\d{9}$/.test(str),
    KZ: (str) => /^(KZ)?\d{9}$/.test(str),
    NZ: (str) => /^(NZ)?\d{9}$/.test(str),
    NG: (str) => /^(NG)?(\d{12}|(\d{8}-\d{4}))$/.test(str),
    NO: (str) => /^(NO)?\d{9}MVA$/.test(str),
    PH: (str) => /^(PH)?(\d{12}|\d{3} \d{3} \d{3} \d{3})$/.test(str),
    RU: (str) => /^(RU)?(\d{10}|\d{12})$/.test(str),
    SM: (str) => /^(SM)?\d{5}$/.test(str),
    SA: (str) => /^(SA)?\d{15}$/.test(str),
    RS: (str) => /^(RS)?\d{9}$/.test(str),
    CH: (str) =>
        /^(CH)?(\d{6}|\d{9}|(\d{3}.\d{3})|(\d{3}.\d{3}.\d{3}))(TVA|MWST|IVA)$/.test(
            str
        ),
    TR: (str) => /^(TR)?\d{10}$/.test(str),
    UA: (str) => /^(UA)?\d{12}$/.test(str),
    GB: (str) =>
        /^GB((\d{3} \d{4} ([0-8][0-9]|9[0-6]))|(\d{9} \d{3})|(((GD[0-4])|(HA[5-9]))[0-9]{2}))$/.test(
            str
        ),
    UZ: (str) => /^(UZ)?\d{9}$/.test(str),

    /*VAT numbers of Latin American countries*/
    AR: (str) => /^(AR)?\d{11}$/.test(str),
    BO: (str) => /^(BO)?\d{7}$/.test(str),
    BR: (str) =>
        /^(BR)?((\d{2}.\d{3}.\d{3}\/\d{4}-\d{2})|(\d{3}.\d{3}.\d{3}-\d{2}))$/.test(
            str
        ),
    CL: (str) => /^(CL)?\d{8}-\d{1}$/.test(str),
    CO: (str) => /^(CO)?\d{10}$/.test(str),
    CR: (str) => /^(CR)?\d{9,12}$/.test(str),
    EC: (str) => /^(EC)?\d{13}$/.test(str),
    SV: (str) => /^(SV)?\d{4}-\d{6}-\d{3}-\d{1}$/.test(str),
    GT: (str) => /^(GT)?\d{7}-\d{1}$/.test(str),
    HN: (str) => /^(HN)?$/.test(str),
    MX: (str) => /^(MX)?\w{3,4}\d{6}\w{3}$/.test(str),
    NI: (str) => /^(NI)?\w{3}-\w{6}-\w{4}$\w{1}/.test(str),
    PA: (str) => /^(PA)?$/.test(str),
    PY: (str) => /^(PY)?\d{6,8}-\d{1}$/.test(str),
    PE: (str) => /^(PE)?\d{11}$/.test(str),
    DO: (str) =>
        /^(DO)?(\d{11}|(\d{3}-\d{7}-\d{1})|\d{9}|([1,4,5]{1})-\d{2}-\d{5}-\d{1})$/.test(
            str
        ),
    UY: (str) => /^(UY)?\d{12}$/.test(str),
    VE: (str) => /^(VE)?[J,G,V,E]{1}-(\d{9}|(\d{8}-\d{1}))$/.test(str),
};

export default function isVAT(str, countryCode) {
    assertString(str);
    assertString(countryCode);

    if (countryCode in vatMatchers) {
        return vatMatchers[countryCode](str);
    }
    throw new Error(`Invalid country code: '${countryCode}'`);
}
rubiin commented 2 years ago

would you like to create a PR?

carkod commented 1 year ago

Looks like this is done already.

WikiRik commented 1 year ago

Yup, this was done in #1989

rubiin commented 1 year ago

Closing in favor of the PR https://github.com/validatorjs/validator.js/pull/1989