diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 6062025..a1b7f83 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -16,5 +16,5 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] [assembly: Guid("aaf08873-14e5-411d-8ec8-629782ac8f03")] -[assembly: AssemblyVersion("2.1.22074.1600")] -[assembly: AssemblyFileVersion("2.1.22074.1600")] \ No newline at end of file +[assembly: AssemblyVersion("2.1.22090.1256")] +[assembly: AssemblyFileVersion("2.1.22090.1256")] \ No newline at end of file diff --git a/VatUtils.cs b/VatUtils.cs new file mode 100644 index 0000000..b8959ad --- /dev/null +++ b/VatUtils.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Security.Cryptography.X509Certificates; + +namespace FCS.Lib.Utility +{ + public class VatUtils + { + public static bool CheckVat(string countryCode, string vatNumber) + { + return countryCode.ToUpperInvariant() switch + { + "DK" => CheckVatNumberDenmark(vatNumber), + "NO" => CheckVatNumberNorway(vatNumber), + "SE" => CheckVatNumberSweden(vatNumber), + _ => false + }; + } + + private static bool CheckVatNumberNorway(string vatNumber) + { + // https://wiki.scn.sap.com/wiki/display/CRM/Norway + // 12 digits + // C1..C8 random 0 to 9 + // C9 Check MOD11 + // C10 C11 C12 chars == MVA + return vatNumber.Length >= 9 && ValidateMod11(vatNumber.Substring(0, 8)); + } + + private static bool CheckVatNumberDenmark(string vatNumber) + { + // https://wiki.scn.sap.com/wiki/display/CRM/Denmark + // 8 digits 0 to 9 + // C1..C7 + // C8 Modulo 11 check digit + // C1 > 0 + // R = (2*C1 + 7*C2 + 6*C3 + 5*C4 + 4*C5 + 3*C6 + 2*C7 + C8) + if ((int)char.GetNumericValue(vatNumber[0]) < 1 || vatNumber.Length > 8) + return false; + return ValidateMod11(vatNumber); + } + + private static bool CheckVatNumberSweden(string vatNumber) + { + // https://wiki.scn.sap.com/wiki/display/CRM/Sweden + // 12 digits 0 to 9 + // C1 = (10 - (R + C2 + C4 + C6 + C8) modulo 10) modulo 10 + // R = S1 + S3 + S5 + S7 + S9 + // Si = int(Ci/5) + (Ci*2) modulo 10) + // C11 C12 >= 01 <= 94 + // + // Validate full string using Luhn algoritm + // 12 chars + if (vatNumber.Length != 12) + return false; + // validate if number + if (!int.TryParse(vatNumber, out var result)) + return false; + //validate char 11 and 12 + var valid = int.TryParse(vatNumber.Substring(10, 2), out var v2); + if(valid && v2 is < 1 or > 94 ) + return false; + // check vatnumber + return CheckLuhn(vatNumber); + } + + public static bool ValidateMod11(string number) + { + if (long.Parse(number) == 0) + return false; + + var sum = 0; + for (int i = number.Length - 1, multiplier = 1; i >= 0; i--) + { + Console.WriteLine($"char: {number[i]} multiplier: {multiplier}"); + sum += (int)char.GetNumericValue(number[i]) * multiplier; + if (++multiplier > 7) multiplier = 2; + } + return sum % 11 == 0; + } + + public static bool ValidateMod10(string number) + { + if (long.Parse(number) == 0) + return false; + + var nDigits = number.Length; + var nSum = 0; + var isSecond = false; + for (var i = nDigits - 1; i >= 0; i--) + { + var d = (int)char.GetNumericValue(number[i]) - '0'; + if (isSecond) + d *= 2; + nSum += d / 10; + nSum += d % 10; + isSecond = !isSecond; + } + return (nSum % 10 == 0); + } + + public static bool CheckLuhn(string vatNumber) + { + // https://www.geeksforgeeks.org/luhn-algorithm/ + + var nDigits = vatNumber.Length; + var nSum = 0; + var isSecond = false; + for (var i = nDigits - 1; i >= 0; i--) + { + var d = (int)char.GetNumericValue(vatNumber[i]) - '0'; + if (isSecond) + d *= 2; + // We add two digits to handle + // cases that make two digits + // after doubling + nSum += d / 10; + nSum += d % 10; + + isSecond = !isSecond; + } + return (nSum % 10 == 0); + } + + public static string GetMod10CheckDigit(string number) + { + var sum = 0; + var alt = true; + var digits = number.ToCharArray(); + for (int i = digits.Length - 1; i >= 0; i--) + { + var curDigit = digits[i] - 48; + if (alt) + { + curDigit *= 2; + if (curDigit > 9) + curDigit -= 9; + } + sum += curDigit; + alt = !alt; + } + return sum % 10 == 0 ? "0" : (10 - sum % 10).ToString(); + } + + public static string AddSelfCheckDigit(string number) + { + return number + GetMod11CheckDigit(number); + } + + public static string GetMod11CheckDigit(string number) + { + var sum = 0; + for (int i = number.Length - 1, multiplier = 2; i >= 0; i--) + { + sum += (int)char.GetNumericValue(number[i]) * multiplier; + if (++multiplier > 7) multiplier = 2; + } + var modulo = (sum % 11); + return modulo is 0 or 1 ? "0" : (11 - modulo).ToString(); + } + } +} \ No newline at end of file