Current section
Files
Jump to
Current section
Files
lib/invoicex/extra/extra.ex
defmodule Invoicex.Extra do
@moduledoc """
A helper module for `Extra` context.
"""
alias Cldr.Territory
alias Ecto.Schema
data =
Territory.country_codes()
|> Enum.flat_map(fn code ->
Enum.map(Territory.info!(code).currency, &Tuple.insert_at(&1, 0, code))
end)
|> Enum.group_by(fn {code, currency, _} -> {code, currency} end, &elem(&1, 2))
@doc """
Parses number string with country and type check.
"""
@spec parse_number(String.t(), atom, atom) :: {:ok, Schema.t()} | {:error, String.t()}
def parse_number(number, country_iso, type \\ :mobile),
do: number |> ExPhoneNumber.parse(country_iso) |> do_parse_number(type)
defp do_parse_number({:error, _message} = error, _type), do: error
defp do_parse_number({:ok, number}, type),
do: number |> ExPhoneNumber.is_valid_number?() |> do_parse_number(number, type)
defp do_parse_number(false, _number, _type), do: {:error, "Number is not valid"}
defp do_parse_number(true, number, type),
do: number |> ExPhoneNumber.get_number_type() |> check_type(number, type)
defp check_type(real_type, number, type) when real_type == type, do: {:ok, number}
defp check_type(real_type, _number, type),
do: {:error, "Expected type to be `#{type}`, but found `#{real_type}`"}
@doc """
Checks if currency is valid in country at date.
"""
@spec valid_currency?(atom, atom, Date.t(), boolean) :: boolean
def valid_currency?(country_code, currency, date \\ Date.utc_today(), require_tender \\ true)
for {{code, currency}, opts} <- data do
escaped_opts = Macro.escape(opts)
def valid_currency?(unquote(code), unquote(currency), %Date{} = date, require_tender),
do: do_valid_currency?(date, unquote(escaped_opts), require_tender)
end
def valid_currency?(_code, _currency, _date, _require_tender), do: false
defp do_valid_currency?(date, opts, require_tender) when is_list(opts),
do: Enum.any?(opts, &do_valid_currency?(date, &1, require_tender))
defp do_valid_currency?(_date, %{tender: false}, true), do: false
defp do_valid_currency?(date, %{from: from, to: to}, _), do: date_between?(date, from, to)
defp do_valid_currency?(date, %{from: from}, _), do: date_ge?(date, from)
defp do_valid_currency?(date, %{to: to}, _), do: date_le?(date, to)
defp date_between?(date, from, to), do: date_ge?(date, from) and date_le?(date, to)
defp date_ge?(first, second), do: Date.compare(first, second) in [:eq, :gt]
defp date_le?(first, second), do: Date.compare(first, second) in [:eq, :lt]
end