|
1 | 1 | import assertString from './util/assertString'; |
| 2 | +import * as algorithms from './util/algorithms'; |
| 3 | + |
| 4 | +const PT = (str) => { |
| 5 | + const match = str.match(/^(PT)?(\d{9})$/); |
| 6 | + if (!match) { |
| 7 | + return false; |
| 8 | + } |
| 9 | + |
| 10 | + const tin = match[2]; |
| 11 | + |
| 12 | + const checksum = 11 - (algorithms.reverseMultiplyAndSum(tin.split('').slice(0, 8).map(a => parseInt(a, 10)), 9) % 11); |
| 13 | + if (checksum > 9) { |
| 14 | + return parseInt(tin[8], 10) === 0; |
| 15 | + } |
| 16 | + return checksum === parseInt(tin[8], 10); |
| 17 | +}; |
2 | 18 |
|
3 | 19 | export const vatMatchers = { |
4 | | - GB: /^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}))$/, |
5 | | - IT: /^(IT)?[0-9]{11}$/, |
6 | | - NL: /^(NL)?[0-9]{9}B[0-9]{2}$/, |
| 20 | + /** |
| 21 | + * European Union VAT identification numbers |
| 22 | + */ |
| 23 | + AT: str => /^(AT)?U\d{8}$/.test(str), |
| 24 | + BE: str => /^(BE)?\d{10}$/.test(str), |
| 25 | + BG: str => /^(BG)?\d{9,10}$/.test(str), |
| 26 | + HR: str => /^(HR)?\d{11}$/.test(str), |
| 27 | + CY: str => /^(CY)?\w{9}$/.test(str), |
| 28 | + CZ: str => /^(CZ)?\d{8,10}$/.test(str), |
| 29 | + DK: str => /^(DK)?\d{8}$/.test(str), |
| 30 | + EE: str => /^(EE)?\d{9}$/.test(str), |
| 31 | + FI: str => /^(FI)?\d{8}$/.test(str), |
| 32 | + FR: str => /^(FR)?\w{2}\d{9}$/.test(str), |
| 33 | + DE: str => /^(DE)?\d{9}$/.test(str), |
| 34 | + EL: str => /^(EL)?\d{9}$/.test(str), |
| 35 | + HU: str => /^(HU)?\d{8}$/.test(str), |
| 36 | + IE: str => /^(IE)?\d{7}\w{1}(W)?$/.test(str), |
| 37 | + IT: str => /^(IT)?\d{11}$/.test(str), |
| 38 | + LV: str => /^(LV)?\d{11}$/.test(str), |
| 39 | + LT: str => /^(LT)?\d{9,12}$/.test(str), |
| 40 | + LU: str => /^(LU)?\d{8}$/.test(str), |
| 41 | + MT: str => /^(MT)?\d{8}$/.test(str), |
| 42 | + NL: str => /^(NL)?\d{9}B\d{2}$/.test(str), |
| 43 | + PL: str => /^(PL)?(\d{10}|(\d{3}-\d{3}-\d{2}-\d{2})|(\d{3}-\d{2}-\d{2}-\d{3}))$/.test(str), |
| 44 | + PT, |
| 45 | + RO: str => /^(RO)?\d{2,10}$/.test(str), |
| 46 | + SK: str => /^(SK)?\d{10}$/.test(str), |
| 47 | + SI: str => /^(SI)?\d{8}$/.test(str), |
| 48 | + ES: str => /^(ES)?\w\d{7}[A-Z]$/.test(str), |
| 49 | + SE: str => /^(SE)?\d{12}$/.test(str), |
| 50 | + |
| 51 | + /** |
| 52 | + * VAT numbers of non-EU countries |
| 53 | + */ |
| 54 | + AL: str => /^(AL)?\w{9}[A-Z]$/.test(str), |
| 55 | + MK: str => /^(MK)?\d{13}$/.test(str), |
| 56 | + AU: str => /^(AU)?\d{11}$/.test(str), |
| 57 | + BY: str => /^(УНП )?\d{9}$/.test(str), |
| 58 | + CA: str => /^(CA)?\d{9}$/.test(str), |
| 59 | + IS: str => /^(IS)?\d{5,6}$/.test(str), |
| 60 | + IN: str => /^(IN)?\d{15}$/.test(str), |
| 61 | + ID: str => /^(ID)?(\d{15}|(\d{2}.\d{3}.\d{3}.\d{1}-\d{3}.\d{3}))$/.test(str), |
| 62 | + IL: str => /^(IL)?\d{9}$/.test(str), |
| 63 | + KZ: str => /^(KZ)?\d{9}$/.test(str), |
| 64 | + NZ: str => /^(NZ)?\d{9}$/.test(str), |
| 65 | + NG: str => /^(NG)?(\d{12}|(\d{8}-\d{4}))$/.test(str), |
| 66 | + NO: str => /^(NO)?\d{9}MVA$/.test(str), |
| 67 | + PH: str => /^(PH)?(\d{12}|\d{3} \d{3} \d{3} \d{3})$/.test(str), |
| 68 | + RU: str => /^(RU)?(\d{10}|\d{12})$/.test(str), |
| 69 | + SM: str => /^(SM)?\d{5}$/.test(str), |
| 70 | + SA: str => /^(SA)?\d{15}$/.test(str), |
| 71 | + RS: str => /^(RS)?\d{9}$/.test(str), |
| 72 | + CH: str => /^(CH)?(\d{6}|\d{9}|(\d{3}.\d{3})|(\d{3}.\d{3}.\d{3}))(TVA|MWST|IVA)$/.test(str), |
| 73 | + TR: str => /^(TR)?\d{10}$/.test(str), |
| 74 | + UA: str => /^(UA)?\d{12}$/.test(str), |
| 75 | + 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), |
| 76 | + UZ: str => /^(UZ)?\d{9}$/.test(str), |
| 77 | + |
| 78 | + /** |
| 79 | + * VAT numbers of Latin American countries |
| 80 | + */ |
| 81 | + AR: str => /^(AR)?\d{11}$/.test(str), |
| 82 | + BO: str => /^(BO)?\d{7}$/.test(str), |
| 83 | + BR: str => /^(BR)?((\d{2}.\d{3}.\d{3}\/\d{4}-\d{2})|(\d{3}.\d{3}.\d{3}-\d{2}))$/.test(str), |
| 84 | + CL: str => /^(CL)?\d{8}-\d{1}$/.test(str), |
| 85 | + CO: str => /^(CO)?\d{10}$/.test(str), |
| 86 | + CR: str => /^(CR)?\d{9,12}$/.test(str), |
| 87 | + EC: str => /^(EC)?\d{13}$/.test(str), |
| 88 | + SV: str => /^(SV)?\d{4}-\d{6}-\d{3}-\d{1}$/.test(str), |
| 89 | + GT: str => /^(GT)?\d{7}-\d{1}$/.test(str), |
| 90 | + HN: str => /^(HN)?$/.test(str), |
| 91 | + MX: str => /^(MX)?\w{3,4}\d{6}\w{3}$/.test(str), |
| 92 | + NI: str => /^(NI)?\d{3}-\d{6}-\d{4}\w{1}$/.test(str), |
| 93 | + PA: str => /^(PA)?$/.test(str), |
| 94 | + PY: str => /^(PY)?\d{6,8}-\d{1}$/.test(str), |
| 95 | + PE: str => /^(PE)?\d{11}$/.test(str), |
| 96 | + DO: str => /^(DO)?(\d{11}|(\d{3}-\d{7}-\d{1})|[1,4,5]{1}\d{8}|([1,4,5]{1})-\d{2}-\d{5}-\d{1})$/.test(str), |
| 97 | + UY: str => /^(UY)?\d{12}$/.test(str), |
| 98 | + VE: str => /^(VE)?[J,G,V,E]{1}-(\d{9}|(\d{8}-\d{1}))$/.test(str), |
7 | 99 | }; |
8 | 100 |
|
9 | 101 | export default function isVAT(str, countryCode) { |
10 | 102 | assertString(str); |
11 | 103 | assertString(countryCode); |
12 | 104 |
|
13 | 105 | if (countryCode in vatMatchers) { |
14 | | - return vatMatchers[countryCode].test(str); |
| 106 | + return vatMatchers[countryCode](str); |
15 | 107 | } |
16 | 108 | throw new Error(`Invalid country code: '${countryCode}'`); |
17 | 109 | } |
0 commit comments