Skip to content

Commit

Permalink
fix(isVAT): corrected validation for Swiss (CH) locale (#2203)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jimmyorpheus authored Mar 27, 2023
1 parent 9e73a1c commit 698f4e6
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 13 deletions.
17 changes: 16 additions & 1 deletion src/lib/isVAT.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import assertString from './util/assertString';
import * as algorithms from './util/algorithms';

const CH = (str) => {
// @see {@link https://www.ech.ch/de/ech/ech-0097/5.2.0}
const hasValidCheckNumber = (digits) => {
const lastDigit = digits.pop(); // used as check number
const weights = [5, 4, 3, 2, 7, 6, 5, 4];
const calculatedCheckNumber = (11 - (digits.reduce((acc, el, idx) =>
acc + (el * weights[idx]), 0) % 11)) % 11;

return lastDigit === calculatedCheckNumber;
};

// @see {@link https://www.estv.admin.ch/estv/de/home/mehrwertsteuer/uid/mwst-uid-nummer.html}
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)));
};

const PT = (str) => {
const match = str.match(/^(PT)?(\d{9})$/);
if (!match) {
Expand Down Expand Up @@ -69,7 +84,7 @@ export const vatMatchers = {
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),
CH,
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),
Expand Down
36 changes: 24 additions & 12 deletions test/validators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13891,18 +13891,30 @@ describe('Validators', () => {
validator: 'isVAT',
args: ['CH'],
valid: [
'CH123456TVA',
'123456TVA',
'CH123456789MWST',
'123456789MWST',
'CH123.456IVA',
'123.456IVA',
'CH123.456.789TVA',
'123.456.789TVA',
],
invalid: [
'CH 123456',
'12345',
// strictly valid
'CHE-116.281.710 MWST',
'CHE-116.281.710 IVA',
'CHE-116.281.710 TVA',
// loosely valid presentation variants
'CHE 116 281 710 IVA', // all separators are spaces
'CHE-191.398.369MWST', // no space before suffix
'CHE-116281710 MWST', // no number separators
'CHE-116281710MWST', // no number separators and no space before suffix
'CHE105854263MWST', // no separators
'CHE-116.285.524', // no suffix (vat abbreviation)
'CHE116281710', // no suffix and separators
'116.281.710 TVA', // no prefix (CHE, ISO-3166-1 Alpha-3)
'116281710MWST', // no prefix and separators
'100.218.485', // no prefix and suffix
'123456788', // no prefix, separators and suffix
],
invalid: [
'CH-116.281.710 MWST', // invalid prefix (should be CHE)
'CHE-116.281 MWST', // invalid number of digits (should be 9)
'CHE-123.456.789 MWST', // invalid last digit (should match the calculated check-number 8)
'CHE-123.356.780 MWST', // invalid check-number (there are no swiss UIDs with the calculated check number 10)
'CH-116.281.710 VAT', // invalid suffix (should be MWST, IVA or TVA)
'CHE-116/281/710 IVA', // invalid number separators (should be all dots or all spaces)
],
});
test({
Expand Down

0 comments on commit 698f4e6

Please sign in to comment.