For a project, I need to validate a VAT number against the correct algorithm. I found a library for this here (freeware, so I should be able to reuse it) that I'm grateful exists. However, I feel like it could be improved upon in a few ways, but I'm not sure which.
The piece of code is:
// Check the string against the regular expressions for all types of VAT numbers
for (i = 0; i < vatexp.length; i++) {
// Have we recognised the VAT number?
if (vatexp[i].test(vatNumber)) {
// Yes - we have
var cCode = RegExp.$1; // Isolate country code
var cNumber = RegExp.$2; // Isolate the number
if (cCode.length == 0) cCode = defCCode; // Set up default country code
// Now perform the check digit calculation according to the national rules.
//switch (cCode) {
// case "AT":
// valid = ATVATCheckDigit(cNumber);
// break;
// case "BE":
// valid = BEVATCheckDigit(cNumber);
// break;
// case "BG":
// valid = BGVATCheckDigit(cNumber);
// break;
// case "CHE":
// valid = CHEVATCheckDigit(cNumber);
// break;
// case "CY":
// valid = CYVATCheckDigit(cNumber);
// break;
// case "CZ":
// valid = CZVATCheckDigit(cNumber);
// break;
// case "DE":
// valid = DEVATCheckDigit(cNumber);
// break;
// case "DK":
// valid = DKVATCheckDigit(cNumber);
// break;
// case "EE":
// valid = EEVATCheckDigit(cNumber);
// break;
// case "EL":
// valid = ELVATCheckDigit(cNumber);
// break;
// case "ES":
// valid = ESVATCheckDigit(cNumber);
// break;
// case "EU":
// valid = EUVATCheckDigit(cNumber);
// break;
// case "FI":
// valid = FIVATCheckDigit(cNumber);
// break;
// case "FR":
// valid = FRVATCheckDigit(cNumber);
// break;
// case "GB":
// valid = UKVATCheckDigit(cNumber);
// break;
// case "GR":
// valid = ELVATCheckDigit(cNumber);
// break;
// case "HR":
// valid = HRVATCheckDigit(cNumber);
// break;
// case "HU":
// valid = HUVATCheckDigit(cNumber);
// break;
// case "IE":
// valid = IEVATCheckDigit(cNumber);
// break;
// case "IT":
// valid = ITVATCheckDigit(cNumber);
// break;
// case "LT":
// valid = LTVATCheckDigit(cNumber);
// break;
// case "LU":
// valid = LUVATCheckDigit(cNumber);
// break;
// case "LV":
// valid = LVVATCheckDigit(cNumber);
// break;
// case "MT":
// valid = MTVATCheckDigit(cNumber);
// break;
// case "NL":
// valid = NLVATCheckDigit(cNumber);
// break;
// case "NO":
// valid = NOVATCheckDigit(cNumber);
// break;
// case "PL":
// valid = PLVATCheckDigit(cNumber);
// break;
// case "PT":
// valid = PTVATCheckDigit(cNumber);
// break;
// case "RO":
// valid = ROVATCheckDigit(cNumber);
// break;
// case "RS":
// valid = RSVATCheckDigit(cNumber);
// break;
// case "RU":
// valid = RUVATCheckDigit(cNumber);
// break;
// case "SE":
// valid = SEVATCheckDigit(cNumber);
// break;
// case "SI":
// valid = SIVATCheckDigit(cNumber);
// break;
// case "SK":
// valid = SKVATCheckDigit(cNumber);
// break;
//}
valid = window[cCode + "VATCheckDigit"](cNumber);
// Load new VAT number back into the form element
if (valid) {
valid = vatNumber;
}
// We have found that the number is valid - break from loop
break;
}
}
As you can see, I already replaced that ungodly switch statement with a single function call, but I'm wondering if there's a way to rewrite the for
...if
...break
statement so it's no longer a for loop, but while it's still human-readable.
vatexp
is a 44 regex array that has a bunch of regexes in there to determine what country the number is for. I'm hoping there's a way to write this code so it does not have to do a long loop.
The full function:
function checkVATNumber(toCheck) {
// Array holds the regular expressions for the valid VAT number
var vatexp = new Array();
// To change the default country (e.g. from the UK to Germany - DE):
// 1. Change the country code in the defCCode variable below to "DE".
// 2. Remove the question mark from the regular expressions associated with the UK VAT number:
// i.e. "(GB)?" -> "(GB)"
// 3. Add a question mark into the regular expression associated with Germany's number
// following the country code: i.e. "(DE)" -> "(DE)?"
var defCCode = "BE";
// Note - VAT codes without the "**" in the comment do not have check digit checking.
vatexp.push(/^(AT)U(\d{8})$/); //** Austria
vatexp.push(/^(BE)?(0?\d{9})$/); //** Belgium
vatexp.push(/^(BG)(\d{9,10})$/); //** Bulgaria
vatexp.push(/^(CHE)(\d{9})(MWST)?$/); //** Switzerland
vatexp.push(/^(CY)([0-5|9]\d{7}[A-Z])$/); //** Cyprus
vatexp.push(/^(CZ)(\d{8,10})(\d{3})?$/); //** Czech Republic
vatexp.push(/^(DE)([1-9]\d{8})$/); //** Germany
vatexp.push(/^(DK)(\d{8})$/); //** Denmark
vatexp.push(/^(EE)(10\d{7})$/); //** Estonia
vatexp.push(/^(EL)(\d{9})$/); //** Greece
vatexp.push(/^(ES)([A-Z]\d{8})$/); //** Spain (National juridical entities)
vatexp.push(/^(ES)([A-H|N-S|W]\d{7}[A-J])$/); //** Spain (Other juridical entities)
vatexp.push(/^(ES)([0-9|Y|Z]\d{7}[A-Z])$/); //** Spain (Personal entities type 1)
vatexp.push(/^(ES)([K|L|M|X]\d{7}[A-Z])$/); //** Spain (Personal entities type 2)
vatexp.push(/^(EU)(\d{9})$/); //** EU-type
vatexp.push(/^(FI)(\d{8})$/); //** Finland
vatexp.push(/^(FR)(\d{11})$/); //** France (1)
vatexp.push(/^(FR)([(A-H)|(J-N)|(P-Z)]\d{10})$/); // France (2)
vatexp.push(/^(FR)(\d[(A-H)|(J-N)|(P-Z)]\d{9})$/); // France (3)
vatexp.push(/^(FR)([(A-H)|(J-N)|(P-Z)]{2}\d{9})$/); // France (4)
vatexp.push(/^(GB)(\d{9})$/); //** UK (Standard)
vatexp.push(/^(GB)(\d{12})$/); //** UK (Branches)
vatexp.push(/^(GB)(GD\d{3})$/); //** UK (Government)
vatexp.push(/^(GB)(HA\d{3})$/); //** UK (Health authority)
vatexp.push(/^(GR)(\d{8,9})$/); //** Greece
vatexp.push(/^(HR)(\d{11})$/); //** Croatia
vatexp.push(/^(HU)(\d{8})$/); //** Hungary
vatexp.push(/^(IE)(\d{7}[A-W])$/); //** Ireland (1)
vatexp.push(/^(IE)([7-9][A-Z\*\+)]\d{5}[A-W])$/); //** Ireland (2)
vatexp.push(/^(IE)(\d{7}[A-W][AH])$/); //** Ireland (3)
vatexp.push(/^(IT)(\d{11})$/); //** Italy
vatexp.push(/^(LV)(\d{11})$/); //** Latvia
vatexp.push(/^(LT)(\d{9}|\d{12})$/); //** Lithunia
vatexp.push(/^(LU)(\d{8})$/); //** Luxembourg
vatexp.push(/^(MT)([1-9]\d{7})$/); //** Malta
vatexp.push(/^(NL)(\d{9})B\d{2}$/); //** Netherlands
vatexp.push(/^(NO)(\d{9})$/); //** Norway (not EU)
vatexp.push(/^(PL)(\d{10})$/); //** Poland
vatexp.push(/^(PT)(\d{9})$/); //** Portugal
vatexp.push(/^(RO)([1-9]\d{1,9})$/); //** Romania
vatexp.push(/^(RU)(\d{10}|\d{12})$/); //** Russia
vatexp.push(/^(RS)(\d{9})$/); //** Serbia
vatexp.push(/^(SI)([1-9]\d{7})$/); //** Slovenia
vatexp.push(/^(SK)([1-9]\d[(2-4)|(6-9)]\d{7})$/); //** Slovakia Republic
vatexp.push(/^(SE)(\d{10}01)$/); //** Sweden
// Load up the string to check
var vatNumber = toCheck.toUpperCase();
// Remove spaces etc. from the VAT number to help validation
var chars = [" ", "-", ",", "."];
for (var i = 0; i < chars.length; i++) {
while (vatNumber.indexOf(chars[i]) != -1) {
vatNumber = vatNumber.slice(0, vatNumber.indexOf(chars[i])) + vatNumber.slice(vatNumber.indexOf(chars[i]) + 1);
}
}
// Assume we're not going to find a valid VAT number
var valid = false;
// Check the string against the regular expressions for all types of VAT numbers
for (i = 0; i < vatexp.length; i++) {
// Have we recognised the VAT number?
if (vatexp[i].test(vatNumber)) {
// Yes - we have
var cCode = RegExp.$1; // Isolate country code
var cNumber = RegExp.$2; // Isolate the number
if (cCode.length == 0) {
cCode = defCCode;
} // Set up default country code
// Now perform the check digit calculation according to the national rules.
//Each function starts with 2-3 char code that indicates the country the function is for.
//this function replaces the original 100 line switch with 37 cases that the original author used.
valid = window[cCode + "VATCheckDigit"](cNumber);
// Load new VAT number back into the form element
if (valid) {
var splitVat = new Array();
splitVat[0] = cCode;
splitVat[1] = cNumber;
valid = splitVat;
}
// We have found that the number is valid - break from loop
break;
}
}
// Return with either an error or the reformatted VAT number
return valid;
}
Does anyone have a suggestion on if and how I can replace this for
loop?
NOTE: this particular code is not the most recent one. I just fixed 2 bugs related to my simplification (2 methods that didn't follow the naming convention and had to be renamed or redirected). The question still remains the same.
vatNumber
,defCode
, etc \$\endgroup\$AT
despite beingATU
, or is it okay to just change the function call toATUVAT...
(or alias)? \$\endgroup\$