diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1bd45a8..f041921 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,18 +17,18 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' - + - name: Install dependencies run: npm install - + - name: Check types run: npm run type-check - + - name: Check linting run: npm run lint - + - name: Run tests run: npm run test:ci - + - name: Build package run: yarn build diff --git a/src/country.ts b/src/country.ts index d9bf824..0ee4925 100644 --- a/src/country.ts +++ b/src/country.ts @@ -1,77 +1,77 @@ export enum Country { - Albania = "Albania", - Argentina = "Argentina", - Australia = "Australia", - Austria = "Austria", - Belarus = "Belarus", - Belgium = "Belgium", - Belize = "Belize", - Bolivia = "Bolivia", - BosniaHerzegovina = "Bosnia Herzegovina", - Brazil = "Brazil", - Bulgaria = "Bulgaria", - Canada = "Canada", - Chile = "Chile", - China = "China", - Colombia = "Colombia", - CostaRica = "Costa Rica", - Croatia = "Croatia", - Cyprus = "Cyprus", - CzechRepublic = "Czech Republic", - Denmark = "Denmark", - DominicanRepublic = "Dominican Republic", - Ecuador = "Ecuador", - ElSalvador = "El Salvador", - Estonia = "Estonia", - Finland = "Finland", - France = "France", - Germany = "Germany", - Greece = "Greece", - Guatemala = "Guatemala", - Honduras = "Honduras", - Hungary = "Hungary", - Iceland = "Iceland", - India = "India", - Indonesia = "Indonesia", - Ireland = "Ireland", - IsleOfMan = "Isle of Man", - Israel = "Israel", - Italy = "Italy", - Japan = "Japan", - Kazakhstan = "Kazakhstan", - Latvia = "Latvia", - Lithuania = "Lithuania", - Luxembourg = "Luxembourg", - Malta = "Malta", - Mexico = "Mexico", - Monaco = "Monaco", - Netherlands = "Netherlands", - NewZealand = "New Zealand", - Nicaragua = "Nicaragua", - Nigeria = "Nigeria", - NorthMacedonia = "North Macedonia", - Norway = "Norway", - Panama = "Panama", - Paraguay = "Paraguay", - Peru = "Peru", - Philippines = "Philippines", - Poland = "Poland", - Portugal = "Portugal", - Romania = "Romania", - Russia = "Russia", - SanMarino = "San Marino", - SaudiArabia = "Saudi Arabia", - Serbia = "Serbia", - Slovakia = "Slovakia", - Slovenia = "Slovenia", - Spain = "Spain", - Sweden = "Sweden", - Switzerland = "Switzerland", - Taiwan = "Taiwan", - Turkey = "Turkey", - Ukraine = "Ukraine", - UnitedKingdom = "United Kingdom", - Uruguay = "Uruguay", - Uzbekistan = "Uzbekistan", - Venezuela = "Venezuela" -} \ No newline at end of file + Albania = 'Albania', + Argentina = 'Argentina', + Australia = 'Australia', + Austria = 'Austria', + Belarus = 'Belarus', + Belgium = 'Belgium', + Belize = 'Belize', + Bolivia = 'Bolivia', + BosniaHerzegovina = 'Bosnia Herzegovina', + Brazil = 'Brazil', + Bulgaria = 'Bulgaria', + Canada = 'Canada', + Chile = 'Chile', + China = 'China', + Colombia = 'Colombia', + CostaRica = 'Costa Rica', + Croatia = 'Croatia', + Cyprus = 'Cyprus', + CzechRepublic = 'Czech Republic', + Denmark = 'Denmark', + DominicanRepublic = 'Dominican Republic', + Ecuador = 'Ecuador', + ElSalvador = 'El Salvador', + Estonia = 'Estonia', + Finland = 'Finland', + France = 'France', + Germany = 'Germany', + Greece = 'Greece', + Guatemala = 'Guatemala', + Honduras = 'Honduras', + Hungary = 'Hungary', + Iceland = 'Iceland', + India = 'India', + Indonesia = 'Indonesia', + Ireland = 'Ireland', + IsleOfMan = 'Isle of Man', + Israel = 'Israel', + Italy = 'Italy', + Japan = 'Japan', + Kazakhstan = 'Kazakhstan', + Latvia = 'Latvia', + Lithuania = 'Lithuania', + Luxembourg = 'Luxembourg', + Malta = 'Malta', + Mexico = 'Mexico', + Monaco = 'Monaco', + Netherlands = 'Netherlands', + NewZealand = 'New Zealand', + Nicaragua = 'Nicaragua', + Nigeria = 'Nigeria', + NorthMacedonia = 'North Macedonia', + Norway = 'Norway', + Panama = 'Panama', + Paraguay = 'Paraguay', + Peru = 'Peru', + Philippines = 'Philippines', + Poland = 'Poland', + Portugal = 'Portugal', + Romania = 'Romania', + Russia = 'Russia', + SanMarino = 'San Marino', + SaudiArabia = 'Saudi Arabia', + Serbia = 'Serbia', + Slovakia = 'Slovakia', + Slovenia = 'Slovenia', + Spain = 'Spain', + Sweden = 'Sweden', + Switzerland = 'Switzerland', + Taiwan = 'Taiwan', + Turkey = 'Turkey', + Ukraine = 'Ukraine', + UnitedKingdom = 'United Kingdom', + Uruguay = 'Uruguay', + Uzbekistan = 'Uzbekistan', + Venezuela = 'Venezuela', +} diff --git a/src/countryRegex.ts b/src/countryRegex.ts index c894558..e05c872 100644 --- a/src/countryRegex.ts +++ b/src/countryRegex.ts @@ -6,7 +6,10 @@ export const countryRegexMap: Map = new Map([ [Country.Australia, String.raw`^\d{11}$`], [Country.Austria, String.raw`^ATU\d{8}$`], [Country.Belarus, String.raw`^\d{9}$`], - [Country.Belgium, String.raw`^(BE0?|BE1)\d{8}(?:(?<=\d)(\d{2})|(?<=\d)(\d{1})X)$`], + [ + Country.Belgium, + String.raw`^(BE0?|BE1)\d{8}(?:(?<=\d)(\d{2})|(?<=\d)(\d{1})X)$`, + ], [Country.Belize, String.raw`^\d{6}$`], [Country.Bolivia, String.raw`^\d{7}$`], [Country.BosniaHerzegovina, String.raw`^\d{12,13}$`], @@ -35,7 +38,10 @@ export const countryRegexMap: Map = new Map([ [Country.India, String.raw`^[a-zA-Z0-9]{15}$`], [Country.Indonesia, String.raw`^\d{15,16}$`], [Country.Ireland, String.raw`^IE(\d{7}[A-Z]|(\d{7}[A-Z]{2}))$`], - [Country.IsleOfMan, String.raw`^GB(\d{3} \d{4} \d{2}|\d{9}|\d{9} \d{3}|GD([0-4][0-9]{2})|HA([5-9][0-9]{2}))$`], + [ + Country.IsleOfMan, + String.raw`^GB(\d{3} \d{4} \d{2}|\d{9}|\d{9} \d{3}|GD([0-4][0-9]{2})|HA([5-9][0-9]{2}))$`, + ], [Country.Israel, String.raw`^\d{9}$`], [Country.Italy, String.raw`^IT\d{11}$`], [Country.Japan, String.raw`^\d{13}$`], @@ -55,7 +61,10 @@ export const countryRegexMap: Map = new Map([ [Country.Paraguay, String.raw`^\d{6,8}-\d$`], [Country.Peru, String.raw`^\d{11}$`], [Country.Philippines, String.raw`^\d{12}$`], - [Country.Poland, String.raw`^PL(?:\d{3}-\d{3}-\d{2}-\d{2}|\d{3}-\d{2}-\d{2}-\d{3}|\d{10})$`], + [ + Country.Poland, + String.raw`^PL(?:\d{3}-\d{3}-\d{2}-\d{2}|\d{3}-\d{2}-\d{2}-\d{3}|\d{10})$`, + ], [Country.Portugal, String.raw`^PT\d{9}$`], [Country.Romania, String.raw`^RO\d{2,10}$`], [Country.Russia, String.raw`^\d{10}(?:\d{2})?$`], @@ -66,11 +75,17 @@ export const countryRegexMap: Map = new Map([ [Country.Slovenia, String.raw`^SI\d{8}$`], [Country.Spain, String.raw`^ES[A-Z]\d{7}[A-Z0-9]$`], [Country.Sweden, String.raw`^SE\d{12}$`], - [Country.Switzerland, String.raw`^CHE-\d{3}\.\d{3}\.\d{3}\s(?:MWST|TVA|IVA)$`], + [ + Country.Switzerland, + String.raw`^CHE-\d{3}\.\d{3}\.\d{3}\s(?:MWST|TVA|IVA)$`, + ], [Country.Taiwan, String.raw`^\d{8}$`], [Country.Turkey, String.raw`^\d{10}$`], [Country.Ukraine, String.raw`^\d{12}$`], - [Country.UnitedKingdom, String.raw`^GB(\d{3} \d{4} \d{2}|\d{9}|\d{9} \d{3}|GD([0-4][0-9]{2})|HA([5-9][0-9]{2}))$`], + [ + Country.UnitedKingdom, + String.raw`^GB(\d{3} \d{4} \d{2}|\d{9}|\d{9} \d{3}|GD([0-4][0-9]{2})|HA([5-9][0-9]{2}))$`, + ], [Country.Uruguay, String.raw`^\d{8}001\d$`], [Country.Uzbekistan, String.raw`^[24-7]\d{8}$`], [Country.Venezuela, String.raw`^(J|G|V|E)-\d{9}$`], diff --git a/src/specialCaseValidator.ts b/src/specialCaseValidator.ts index 0a181ee..f2312e3 100644 --- a/src/specialCaseValidator.ts +++ b/src/specialCaseValidator.ts @@ -1,464 +1,469 @@ import { Country } from './country'; -import { countryRegexMap } from "./countryRegex"; +import { countryRegexMap } from './countryRegex'; function validateFrenchVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.France); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.France); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const validationKey = Number(vat.substring(2, 4)); - const siren = vat.substring(4); - const calculatedKey = (12 + 3 * (Number(siren) % 97)) % 97; + const validationKey = Number(vat.substring(2, 4)); + const siren = vat.substring(4); + const calculatedKey = (12 + 3 * (Number(siren) % 97)) % 97; - return validationKey === calculatedKey; + return validationKey === calculatedKey; } function validateItalianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Italy); - const vatRegex = new RegExp(regex as string); - - if (!vatRegex.test(vat)) { - return false; - } + const regex = countryRegexMap.get(Country.Italy); + const vatRegex = new RegExp(regex as string); - const digits = vat.substring(2).split('').map(Number); + if (!vatRegex.test(vat)) { + return false; + } - digits.reverse(); + const digits = vat.substring(2).split('').map(Number); - let sum = 0; + digits.reverse(); - for (let i = 0; i < digits.length; i++) { - let digit = digits[i]; + let sum = 0; - if (i % 2 === 1) { - digit *= 2; - if (digit > 9) { - digit -= 9; - } - } + for (let i = 0; i < digits.length; i++) { + let digit = digits[i]; - sum += digit; + if (i % 2 === 1) { + digit *= 2; + if (digit > 9) { + digit -= 9; + } } - return sum % 10 === 0; + sum += digit; + } + + return sum % 10 === 0; } function validateSlovakianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Slovakia); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Slovakia); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const num = BigInt(vat.substring(2)); + const num = BigInt(vat.substring(2)); - return num % BigInt(11) === BigInt(0); + return num % BigInt(11) === BigInt(0); } function validateAustralianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Australia); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Australia); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const identifier = vat.substring(0, 9); - const checkDigits = vat.substring(9); - let total = 0; + const identifier = vat.substring(0, 9); + const checkDigits = vat.substring(9); + let total = 0; - for (let i = 0; i < identifier.length; i++) { - const digit = parseInt(identifier[i]); - total = (total * 10 + digit) % 89; - } + for (let i = 0; i < identifier.length; i++) { + const digit = parseInt(identifier[i]); + total = (total * 10 + digit) % 89; + } - const calculatedCheckDigits = total.toString().padStart(2, '0'); + const calculatedCheckDigits = total.toString().padStart(2, '0'); - return calculatedCheckDigits === checkDigits; + return calculatedCheckDigits === checkDigits; } function validateIsraeliVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Israel); - const vatRegex = new RegExp(regex as string); - - if (!vatRegex.test(vat)) { - return false; - } + const regex = countryRegexMap.get(Country.Israel); + const vatRegex = new RegExp(regex as string); - const digits = vat.split('').map(Number); - let sum = 0; + if (!vatRegex.test(vat)) { + return false; + } - for (let i = 0; i < 9; i++) { - let digit = digits[i]; + const digits = vat.split('').map(Number); + let sum = 0; - if (i % 2 === 1) { - digit *= 2; + for (let i = 0; i < 9; i++) { + let digit = digits[i]; - if (digit > 9) { - digit -= 9; - } - } + if (i % 2 === 1) { + digit *= 2; - sum += digit; + if (digit > 9) { + digit -= 9; + } } - return sum % 10 === 0; + sum += digit; + } + + return sum % 10 === 0; } function validateNorwegianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Norway); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Norway); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const digits = vat.split('').map(Number); - let sum = 0; + const digits = vat.split('').map(Number); + let sum = 0; - for (let i = 0; i < 8; i++) { - sum += digits[i] * [3, 2, 7, 6, 5, 4, 3, 2][i]; - } + for (let i = 0; i < 8; i++) { + sum += digits[i] * [3, 2, 7, 6, 5, 4, 3, 2][i]; + } - const remainder = sum % 11; - const checkDigit = remainder <= 1 ? 0 : 11 - remainder; + const remainder = sum % 11; + const checkDigit = remainder <= 1 ? 0 : 11 - remainder; - return checkDigit === digits[8]; + return checkDigit === digits[8]; } function validateFilipinoVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Philippines); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Philippines); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const sequentialDigits = vat.substring(1, 8); + const sequentialDigits = vat.substring(1, 8); - for (let i = 1; i < 7; i++) { - if (parseInt(sequentialDigits[i]) !== parseInt(sequentialDigits[i - 1]) + 1) { - return false; - } + for (let i = 1; i < 7; i++) { + if ( + parseInt(sequentialDigits[i]) !== + parseInt(sequentialDigits[i - 1]) + 1 + ) { + return false; } + } - return true; + return true; } function validateSerbianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Serbia); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Serbia); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } - - const digits = vat.split('').map(Number); - let sum = 10; + if (!vatRegex.test(vat)) { + return false; + } - for (let i = 0; i < 8; i++) { - sum += digits[i]; - sum %= 10; + const digits = vat.split('').map(Number); + let sum = 10; - if (sum === 0) { - sum = 10; - } + for (let i = 0; i < 8; i++) { + sum += digits[i]; + sum %= 10; - sum *= 2; - sum %= 11; + if (sum === 0) { + sum = 10; } - const expectedChecksum = (11 - sum) % 10; + sum *= 2; + sum %= 11; + } - return expectedChecksum === digits[8]; + const expectedChecksum = (11 - sum) % 10; + + return expectedChecksum === digits[8]; } function validateSwissVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Switzerland); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Switzerland); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const digits = vat.substring(0, 8).split('').map(Number); - let weightedSum = 0; + const digits = vat.substring(0, 8).split('').map(Number); + let weightedSum = 0; - for (let i = 0; i < 8; i++) { - weightedSum += digits[i] * [5, 4, 3, 2, 7, 6, 5, 4][i]; - } + for (let i = 0; i < 8; i++) { + weightedSum += digits[i] * [5, 4, 3, 2, 7, 6, 5, 4][i]; + } - const remainder = weightedSum % 11; - const checksum = remainder <= 1 ? 0 : 11 - remainder; - const vatChecksum = parseInt(vat.charAt(8)); + const remainder = weightedSum % 11; + const checksum = remainder <= 1 ? 0 : 11 - remainder; + const vatChecksum = parseInt(vat.charAt(8)); - return checksum === vatChecksum; + return checksum === vatChecksum; } function validateTaiwaneseVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Taiwan); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Taiwan); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const digits = vat.split('').map(Number); - const weights = [1, 2, 1, 2, 1, 2, 4, 1]; - let sum = 0; + const digits = vat.split('').map(Number); + const weights = [1, 2, 1, 2, 1, 2, 4, 1]; + let sum = 0; - for (let i = 0; i < 8; i++) { - const product = digits[i] * weights[i]; - if (product >= 10) { - sum += Math.floor(product / 10) + (product % 10); - } else { - sum += product; - } + for (let i = 0; i < 8; i++) { + const product = digits[i] * weights[i]; + if (product >= 10) { + sum += Math.floor(product / 10) + (product % 10); + } else { + sum += product; } + } - return sum % 10 === 0; + return sum % 10 === 0; } function validateBrazilianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Brazil); - const vatRegex = new RegExp(regex as string); - - if (!vatRegex.test(vat)) { - return false; - } + const regex = countryRegexMap.get(Country.Brazil); + const vatRegex = new RegExp(regex as string); - if (vat.length === 14) { - const weightsFirstDigit = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; - const weightsSecondDigit = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; - let sum = 0; - - for (let i = 0; i < 12; i++) { - sum += parseInt(vat.charAt(i)) * weightsFirstDigit[i]; - } + if (!vatRegex.test(vat)) { + return false; + } - let remainder = sum % 11; - const firstCheckDigit = remainder <= 1 ? 0 : 11 - remainder; + if (vat.length === 14) { + const weightsFirstDigit = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; + const weightsSecondDigit = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; + let sum = 0; - if (parseInt(vat.charAt(12)) !== firstCheckDigit) { - return false; - } + for (let i = 0; i < 12; i++) { + sum += parseInt(vat.charAt(i)) * weightsFirstDigit[i]; + } - sum = 0; + let remainder = sum % 11; + const firstCheckDigit = remainder <= 1 ? 0 : 11 - remainder; - for (let i = 0; i < 13; i++) { - sum += parseInt(vat.charAt(i)) * weightsSecondDigit[i]; - } + if (parseInt(vat.charAt(12)) !== firstCheckDigit) { + return false; + } - remainder = sum % 11; - const secondCheckDigit = remainder <= 1 ? 0 : 11 - remainder; + sum = 0; - return parseInt(vat.charAt(13)) === secondCheckDigit; + for (let i = 0; i < 13; i++) { + sum += parseInt(vat.charAt(i)) * weightsSecondDigit[i]; } - if (vat.length === 11) { - const weightsFirstDigit = [10, 9, 8, 7, 6, 5, 4, 3, 2]; - const weightsSecondDigit = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]; - let sum = 0; + remainder = sum % 11; + const secondCheckDigit = remainder <= 1 ? 0 : 11 - remainder; - for (let i = 0; i < 9; i++) { - sum += parseInt(vat.charAt(i)) * weightsFirstDigit[i]; - } + return parseInt(vat.charAt(13)) === secondCheckDigit; + } - let remainder = sum % 11; - const firstCheckDigit = remainder <= 1 ? 0 : 11 - remainder; + if (vat.length === 11) { + const weightsFirstDigit = [10, 9, 8, 7, 6, 5, 4, 3, 2]; + const weightsSecondDigit = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]; + let sum = 0; - if (parseInt(vat.charAt(9)) !== firstCheckDigit) { - return false; - } + for (let i = 0; i < 9; i++) { + sum += parseInt(vat.charAt(i)) * weightsFirstDigit[i]; + } - sum = 0; + let remainder = sum % 11; + const firstCheckDigit = remainder <= 1 ? 0 : 11 - remainder; - for (let i = 0; i < 10; i++) { - sum += parseInt(vat.charAt(i)) * weightsSecondDigit[i]; - } + if (parseInt(vat.charAt(9)) !== firstCheckDigit) { + return false; + } - remainder = sum % 11; - const secondCheckDigit = remainder <= 1 ? 0 : 11 - remainder; + sum = 0; - return parseInt(vat.charAt(10)) === secondCheckDigit; + for (let i = 0; i < 10; i++) { + sum += parseInt(vat.charAt(i)) * weightsSecondDigit[i]; } - return false; + remainder = sum % 11; + const secondCheckDigit = remainder <= 1 ? 0 : 11 - remainder; + + return parseInt(vat.charAt(10)) === secondCheckDigit; + } + + return false; } function validateChileanVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Chile); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Chile); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const checkDigit = vat.charAt(9); - const digits = vat.slice(0, 8); + const checkDigit = vat.charAt(9); + const digits = vat.slice(0, 8); - const weights = [3, 2, 7, 6, 5, 4, 3, 2]; - let sum = 0; + const weights = [3, 2, 7, 6, 5, 4, 3, 2]; + let sum = 0; - for (let i = 0; i < digits.length; i++) { - sum += parseInt(digits.charAt(i)) * weights[i]; - } + for (let i = 0; i < digits.length; i++) { + sum += parseInt(digits.charAt(i)) * weights[i]; + } - const remainder = sum % 11; - const calculatedCheckDigit = 11 - remainder; + const remainder = sum % 11; + const calculatedCheckDigit = 11 - remainder; - if (calculatedCheckDigit === 11) { - return checkDigit === '0'; - } else if (calculatedCheckDigit === 10) { - return checkDigit === 'K'; - } else { - return parseInt(checkDigit) === calculatedCheckDigit; - } + if (calculatedCheckDigit === 11) { + return checkDigit === '0'; + } else if (calculatedCheckDigit === 10) { + return checkDigit === 'K'; + } else { + return parseInt(checkDigit) === calculatedCheckDigit; + } } function validateColombianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Colombia); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Colombia); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const checkDigit = vat.charAt(10); - const digits = vat.slice(0, 9); + const checkDigit = vat.charAt(10); + const digits = vat.slice(0, 9); - const weights = [41, 37, 29, 23, 19, 17, 13, 7, 3]; - let sum = 0; + const weights = [41, 37, 29, 23, 19, 17, 13, 7, 3]; + let sum = 0; - for (let i = 0; i < digits.length; i++) { - sum += parseInt(digits.charAt(i)) * weights[i]; - } + for (let i = 0; i < digits.length; i++) { + sum += parseInt(digits.charAt(i)) * weights[i]; + } - const remainder = sum % 11; - const calculatedCheckDigit = (remainder == 0 || remainder == 1) ? remainder : 11 - remainder; - return parseInt(checkDigit) === calculatedCheckDigit; + const remainder = sum % 11; + const calculatedCheckDigit = + remainder == 0 || remainder == 1 ? remainder : 11 - remainder; + return parseInt(checkDigit) === calculatedCheckDigit; } function validateDominicanRepublicVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.DominicanRepublic); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.DominicanRepublic); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const checkDigit = vat.charAt(10); - const digits = vat.slice(0, 9); + const checkDigit = vat.charAt(10); + const digits = vat.slice(0, 9); - const weights = [7, 9, 8, 6, 5, 4, 3, 2]; - let sum = 0; + const weights = [7, 9, 8, 6, 5, 4, 3, 2]; + let sum = 0; - for (let i = 0; i < digits.length; i++) { - sum += parseInt(digits.charAt(i)) * weights[i]; - } + for (let i = 0; i < digits.length; i++) { + sum += parseInt(digits.charAt(i)) * weights[i]; + } - const remainder = sum % 11; - const calculatedCheckDigit = (10 - remainder) % 9 + 1; - return parseInt(checkDigit) === calculatedCheckDigit; + const remainder = sum % 11; + const calculatedCheckDigit = ((10 - remainder) % 9) + 1; + return parseInt(checkDigit) === calculatedCheckDigit; } function validateParaguayVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Paraguay); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Paraguay); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } + if (!vatRegex.test(vat)) { + return false; + } - const digitsOnly = vat.replace(/\D/g, ''); - const checkDigit = digitsOnly.charAt(digitsOnly.length - 1); - const digits = digitsOnly.slice(0, -1); - let sum = 0; + const digitsOnly = vat.replace(/\D/g, ''); + const checkDigit = digitsOnly.charAt(digitsOnly.length - 1); + const digits = digitsOnly.slice(0, -1); + let sum = 0; - for (let i = 0; i < digits.length; i++) { - sum += (i + 2) * parseInt(digits.charAt(digits.length - i - 1)); - } + for (let i = 0; i < digits.length; i++) { + sum += (i + 2) * parseInt(digits.charAt(digits.length - i - 1)); + } - const calculatedCheckDigit = String((-sum % 11) % 10); + const calculatedCheckDigit = String((-sum % 11) % 10); - return checkDigit === calculatedCheckDigit; + return checkDigit === calculatedCheckDigit; } function validatePeruvianVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Peru); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Peru); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } - - const number = vat.slice(0, 10); - const weights = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2]; - const weightedSum = weights.reduce((sum, weight, index) => { - const digit = parseInt(number.charAt(index)); - return sum + weight * digit; - }, 0); - const checkDigit = (11 - (weightedSum % 11)) % 10; - const calculatedCheckDigit = String(checkDigit); - const providedCheckDigit = vat.charAt(10); - - return calculatedCheckDigit === providedCheckDigit; + if (!vatRegex.test(vat)) { + return false; + } + + const number = vat.slice(0, 10); + const weights = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2]; + const weightedSum = weights.reduce((sum, weight, index) => { + const digit = parseInt(number.charAt(index)); + return sum + weight * digit; + }, 0); + const checkDigit = (11 - (weightedSum % 11)) % 10; + const calculatedCheckDigit = String(checkDigit); + const providedCheckDigit = vat.charAt(10); + + return calculatedCheckDigit === providedCheckDigit; } function validateUruguayanVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Uruguay); - const vatRegex = new RegExp(regex as string); + const regex = countryRegexMap.get(Country.Uruguay); + const vatRegex = new RegExp(regex as string); - if (!vatRegex.test(vat)) { - return false; - } - - const number = vat.slice(0, 11); - const weights = [4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; - const total = weights.reduce((sum, weight, index) => { - const digit = parseInt(number.charAt(index)); - return sum + digit * weight; - }, 0); - const calculatedCheckDigit = (-total % 11).toString(); - const providedCheckDigit = vat.charAt(11); - - return calculatedCheckDigit === providedCheckDigit; + if (!vatRegex.test(vat)) { + return false; + } + + const number = vat.slice(0, 11); + const weights = [4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; + const total = weights.reduce((sum, weight, index) => { + const digit = parseInt(number.charAt(index)); + return sum + digit * weight; + }, 0); + const calculatedCheckDigit = (-total % 11).toString(); + const providedCheckDigit = vat.charAt(11); + + return calculatedCheckDigit === providedCheckDigit; } function validateVenezuelanVat(vat: string): boolean { - const regex = countryRegexMap.get(Country.Venezuela); - const vatRegex = new RegExp(regex as string); - - if (!vatRegex.test(vat)) { - return false; - } - - const number = vat.slice(0, 10).replace(/[-\s]/g, ''); - const weights = [3, 2, 7, 6, 5, 4, 3, 2]; - const _companyTypes: { [key: string]: number } = { - 'V': 4, // natural person born in Venezuela - 'E': 8, // foreign natural person - 'J': 12, // company - 'P': 16, // passport - 'G': 20, // government - }; - const companyType = _companyTypes[number[0]]; - let sum = companyType; - for (let i = 0; i < 8; i++) { - sum += weights[i] * parseInt(number.charAt(i + 1)); - } + const regex = countryRegexMap.get(Country.Venezuela); + const vatRegex = new RegExp(regex as string); - const calculatedCheckDigit = '00987654321'[(sum % 11)]; - const providedCheckDigit = vat.charAt(10); - - return calculatedCheckDigit === providedCheckDigit; + if (!vatRegex.test(vat)) { + return false; + } + + const number = vat.slice(0, 10).replace(/[-\s]/g, ''); + const weights = [3, 2, 7, 6, 5, 4, 3, 2]; + const _companyTypes: { [key: string]: number } = { + V: 4, // natural person born in Venezuela + E: 8, // foreign natural person + J: 12, // company + P: 16, // passport + G: 20, // government + }; + const companyType = _companyTypes[number[0]]; + let sum = companyType; + for (let i = 0; i < 8; i++) { + sum += weights[i] * parseInt(number.charAt(i + 1)); + } + + const calculatedCheckDigit = '00987654321'[sum % 11]; + const providedCheckDigit = vat.charAt(10); + + return calculatedCheckDigit === providedCheckDigit; } -export const countrySpecialCaseMap: Map boolean> = new Map([ +export const countrySpecialCaseMap: Map boolean> = + new Map([ [Country.France, validateFrenchVat], [Country.Italy, validateItalianVat], [Country.Slovakia, validateSlovakianVat], @@ -478,4 +483,4 @@ export const countrySpecialCaseMap: Map boolean> = new [Country.Peru, validatePeruvianVat], [Country.Uruguay, validateUruguayanVat], [Country.Venezuela, validateVenezuelanVat], -]); + ]); diff --git a/src/vatValidator.ts b/src/vatValidator.ts index 32a84d1..eff9b13 100644 --- a/src/vatValidator.ts +++ b/src/vatValidator.ts @@ -2,12 +2,11 @@ import { Country } from './country'; import { countryRegexMap } from './countryRegex'; import { countrySpecialCaseMap } from './specialCaseValidator'; -export function isValidCountryVat( - vat: string, - country: Country -): boolean { +export function isValidCountryVat(vat: string, country: Country): boolean { if (countrySpecialCaseMap.has(country)) { - const specialCaseValidator = countrySpecialCaseMap.get(country) as (vat: string) => boolean; + const specialCaseValidator = countrySpecialCaseMap.get(country) as ( + vat: string + ) => boolean; return specialCaseValidator(vat); } diff --git a/test/postalCodeValidator.test.ts b/test/postalCodeValidator.test.ts index 3c524ad..88185e8 100644 --- a/test/postalCodeValidator.test.ts +++ b/test/postalCodeValidator.test.ts @@ -1,7 +1,4 @@ -import { - isValidCountryVat, - isValidVat, -} from '../src/vatValidator'; +import { isValidCountryVat, isValidVat } from '../src/vatValidator'; import { validVats, invalidVats, @@ -28,19 +25,13 @@ describe('isValidCountryVat', () => { }); describe('isValidVat', () => { - test.each(validVatsArray)( - 'should return true for %s', - (vat) => { - const result = isValidVat(vat); - expect(result).toBe(true); - } - ); + test.each(validVatsArray)('should return true for %s', (vat) => { + const result = isValidVat(vat); + expect(result).toBe(true); + }); - test.each(invalidVatsArray)( - 'should return false for %s', - (vat) => { - const result = isValidVat(vat); - expect(result).toBe(false); - } - ); + test.each(invalidVatsArray)('should return false for %s', (vat) => { + const result = isValidVat(vat); + expect(result).toBe(false); + }); }); diff --git a/test/utils.ts b/test/utils.ts index d872c86..7881dc5 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -56,15 +56,10 @@ export const validVats: { country: Country; vat: string }[] = [ { vat: 'J-305959918', country: Country.Venezuela }, ]; - export const invalidVats: { country: Country; vat: string }[] = [ { vat: '12345', country: Country.Albania }, ]; -export const validVatsArray = [ - '22150', -]; +export const validVatsArray = ['22150']; -export const invalidVatsArray = [ - 'abcdef', -]; +export const invalidVatsArray = ['abcdef'];