Skip to content

Commit

Permalink
feat(validationUtils): add VAT validation function (#91)
Browse files Browse the repository at this point in the history
* feat(validationUtils): add VAT validation function
* feat(validationUtils): add more test cases
* feat(validationUtils): add external reference, review changes
  • Loading branch information
arisGio authored Dec 7, 2024
1 parent 2476655 commit 673cca5
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 1 deletion.
11 changes: 11 additions & 0 deletions docs/validationUtils.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
## Table of Contents

- [**validatePostalCode()**](#validatePostalCode)
- [**validateVATNumber()**](#validateVATNumber)
- [**validateAMKA()**](#validateAMKA)

---
Expand All @@ -19,6 +20,16 @@

**Return Type**: Boolean indicating whether the postal code is valid.

### validateVATNumber()<a id='validateVATNumber'></a>

**Description**: Validates an a greek tax id / AFM (ΑΦΜ) .

**Parameters:**

**`VATNumber`**: The VAT Number to validate.

**Return Type**: Boolean indicating whether the VAT Number is valid.

### validateAMKA()<a id='validateAMKA'></a>

**Description**: Validates an AMKA number.
Expand Down
84 changes: 83 additions & 1 deletion src/__tests__/validationUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { validatePostalCode, validateAMKA } from "../validationUtils";
import { validateAMKA, validatePostalCode, validateVATNumber } from "../validationUtils";

describe("validatePostalCode", () => {
it("returns true on existing postal codes", () => {
Expand Down Expand Up @@ -52,3 +52,85 @@ describe("validateAMKA", () => {
expect(validateAMKA("30022455551")).toBe(false);
});
});

describe("validateVATNumber", () => {
it("returns false for invalid afms", () => {
expect(validateVATNumber("12345678")).toBe(false);
expect(validateVATNumber("xxxxxxxx")).toBe(false);
expect(validateVATNumber("141212176")).toBe(false);
expect(validateVATNumber("111111111")).toBe(false);
expect(validateVATNumber("311199349")).toBe(false);
expect(validateVATNumber("000000000")).toBe(false);
});
it("returns true for valid afms", () => {
expect(validateVATNumber("011111111")).toBe(true);
expect(validateVATNumber("150892297")).toBe(true);
expect(validateVATNumber("126668921")).toBe(true);
expect(validateVATNumber("234893562")).toBe(true);
expect(validateVATNumber("126668921")).toBe(true);
expect(validateVATNumber("565830300")).toBe(true);
});

it("should work with input older than year 1999", () => {
expect(validateVATNumber("050503557")).toBe(true);
expect(validateVATNumber("056203761")).toBe(false);
});

it("should work with legal entities, i.e. starting with 7-9", () => {
expect(validateVATNumber("737616950")).toBe(true);
expect(validateVATNumber("816267772")).toBe(true);
expect(validateVATNumber("985117351")).toBe(true);
});

it("should work with physical entities, i.e. starting with 1-4", () => {
expect(validateVATNumber("115410916")).toBe(true);
expect(validateVATNumber("234383562")).toBe(true);
expect(validateVATNumber("302054370")).toBe(true);
expect(validateVATNumber("479901058")).toBe(true);
});

it("should work with specific repeated digits tolerance", () => {
expect(validateVATNumber("267687344")).toBe(false);
expect(validateVATNumber("830777267")).toBe(false);
});

it("should not accept more than 9 digits", () => {
expect(validateVATNumber("23798790484567")).toBe(false);
expect(validateVATNumber("237987904860")).toBe(false);
expect(validateVATNumber("23798790489")).toBe(false);
expect(validateVATNumber("2379879048")).toBe(false);
});

it("should not accept less than 9 digits", () => {
expect(validateVATNumber("42496046")).toBe(false);
expect(validateVATNumber("4249604")).toBe(false);
expect(validateVATNumber("424960")).toBe(false);
expect(validateVATNumber("42496")).toBe(false);
expect(validateVATNumber("4249")).toBe(false);
expect(validateVATNumber("424")).toBe(false);
expect(validateVATNumber("42")).toBe(false);
expect(validateVATNumber("4")).toBe(false);
expect(validateVATNumber("")).toBe(false);
});

it("should accept only numerical characters", () => {
expect(validateVATNumber("2379w7904")).toBe(false);
expect(validateVATNumber("9K7755250")).toBe(false);
});

it("works with int or string values", () => {
expect(validateVATNumber(111111111)).toBe(false);
expect(validateVATNumber(1111111111111)).toBe(false);
expect(validateVATNumber(1.1)).toBe(false);
expect(validateVATNumber(1.11111111)).toBe(false);
expect(validateVATNumber("xxxxxxxx")).toBe(false);
expect(validateVATNumber("141212176")).toBe(false);

expect(validateVATNumber("150892297")).toBe(true);
expect(validateVATNumber(150892297)).toBe(true);
expect(validateVATNumber(126668921)).toBe(true);
expect(validateVATNumber("126668921")).toBe(true);
expect(validateVATNumber(234893562)).toBe(true);
expect(validateVATNumber("234893562")).toBe(true);
});
});
31 changes: 31 additions & 0 deletions src/validationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,34 @@ export function validateAMKA(amka: string | number): boolean {
// The sum should be divisible by 10
return sum % 10 === 0;
}

/**
* Validates a VAT (Value Added Tax) number based on the following criteria:
* - Must be exactly 9 digits long.
* - Must consist of numeric characters only.
* - Cannot be all zeros.
* - The last digit (checksum) must satisfy a specific calculation based on the preceding digits.
*
* @param {string | number} vatNumber - The VAT number to validate, provided as a string or a number.
* @returns {boolean} - Returns `true` if the VAT number is valid according to the specified rules; otherwise, `false`.
*
* Reference:{@link https://lytrax.io/blog/projects/greek-tin-validator-generator Greek TIN Validator Generator}
*/
export function validateVATNumber(vatNumberInput: string | number): boolean {
const vatNumber = String(vatNumberInput);

if (vatNumber.length !== 9 || !/^\d+$/.test(vatNumber) || vatNumber === "0".repeat(9)) {
return false;
}

const sum = vatNumber
.substring(0, 8)
.split("")
.reduce((s, v, i) => s + (parseInt(v) << (8 - i)), 0);

const calc = sum % 11;
const d9 = parseInt(vatNumber[8]!);
const valid = calc % 10 === d9;

return valid;
}

0 comments on commit 673cca5

Please sign in to comment.