Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -13865,18 +13865,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