BBAN parts: bank_code, account_number, branch_code and national_check supported in parser and validator
This commit is contained in:
@@ -41,6 +41,7 @@ defmodule IbanEx.Country do
|
||||
"IE" => IbanEx.Country.IE,
|
||||
"IL" => IbanEx.Country.IL,
|
||||
"IT" => IbanEx.Country.IT,
|
||||
"IS" => IbanEx.Country.IS,
|
||||
"JO" => IbanEx.Country.JO,
|
||||
"KZ" => IbanEx.Country.KZ,
|
||||
"KW" => IbanEx.Country.KW,
|
||||
|
||||
45
lib/iban_ex/country/is.ex
Normal file
45
lib/iban_ex/country/is.ex
Normal file
@@ -0,0 +1,45 @@
|
||||
defmodule IbanEx.Country.IS do
|
||||
# !TODO Iceland IBAN contains identification number (last 10 digits of account number)
|
||||
|
||||
@moduledoc """
|
||||
Island IBAN parsing rules
|
||||
|
||||
## Examples
|
||||
|
||||
```elixir
|
||||
iex> %IbanEx.Iban{
|
||||
...> country_code: "IS",
|
||||
...> check_digits: "14",
|
||||
...> bank_code: "0159",
|
||||
...> branch_code: "26",
|
||||
...> national_check: nil,
|
||||
...> account_number: "0076545510730339"
|
||||
...> }
|
||||
...> |> IbanEx.Country.IS.to_string()
|
||||
"IS 14 0159 26 0076545510730339"
|
||||
```
|
||||
"""
|
||||
|
||||
@size 26
|
||||
@rule ~r/^(?<bank_code>[0-9]{4})(?<branch_code>[0-9]{2})(?<account_number>[0-9]{16})$/i
|
||||
|
||||
use IbanEx.Country.Template
|
||||
|
||||
@impl IbanEx.Country.Template
|
||||
@spec to_string(Iban.t()) :: binary()
|
||||
@spec to_string(Iban.t(), binary()) :: binary()
|
||||
def to_string(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
||||
@@ -9,6 +9,10 @@ defmodule IbanEx.Error do
|
||||
| :can_not_parse_map
|
||||
| :length_to_long
|
||||
| :length_to_short
|
||||
| :invalid_bank_code
|
||||
| :invalid_account_number
|
||||
| :invalid_branch_code
|
||||
| :invalid_national_check
|
||||
| atom()
|
||||
@type errors() :: [error()]
|
||||
@errors [
|
||||
@@ -18,7 +22,11 @@ defmodule IbanEx.Error do
|
||||
:invalid_checksum,
|
||||
:can_not_parse_map,
|
||||
:length_to_long,
|
||||
:length_to_short
|
||||
:length_to_short,
|
||||
:invalid_bank_code,
|
||||
:invalid_account_number,
|
||||
:invalid_branch_code,
|
||||
:invalid_national_check
|
||||
]
|
||||
|
||||
@messages [
|
||||
@@ -28,8 +36,12 @@ defmodule IbanEx.Error do
|
||||
invalid_checksum: "IBAN's checksum is invalid",
|
||||
can_not_parse_map: "Can't parse map to IBAN struct",
|
||||
length_to_long: "IBAN longer then required length",
|
||||
length_to_short: "IBAN shorter then required length"
|
||||
]
|
||||
length_to_short: "IBAN shorter then required length",
|
||||
invalid_bank_code: "Bank code violates required format",
|
||||
invalid_account_number: "Account number violates required format",
|
||||
invalid_branch_code: "Branch code violates required format",
|
||||
invalid_national_check: "National check symbols violates required format",
|
||||
]
|
||||
|
||||
@spec message(error()) :: String.t()
|
||||
def message(error) when error in @errors, do: @messages[error]
|
||||
|
||||
@@ -69,8 +69,9 @@ def parse_bban(bban_string, country_code, options \\ [incomplete: false])
|
||||
def parse_bban(bban_string, country_code, incomplete: true) do
|
||||
case Country.is_country_code_supported?(country_code) do
|
||||
true ->
|
||||
parse_bban_by_rules(bban_string, Country.country_module(country_code))
|
||||
|
||||
country_code
|
||||
|> Country.country_module()
|
||||
|> parse_bban_by_country_rules(bban_string)
|
||||
false ->
|
||||
%{}
|
||||
end
|
||||
@@ -81,13 +82,12 @@ def parse_bban(bban_string, country_code, incomplete: false) do
|
||||
true ->
|
||||
Country.country_module(country_code).rule()
|
||||
|> parse_bban_by_regex(bban_string)
|
||||
|
||||
false ->
|
||||
%{}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_bban_by_rules(bban_string, country_module) do
|
||||
defp parse_bban_by_country_rules(country_module, bban_string) do
|
||||
for {field, rule} <- country_module.rules,
|
||||
into: %{},
|
||||
do: {field, normalize_and_slice(bban_string, rule.range)}
|
||||
|
||||
@@ -3,10 +3,11 @@ defmodule IbanEx.Validator do
|
||||
|
||||
alias IbanEx.{Country, Parser}
|
||||
alias IbanEx.Validator.Replacements
|
||||
import IbanEx.Commons, only: [normalize: 1]
|
||||
import IbanEx.Commons, only: [normalize: 1, normalize_and_slice: 2]
|
||||
|
||||
defp error_accumulator(acc, error_message)
|
||||
defp error_accumulator(acc, {:error, error}), do: [error | acc]
|
||||
# defp error_accumulator(acc, list) when is_list(list), do: list ++ acc
|
||||
defp error_accumulator(acc, _), do: acc
|
||||
|
||||
defp violation_functions(),
|
||||
@@ -15,12 +16,25 @@ defp violation_functions(),
|
||||
{&__MODULE__.iban_unsupported_country?/1, {:error, :unsupported_country_code}},
|
||||
{&__MODULE__.iban_violates_length?/1, {:error, :invalid_length}},
|
||||
{&__MODULE__.iban_violates_country_rule?/1, {:error, :invalid_format_for_country}},
|
||||
{&__MODULE__.iban_violates_checksum?/1, {:error, :invalid_checksum}}
|
||||
{&__MODULE__.iban_violates_bank_code_format?/1, {:error, :invalid_bank_code}},
|
||||
{&__MODULE__.iban_violates_account_number_format?/1, {:error, :invalid_account_number}},
|
||||
{&__MODULE__.iban_violates_branch_code_format?/1, {:error, :invalid_branch_code}},
|
||||
{&__MODULE__.iban_violates_national_check_format?/1, {:error, :invalid_national_check}},
|
||||
{&__MODULE__.iban_violates_checksum?/1, {:error, :invalid_checksum}},
|
||||
]
|
||||
|
||||
@doc """
|
||||
Accumulate check results in the list of errors
|
||||
Check iban_violates_format?, iban_unsupported_country?, iban_violates_length?, iban_violates_country_rule?, iban_violates_checksum?
|
||||
Check
|
||||
iban_violates_format?,
|
||||
iban_unsupported_country?,
|
||||
iban_violates_length?,
|
||||
iban_violates_country_rule?,
|
||||
iban_violates_bank_code_format?,
|
||||
iban_violates_account_number_format?
|
||||
iban_violates_branch_code_format?,
|
||||
iban_violates_national_check_format?,
|
||||
iban_violates_checksum?,
|
||||
"""
|
||||
@spec violations(String.t()) :: [] | [atom()]
|
||||
def violations(iban) do
|
||||
@@ -36,8 +50,11 @@ def violations(iban) do
|
||||
iban_unsupported_country?,
|
||||
iban_violates_length?,
|
||||
iban_violates_country_rule?,
|
||||
iban_violates_checksum?
|
||||
|
||||
iban_violates_bank_code_format?,
|
||||
iban_violates_account_number_format?,
|
||||
iban_violates_branch_code_format?,
|
||||
iban_violates_national_check_format?,
|
||||
iban_violates_checksum?,
|
||||
"""
|
||||
@type iban() :: binary()
|
||||
@type iban_or_error() ::
|
||||
@@ -46,6 +63,10 @@ def violations(iban) do
|
||||
| {:invalid_format, binary()}
|
||||
| {:invalid_length, binary()}
|
||||
| {:unsupported_country_code, binary()}
|
||||
| {:invalid_bank_code, binary()}
|
||||
| {:invalid_account_number, binary()}
|
||||
| {:invalid_branch_code, binary()}
|
||||
| {:invalid_national_check, binary()}
|
||||
@spec validate(String.t()) :: {:ok, String.t()} | {:error, atom()}
|
||||
|
||||
def validate(iban) do
|
||||
@@ -54,6 +75,10 @@ def validate(iban) do
|
||||
iban_unsupported_country?(iban) -> {:error, :unsupported_country_code}
|
||||
iban_violates_length?(iban) -> {:error, :invalid_length}
|
||||
iban_violates_country_rule?(iban) -> {:error, :invalid_format_for_country}
|
||||
iban_violates_bank_code_format?(iban) -> {:error, :invalid_bank_code}
|
||||
iban_violates_account_number_format?(iban) -> {:error, :invalid_account_number}
|
||||
iban_violates_branch_code_format?(iban) -> {:error, :invalid_branch_code}
|
||||
iban_violates_national_check_format?(iban) -> {:error, :invalid_national_check}
|
||||
iban_violates_checksum?(iban) -> {:error, :invalid_checksum}
|
||||
true -> {:ok, normalize(iban)}
|
||||
end
|
||||
@@ -71,6 +96,34 @@ defp size(iban) do
|
||||
def iban_violates_format?(iban),
|
||||
do: Regex.match?(~r/[^A-Z0-9]/i, normalize(iban))
|
||||
|
||||
# - Check whether a given IBAN violates the required format in bank_code.
|
||||
@spec iban_violates_bank_code_format?(binary()) :: boolean
|
||||
def iban_violates_bank_code_format?(iban), do: iban_violates_bban_part_format?(iban, :bank_code)
|
||||
|
||||
# - Check whether a given IBAN violates the required format in branch_code.
|
||||
@spec iban_violates_branch_code_format?(binary()) :: boolean
|
||||
def iban_violates_branch_code_format?(iban), do: iban_violates_bban_part_format?(iban, :branch_code)
|
||||
|
||||
# - Check whether a given IBAN violates the required format in account_number.
|
||||
@spec iban_violates_account_number_format?(binary()) :: boolean
|
||||
def iban_violates_account_number_format?(iban), do: iban_violates_bban_part_format?(iban, :account_number)
|
||||
|
||||
# - Check whether a given IBAN violates the required format in national_check.
|
||||
@spec iban_violates_national_check_format?(binary()) :: boolean
|
||||
def iban_violates_national_check_format?(iban), do: iban_violates_bban_part_format?(iban, :national_check)
|
||||
|
||||
defp iban_violates_bban_part_format?(iban, part) do
|
||||
with country <- Parser.country_code(iban),
|
||||
bban <- Parser.bban(iban),
|
||||
true <- Country.is_country_code_supported?(country),
|
||||
country_module <- Country.country_module(country),
|
||||
{:ok, rule} <- Map.fetch(country_module.rules_map(), part) do
|
||||
!Regex.match?(rule.regex, normalize_and_slice(bban, rule.range))
|
||||
else
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
# - Check whether a given IBAN violates the supported countries.
|
||||
@spec iban_unsupported_country?(String.t()) :: boolean
|
||||
def iban_unsupported_country?(iban) do
|
||||
|
||||
Reference in New Issue
Block a user