Itin parser and validator added
This commit is contained in:
42
lib/ukraine_taxid_ex/itin/check_sum.ex
Normal file
42
lib/ukraine_taxid_ex/itin/check_sum.ex
Normal file
@@ -0,0 +1,42 @@
|
||||
defmodule UkraineTaxidEx.Itin.CheckSum do
|
||||
@moduledoc """
|
||||
Module for calculating the checksum of Ukrainian Individual Tax Identification Numbers (ITIN).
|
||||
Provides functions for checksum calculation based on weighted digits and helper functions
|
||||
for working with ITIN weights and dividers. Uses specified numerical weights to multiply
|
||||
each digit of the ITIN and validate its authenticity.
|
||||
"""
|
||||
|
||||
alias UkraineTaxidEx.Commons, as: C
|
||||
|
||||
import UkraineTaxidEx.Commons, only: [value_digits: 1]
|
||||
|
||||
@weights [-1, 5, 7, 9, 4, 6, 10, 5, 7]
|
||||
|
||||
@doc """
|
||||
Returns the list of numerical weights used to calculate the ITIN checksum.
|
||||
Each digit in the ITIN is multiplied by its corresponding weight.
|
||||
"""
|
||||
@spec weights() :: C.digits()
|
||||
def weights(), do: @weights
|
||||
|
||||
@spec divider() :: non_neg_integer()
|
||||
defp divider(), do: 11
|
||||
|
||||
@doc """
|
||||
Calculate checksum for ITIN number.
|
||||
The checksum for ITIN is calculated in several steps:
|
||||
1. Multiply each digit by its corresponding weight
|
||||
2. Sum the products
|
||||
3. Take mod 11 of the sum
|
||||
4. If mod 11 is greater or equal than 10, repeat steps 2-4 with weights +2
|
||||
"""
|
||||
@spec check_sum(digits :: C.digits(), weights :: C.digits()) :: integer()
|
||||
def check_sum(digits, weights \\ @weights) do
|
||||
digits
|
||||
|> value_digits()
|
||||
|> Enum.zip(weights)
|
||||
|> Enum.reduce(0, fn {digit, weight}, acc -> digit * weight + acc end)
|
||||
|> rem(divider())
|
||||
|> rem(10)
|
||||
end
|
||||
end
|
||||
@@ -12,10 +12,10 @@ defmodule UkraineTaxidEx.Itin.Error do
|
||||
:length_to_short
|
||||
]
|
||||
@messages [
|
||||
invalid_length: "EDRPOU violates the required length",
|
||||
invalid_checksum: "EDRPOU checksum is invalid",
|
||||
length_to_long: "EDRPOU longer then required length",
|
||||
length_to_short: "EDRPOU shorter then required length"
|
||||
invalid_length: "ITIN violates the required length",
|
||||
invalid_checksum: "ITIN checksum is invalid",
|
||||
length_to_long: "ITIN longer then required length",
|
||||
length_to_short: "ITIN shorter then required length"
|
||||
]
|
||||
|
||||
@spec message(error()) :: String.t()
|
||||
|
||||
93
lib/ukraine_taxid_ex/itin/parser.ex
Normal file
93
lib/ukraine_taxid_ex/itin/parser.ex
Normal file
@@ -0,0 +1,93 @@
|
||||
defmodule UkraineTaxidEx.Itin.Parser do
|
||||
@moduledoc """
|
||||
This module provides parsing functionality for Ukrainian Individual Taxpayer Identification Numbers (ITIN).
|
||||
|
||||
ITINs (also known as РНОКПП/ІПН) are unique identifiers assigned to individuals in Ukraine for tax purposes.
|
||||
The parser validates the number format and extracts meaningful components like the checksum according to
|
||||
official requirements.
|
||||
|
||||
Key features:
|
||||
- Validates ITIN format and length
|
||||
- Calculates and verifies checksum
|
||||
- Parses components into a structured format
|
||||
- Handles both raw strings and pre-validated input
|
||||
|
||||
Examples of successful ITIN parsing:
|
||||
|
||||
```elixir
|
||||
iex> UkraineTaxidEx.Itin.Parser.parse("2222222222")
|
||||
{:ok, %UkraineTaxidEx.Itin{code: "2222222222", birth_date: ~D[1960-12-17], number: 2222, gender: 0, check_sum: 2, check_digit: 2}}
|
||||
|
||||
iex> UkraineTaxidEx.Itin.Parser.parse("3333333333")
|
||||
{:ok, %UkraineTaxidEx.Itin{code: "3333333333", birth_date: ~D[1991-03-05], number: 3333, gender: 1, check_sum: 3, check_digit: 3}}
|
||||
```
|
||||
|
||||
Examples of unsuccessful ITIN parsing:
|
||||
|
||||
```elixir
|
||||
iex> UkraineTaxidEx.Itin.Parser.parse("123456")
|
||||
{:error, :length_too_short}
|
||||
|
||||
iex> UkraineTaxidEx.Itin.Parser.parse("12345678901")
|
||||
{:error, :length_too_long}
|
||||
|
||||
iex> UkraineTaxidEx.Itin.Parser.parse("1234567890")
|
||||
{:error, :invalid_checksum}
|
||||
```
|
||||
"""
|
||||
alias UkraineTaxidEx.Itin
|
||||
alias UkraineTaxidEx.Commons, as: C
|
||||
|
||||
import UkraineTaxidEx.Itin, only: [length: 0]
|
||||
import UkraineTaxidEx.Itin.CheckSum, only: [check_sum: 1]
|
||||
import UkraineTaxidEx.Itin.Validator, only: [validate: 1]
|
||||
import UkraineTaxidEx.Commons, only: [check_digit: 1, digits: 1, digits: 3, undigits: 1, ok: 1]
|
||||
|
||||
require Integer
|
||||
|
||||
@type itin_string() :: String.t()
|
||||
@type itin() :: Itin.t()
|
||||
@type itin_or_error() ::
|
||||
{:ok, Itin.t()}
|
||||
| {:error,
|
||||
:length_too_short
|
||||
| :length_too_long
|
||||
| :invalid_checksum}
|
||||
|
||||
@base_date Date.new!(1899, 12, 31)
|
||||
def base_date(), do: @base_date
|
||||
|
||||
use UkraineTaxidEx.BaseParser
|
||||
|
||||
defp generate({:ok, string}) do
|
||||
digits = digits(string)
|
||||
|
||||
%{
|
||||
code: string,
|
||||
birth_date: birth_date(digits),
|
||||
number: number(digits),
|
||||
gender: gender(digits),
|
||||
check_sum: check_sum(digits),
|
||||
check_digit: check_digit(digits)
|
||||
}
|
||||
|> to_struct()
|
||||
|> ok()
|
||||
end
|
||||
|
||||
@spec slice(digits :: C.digits(), Range.t()) :: Date.t()
|
||||
defp slice(digits, range) do
|
||||
digits
|
||||
|> Enum.slice(range)
|
||||
|> undigits()
|
||||
|> String.to_integer()
|
||||
end
|
||||
|
||||
@spec birth_date(digits :: C.digits()) :: Date.t()
|
||||
def birth_date(digits), do: Date.add(base_date(), slice(digits, 0..4))
|
||||
|
||||
@spec number(digits :: C.digits()) :: integer()
|
||||
def number(digits), do: slice(digits, 5..8)
|
||||
|
||||
@spec gender(digits :: C.digits()) :: Itin.gender()
|
||||
def gender(digits), do: (Integer.is_odd(slice(digits, -2..-2//1)) && 1) || 0
|
||||
end
|
||||
35
lib/ukraine_taxid_ex/itin/validator.ex
Normal file
35
lib/ukraine_taxid_ex/itin/validator.ex
Normal file
@@ -0,0 +1,35 @@
|
||||
defmodule UkraineTaxidEx.Itin.Validator do
|
||||
@moduledoc """
|
||||
Validator module for Ukrainian Individual Taxpayer Identification Number (ITIN/IPN).
|
||||
Handles validation of ITIN numbers according to Ukrainian tax authority requirements.
|
||||
|
||||
Validates an ITIN number to check if it meets length requirements and has a valid checksum.
|
||||
|
||||
Returns:
|
||||
* `{:ok, itin}` if validation successful
|
||||
* `{:error, :length_too_short}` if shorter than required length
|
||||
* `{:error, :length_too_long}` if longer than required length
|
||||
* `{:error, :invalid_checksum}` if checksum is invalid
|
||||
|
||||
Examples:
|
||||
|
||||
```elixir
|
||||
iex> UkraineTaxidEx.Itin.Validator.validate("3184710691")
|
||||
{:ok, "3184710691"}
|
||||
|
||||
iex> UkraineTaxidEx.Itin.Validator.validate("123456")
|
||||
{:error, :length_too_short}
|
||||
|
||||
iex> UkraineTaxidEx.Itin.Validator.validate("12345678901")
|
||||
{:error, :length_too_long}
|
||||
|
||||
iex> UkraineTaxidEx.Itin.Validator.validate("3184710692")
|
||||
{:error, :invalid_checksum}
|
||||
```
|
||||
"""
|
||||
|
||||
import UkraineTaxidEx.Itin, only: [length: 0]
|
||||
import UkraineTaxidEx.Itin.CheckSum, only: [check_sum: 1]
|
||||
|
||||
use UkraineTaxidEx.BaseValidator
|
||||
end
|
||||
Reference in New Issue
Block a user