Skip to content

Commit

Permalink
Add NIPT, NUIS (Numri i Identifikimit për Personin e Tatueshëm, Alban…
Browse files Browse the repository at this point in the history
…ian tax number).
  • Loading branch information
kupolak committed Jan 29, 2024
1 parent bd0fb6b commit 74972a7
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/al/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(library
(name al)
(modules nipt)
(libraries tools))
28 changes: 28 additions & 0 deletions lib/al/nipt.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
open Tools

exception Invalid_length
exception Invalid_format

let nipt_re = Str.regexp "^[A-M][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][A-Z]$"

let compact number =
let number =
Utils.clean number " " |> String.uppercase_ascii |> String.trim
in
if String.starts_with number ~prefix:"AL" then
String.sub number 2 (String.length number - 2)
else if String.starts_with number ~prefix:"(AL)" then
String.sub number 4 (String.length number - 4)
else number

let validate number =
let number = compact number in
print_string number;
if not (Str.string_match nipt_re number 0) then raise Invalid_format
else number

let is_valid number =
try
ignore (validate number);
true
with _ -> false
32 changes: 32 additions & 0 deletions lib/al/nipt.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(*
NIPT, NUIS (Numri i Identifikimit për Personin e Tatueshëm, Albanian tax number).
The Albanian NIPT is a 10-digit number with the first and last character
being letters. The number is assigned to individuals and organisations for
tax purposes.
The first letter indicates the decade the number was assigned or date birth
date for individuals, followed by a digit for the year. The next two digits
contain the month (and gender for individuals and region for organisations)
followed by two digits for the day of the month. The remainder is a serial
followed by a check letter (check digit algorithm unknown).
*)

exception Invalid_length
(** Exception raised when the NIPT number has an invalid length. *)

exception Invalid_format
(** Exception raised when the NIPT number has an invalid format. *)

val nipt_re : Str.regexp

val compact : string -> string
(** [compact number] removes spaces and converts [number] to uppercase. *)

val validate : string -> string
(** [validate number] checks if [number] is a valid NIPT number.
It raises [Invalid_length] or [Invalid_format] if the number is not valid. *)

val is_valid : string -> bool
(** [is_valid number] checks if [number] is a valid NIPT number.
It returns [true] if the number is valid, [false] otherwise. *)
4 changes: 4 additions & 0 deletions test/al/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(test
(name test_nipt)
(libraries alcotest al)
(modules test_nipt))
134 changes: 134 additions & 0 deletions test/al/test_nipt.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
open Alcotest

let test_is_valid () =
let numbers =
[
"J 98624806 P"
; "J61827501H"
; "J61922018S"
; "J61923008Q"
; "J62903770O"
; "J66702410U"
; "J67902218L"
; "J67902618M"
; "J73721043Q"
; "J74517201G"
; "J76418907K"
; "J76705047U"
; "J77411245Q"
; "J78716317H"
; "J82916489E"
; "J86526614T"
; "J91425005N"
; "J93910409N"
; "K 01725001F"
; "K 11723003 M"
; "K 37507987 N"
; "K 41424801 U"
; "K 47905861 R"
; "K 63005203 O"
; "K 67204202 P"
; "K01730502W"
; "K11515001T"
; "K12113002H"
; "K13001013H"
; "K14019001H"
; "K21622001M"
; "K22218003V"
; "K31518077S"
; "K31525146H"
; "K31526056N"
; "K32203501H"
; "K32801430W"
; "K33714725W"
; "K36308746I"
; "K36520204A"
; "K41315003J"
; "K46621201I"
; "K51518058O"
; "K56417201G"
; "K59418208E"
; "K61617040L"
; "K71822006R"
; "K72113010E"
; "K81428502L"
; "K81618039O"
; "K82418002C"
; "K82612003J"
; "K91725009J"
; "K92402023O"
; "L 22614402 H"
; "L 62119008 A"
; "L01622006F"
; "L01717030C"
; "L02023501H"
; "L02226012N"
; "L03321203G"
; "L03929803I"
; "L06426702Q"
; "L07305201K"
; "L08711201I"
; "L11325024K"
; "L11810502T"
; "L12213005M"
; "L14118803B"
; "L21310054D"
; "L21408015A"
; "L21429502L"
; "L21906001L"
; "L21923507N"
; "L22804207O"
; "L24006002V"
; "L29616001A"
; "L31518001O"
; "L32210507A"
; "L32319014A"
; "L32622601G"
; "L41410025S"
; "L41512005R"
; "L42008005H"
; "L42115015G"
; "L42307007E"
; "L44119601E"
; "L47014204F"
; "L48117101S"
; "L52305009L"
; "L58428303T"
; "L62119008A"
; "L72031013B"
; "L81506043D"
; "L81618040T"
; "L82306024Q"
; "L98602504L"
; "M 02129023 S"
; "M 11807013 N"
; "M02129023 S"
]
in
List.iter
(fun number -> check bool number true (Al.Nipt.is_valid number))
numbers

let test_is_not_valid () =
let numbers =
[
"A 123 V"
; "ABCDEF"
; "1234567890"
; "A B C D E F"
; "J 986206 P"
; "X 98624806 M"
; "Z67902218L"
]
in
List.iter
(fun number -> check bool number false (Al.Nipt.is_valid number))
numbers

let suite =
[
("test_validate", `Quick, test_is_valid)
; ("test_is_not_valid", `Quick, test_is_not_valid)
]

let () = Alcotest.run "Us.Atin" [ ("suite", suite) ]

0 comments on commit 74972a7

Please sign in to comment.