Skip to content

Commit 698f4e6

Browse files
authoredMar 27, 2023
fix(isVAT): corrected validation for Swiss (CH) locale (#2203)
* correct isVAT regex for CH * use checkdigit for swiss isvat * allow space as number separator, edit test cases * fix: correct test-case, edit comment * extract vat matcher for Switzerland (CH) - adjust number separation rule - edit comments in test
1 parent 9e73a1c commit 698f4e6

File tree

2 files changed

+40
-13
lines changed

2 files changed

+40
-13
lines changed
 

‎src/lib/isVAT.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
import assertString from './util/assertString';
22
import * as algorithms from './util/algorithms';
33

4+
const CH = (str) => {
5+
// @see {@link https://www.ech.ch/de/ech/ech-0097/5.2.0}
6+
const hasValidCheckNumber = (digits) => {
7+
const lastDigit = digits.pop(); // used as check number
8+
const weights = [5, 4, 3, 2, 7, 6, 5, 4];
9+
const calculatedCheckNumber = (11 - (digits.reduce((acc, el, idx) =>
10+
acc + (el * weights[idx]), 0) % 11)) % 11;
11+
12+
return lastDigit === calculatedCheckNumber;
13+
};
14+
15+
// @see {@link https://www.estv.admin.ch/estv/de/home/mehrwertsteuer/uid/mwst-uid-nummer.html}
16+
return /^(CHE[- ]?)?(\d{9}|(\d{3}\.\d{3}\.\d{3})|(\d{3} \d{3} \d{3})) ?(TVA|MWST|IVA)?$/.test(str) && hasValidCheckNumber((str.match(/\d/g).map(el => +el)));
17+
};
18+
419
const PT = (str) => {
520
const match = str.match(/^(PT)?(\d{9})$/);
621
if (!match) {
@@ -69,7 +84,7 @@ export const vatMatchers = {
6984
SM: str => /^(SM)?\d{5}$/.test(str),
7085
SA: str => /^(SA)?\d{15}$/.test(str),
7186
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),
87+
CH,
7388
TR: str => /^(TR)?\d{10}$/.test(str),
7489
UA: str => /^(UA)?\d{12}$/.test(str),
7590
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),

‎test/validators.test.js

+24-12
Original file line numberDiff line numberDiff line change
@@ -13891,18 +13891,30 @@ describe('Validators', () => {
1389113891
validator: 'isVAT',
1389213892
args: ['CH'],
1389313893
valid: [
13894-
'CH123456TVA',
13895-
'123456TVA',
13896-
'CH123456789MWST',
13897-
'123456789MWST',
13898-
'CH123.456IVA',
13899-
'123.456IVA',
13900-
'CH123.456.789TVA',
13901-
'123.456.789TVA',
13902-
],
13903-
invalid: [
13904-
'CH 123456',
13905-
'12345',
13894+
// strictly valid
13895+
'CHE-116.281.710 MWST',
13896+
'CHE-116.281.710 IVA',
13897+
'CHE-116.281.710 TVA',
13898+
// loosely valid presentation variants
13899+
'CHE 116 281 710 IVA', // all separators are spaces
13900+
'CHE-191.398.369MWST', // no space before suffix
13901+
'CHE-116281710 MWST', // no number separators
13902+
'CHE-116281710MWST', // no number separators and no space before suffix
13903+
'CHE105854263MWST', // no separators
13904+
'CHE-116.285.524', // no suffix (vat abbreviation)
13905+
'CHE116281710', // no suffix and separators
13906+
'116.281.710 TVA', // no prefix (CHE, ISO-3166-1 Alpha-3)
13907+
'116281710MWST', // no prefix and separators
13908+
'100.218.485', // no prefix and suffix
13909+
'123456788', // no prefix, separators and suffix
13910+
],
13911+
invalid: [
13912+
'CH-116.281.710 MWST', // invalid prefix (should be CHE)
13913+
'CHE-116.281 MWST', // invalid number of digits (should be 9)
13914+
'CHE-123.456.789 MWST', // invalid last digit (should match the calculated check-number 8)
13915+
'CHE-123.356.780 MWST', // invalid check-number (there are no swiss UIDs with the calculated check number 10)
13916+
'CH-116.281.710 VAT', // invalid suffix (should be MWST, IVA or TVA)
13917+
'CHE-116/281/710 IVA', // invalid number separators (should be all dots or all spaces)
1390613918
],
1390713919
});
1390813920
test({

0 commit comments

Comments
 (0)
Please sign in to comment.