Compare commits
5 Commits
695ffc31c5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4dbc942929 | |||
| 48672584c7 | |||
| 2fa1c94a22 | |||
| 313868ca94 | |||
| a47e331b25 |
@@ -9,7 +9,7 @@ The package can be installed by adding `ukraine_tax_id` to your list of dependen
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:ukraine_tax_id, "~> 0.1.0"}
|
||||
{:ukraine_tax_id, "~> 0.1.3"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
BIN
assets/cover.png
Normal file
BIN
assets/cover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 403 KiB |
@@ -1,5 +1,56 @@
|
||||
defmodule UkraineTaxidEx do
|
||||
@itin_length UkraineTaxidEx.Itin.length()
|
||||
@edrpou_length UkraineTaxidEx.Edrpou.length()
|
||||
|
||||
@moduledoc """
|
||||
Documentation for `UkraineTaxidEx`.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
`determine/1`: Takes a tax ID string and identifies its type based on length
|
||||
|
||||
- Returns `{:ok, UkraineTaxidEx.Itin}` for ITIN numbers
|
||||
- Returns `{:ok, UkraineTaxidEx.Edrpou}` for EDRPOU numbers
|
||||
- Returns an error if the length is invalid
|
||||
"""
|
||||
def determine(tax_id) when is_binary(tax_id) do
|
||||
case String.length(tax_id) do
|
||||
@itin_length -> {:ok, UkraineTaxidEx.Itin}
|
||||
@edrpou_length -> {:ok, UkraineTaxidEx.Edrpou}
|
||||
_ -> {:error, "Invalid tax ID length"}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
`parse/1`: Determines the tax ID type and parses it using the appropriate parser
|
||||
|
||||
- Uses `Module.concat` to dynamically find the correct parser module
|
||||
- Returns a tuple with status, result, and type information
|
||||
"""
|
||||
def parse(tax_id) do
|
||||
case determine(tax_id) do
|
||||
{:ok, type} ->
|
||||
parser = Module.concat(type, "Parser")
|
||||
{status, result} = parser.parse(tax_id)
|
||||
{status, result, type}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
`validate/1`: Validates a tax ID by attempting to parse it
|
||||
|
||||
- Returns `:ok` if valid
|
||||
- Returns error tuples with details if invalid
|
||||
"""
|
||||
|
||||
def validate(tax_id) do
|
||||
case parse(tax_id) do
|
||||
{:ok, _, _} -> :ok
|
||||
{:error, error, type} -> {:error, error, type}
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,8 +11,8 @@ defmodule UkraineTaxidEx.Base do
|
||||
|
||||
alias UkraineTaxidEx.{Base, Serialize, Commons}
|
||||
|
||||
@parse_module (Module.split(__MODULE__) ++ ["Parser"]) |> Module.safe_concat()
|
||||
# def parse_module(), do: @parse_module
|
||||
@parse_module (Module.split(__MODULE__) ++ ["Parser"]) |> Module.concat()
|
||||
def parse_module(), do: @parse_module
|
||||
|
||||
@impl Base
|
||||
@spec to_map(data :: t()) :: map()
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
defmodule UkraineTaxidEx.BaseParser do
|
||||
@typedoc """
|
||||
Options for parsing:
|
||||
- `:normalize?` - if true, pad the string to the right length (8 for EDRPOU, 10 for ITIN)
|
||||
- `:clean?` - if true, remove non-digit characters
|
||||
"""
|
||||
@type options :: [normalize?: boolean, clean?: boolean]
|
||||
@callback parse(string :: String.t(), options :: options()) :: {:ok, term} | {:error, atom}
|
||||
|
||||
@callback parse(code :: String.t(), options :: options()) :: {:ok, term} | {:error, atom}
|
||||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
@@ -11,14 +17,14 @@ defmodule UkraineTaxidEx.BaseParser do
|
||||
@type string_or_ok() :: String.t() | {:ok, String.t()}
|
||||
@type struct_or_error() :: {:ok, term} | {:error, atom()}
|
||||
|
||||
@struct_module Module.split(__MODULE__) |> Enum.slice(0..-2//1) |> Module.safe_concat()
|
||||
@struct_module Module.split(__MODULE__) |> Enum.slice(0..-2//1) |> Module.concat()
|
||||
# def struct_module(), do: @struct_module
|
||||
|
||||
defp to_struct(map), do: struct(@struct_module, map)
|
||||
|
||||
@impl BaseParser
|
||||
@spec parse(data :: string_or_ok, options :: BaseParser.options()) :: struct_or_error()
|
||||
def parse(data, options \\ [normalize?: false, clean?: false])
|
||||
def parse(data, options \\ [normalize?: false, clean?: true])
|
||||
def parse({:ok, string}, options), do: parse(string, options)
|
||||
def parse({:error, error}, _options), do: {:error, error}
|
||||
|
||||
@@ -34,6 +40,7 @@ defmodule UkraineTaxidEx.BaseParser do
|
||||
end
|
||||
|
||||
defp generate({:error, error}), do: {:error, error}
|
||||
defp generate({:ok, string}), do: generate(string)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
defmodule UkraineTaxidEx.BaseValidator do
|
||||
@callback validate(String.t()) ::
|
||||
{:ok, String.t()}
|
||||
@callback validate(code :: String.t()) ::
|
||||
{:ok, code :: String.t()}
|
||||
| {:error,
|
||||
:length_too_short | :length_too_long | :invalid_length | :invalid_checksum}
|
||||
@callback violates_length?(String.t()) :: boolean
|
||||
@callback violates_length_too_short?(String.t()) :: boolean
|
||||
@callback violates_length_too_long?(String.t()) :: boolean
|
||||
@callback violates_checksum?(String.t()) :: boolean
|
||||
@callback violates_length?(code :: String.t()) :: boolean
|
||||
@callback violates_length_too_short?(code :: String.t()) :: boolean
|
||||
@callback violates_length_too_long?(code :: String.t()) :: boolean
|
||||
@callback violates_checksum?(code :: String.t()) :: boolean
|
||||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
@@ -31,28 +31,25 @@ defmodule UkraineTaxidEx.BaseValidator do
|
||||
|
||||
@doc "Check whether a given EDRPOU violates the required length"
|
||||
@impl BaseValidator
|
||||
@spec violates_length?(String.t()) :: boolean
|
||||
def violates_length?(string),
|
||||
do: String.length(string) != length()
|
||||
@spec violates_length?(code :: String.t()) :: boolean
|
||||
def violates_length?(code), do: String.length(code) != length()
|
||||
|
||||
@doc "Check whether a given EDRPOU too short"
|
||||
@impl BaseValidator
|
||||
@spec violates_length_too_short?(String.t()) :: boolean
|
||||
def violates_length_too_short?(string),
|
||||
do: String.length(string) < length()
|
||||
@spec violates_length_too_short?(code :: String.t()) :: boolean
|
||||
def violates_length_too_short?(code), do: String.length(code) < length()
|
||||
|
||||
@doc "Check whether a given EDRPOU too long"
|
||||
@impl BaseValidator
|
||||
@spec violates_length_too_long?(String.t()) :: boolean
|
||||
def violates_length_too_long?(string),
|
||||
do: String.length(string) > length()
|
||||
@spec violates_length_too_long?(code :: String.t()) :: boolean
|
||||
def violates_length_too_long?(code), do: String.length(code) > length()
|
||||
|
||||
@doc "Check whether a given EDRPOU has correct checksum"
|
||||
@impl BaseValidator
|
||||
@spec violates_checksum?(String.t()) :: boolean
|
||||
def violates_checksum?(string) do
|
||||
@spec violates_checksum?(code :: String.t()) :: boolean
|
||||
def violates_checksum?(code) do
|
||||
{digits, check_digit} =
|
||||
string
|
||||
code
|
||||
|> digits()
|
||||
|> digits_and_check_digit()
|
||||
|
||||
|
||||
@@ -53,9 +53,7 @@ defmodule UkraineTaxidEx.Edrpou.Parser do
|
||||
| :length_too_long
|
||||
| :invalid_checksum}
|
||||
|
||||
defp generate({:error, error}), do: {:error, error}
|
||||
|
||||
defp generate({:ok, string}) do
|
||||
defp generate(string) do
|
||||
digits = digits(string)
|
||||
|
||||
%{code: string, check_sum: check_sum(digits), check_digit: check_digit(digits)}
|
||||
|
||||
@@ -2,20 +2,20 @@ defmodule UkraineTaxidEx.Itin.Error do
|
||||
@type error() ::
|
||||
:invalid_length
|
||||
| :invalid_checksum
|
||||
| :length_to_long
|
||||
| :length_to_short
|
||||
| :length_too_long
|
||||
| :length_too_short
|
||||
@type errors() :: [error()]
|
||||
@errors [
|
||||
:invalid_length,
|
||||
:invalid_checksum,
|
||||
:length_to_long,
|
||||
:length_to_short
|
||||
:length_too_long,
|
||||
:length_too_short
|
||||
]
|
||||
@messages [
|
||||
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"
|
||||
length_too_long: "ITIN longer then required length",
|
||||
length_too_short: "ITIN shorter then required length"
|
||||
]
|
||||
|
||||
@spec message(error()) :: String.t()
|
||||
|
||||
@@ -59,7 +59,7 @@ defmodule UkraineTaxidEx.Itin.Parser do
|
||||
|
||||
use UkraineTaxidEx.BaseParser
|
||||
|
||||
defp generate({:ok, string}) do
|
||||
defp generate(string) do
|
||||
digits = digits(string)
|
||||
|
||||
%{
|
||||
|
||||
7
mix.exs
7
mix.exs
@@ -3,9 +3,11 @@ defmodule UkraineTaxidEx.MixProject do
|
||||
|
||||
@app :ukraine_tax_id
|
||||
@module UkraineTaxidEx
|
||||
@author "Danylo Negrienko"
|
||||
@authors [@author]
|
||||
@source_url "https://g.tulz.dev/opensource/ukraine-taxid-ex"
|
||||
@docs_url "https://hexdocs.pm/#{@app}"
|
||||
@version "0.1.0"
|
||||
@version "0.1.3"
|
||||
|
||||
def project do
|
||||
[
|
||||
@@ -50,6 +52,9 @@ defmodule UkraineTaxidEx.MixProject do
|
||||
|
||||
defp docs() do
|
||||
[
|
||||
authors: @authors,
|
||||
cover: "assets/cover.png",
|
||||
# logo: "assets/logo.svg",
|
||||
main: "readme",
|
||||
name: "#{@module}",
|
||||
source_ref: "v#{@version}",
|
||||
|
||||
@@ -1,4 +1,64 @@
|
||||
defmodule UkraineTaxidExTest do
|
||||
use ExUnit.Case, async: true
|
||||
doctest UkraineTaxidEx
|
||||
|
||||
describe "determine/1" do
|
||||
test "correctly identifies ITIN tax ID" do
|
||||
assert {:ok, UkraineTaxidEx.Itin} = UkraineTaxidEx.determine("1234567890")
|
||||
end
|
||||
|
||||
test "correctly identifies EDRPOU tax ID" do
|
||||
assert {:ok, UkraineTaxidEx.Edrpou} = UkraineTaxidEx.determine("12345678")
|
||||
end
|
||||
|
||||
test "returns error for invalid length" do
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.determine("123")
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.determine("1234")
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.determine("12345")
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.determine("123456")
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.determine("1234567")
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.determine("123456789")
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.determine("12345678901")
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse/1" do
|
||||
test "successfully parses valid ITIN" do
|
||||
assert {:ok, %{code: "1759013776"}, UkraineTaxidEx.Itin} =
|
||||
UkraineTaxidEx.parse("1759013776")
|
||||
end
|
||||
|
||||
test "successfully parses valid EDRPOU" do
|
||||
assert {:ok, %{code: "30283027"}, UkraineTaxidEx.Edrpou} =
|
||||
UkraineTaxidEx.parse("30283027")
|
||||
end
|
||||
|
||||
test "returns error for invalid tax ID" do
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.parse("123")
|
||||
end
|
||||
|
||||
test "returns :length_too_short error for invalid format tax id (valid length but invalid after clean)" do
|
||||
assert {:error, :length_too_short, UkraineTaxidEx.Itin} =
|
||||
UkraineTaxidEx.parse("abcdefghij")
|
||||
|
||||
assert {:error, :length_too_short, UkraineTaxidEx.Edrpou} =
|
||||
UkraineTaxidEx.parse("abcdefgh")
|
||||
end
|
||||
end
|
||||
|
||||
describe "validate/1" do
|
||||
test "returns :invalid_checksum for ITIN with invalid checksum" do
|
||||
assert {:error, :invalid_checksum, UkraineTaxidEx.Itin} =
|
||||
UkraineTaxidEx.validate("1234567890")
|
||||
end
|
||||
|
||||
test "returns :invalid_checksum for EDRPOU with invalid checksum" do
|
||||
assert {:error, :invalid_checksum, UkraineTaxidEx.Edrpou} =
|
||||
UkraineTaxidEx.validate("12345679")
|
||||
end
|
||||
|
||||
test "returns error for invalid tax ID" do
|
||||
assert {:error, "Invalid tax ID length"} = UkraineTaxidEx.validate("123")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user