Current section

Files

Jump to
monetized lib money.ex
Raw

lib/money.ex

defmodule Monetized.Money do
import Monetized.Money.Utils
import Monetized.Currency
@moduledoc """
Defines the money struct and functions to handle it.
Although we're able to override any configuration when
calling functions that create/handle money, it is possible
to change any of the default values seen below, through config.
## Examples
config :monetized, config: [
delimiter: ",",
separator: ".",
currency: "USD",
format: "%c %n%s%d"
]
"""
@typedoc """
A money struct containing the basic unit amount and
the currency key.
"""
@type money :: %Monetized.Money{}
defstruct currency: nil, units: nil
@doc """
Returns a string representation of the given money
## Examples
iex> money = Monetized.Money.make("20150.25", [currency: "GBP"])
...> Monetized.Money.to_string(money, [show_currency: true])
"£ 20,150.25"
iex> money = Monetized.Money.make(999999999)
...> Monetized.Money.to_string(money, [delimiter: " ", separator: " "])
"999 999 999 00"
iex> money = Monetized.Money.make(100_000_000, [currency: "USD"])
...> Monetized.Money.to_string(money, [format: "%n%s%d %c", show_currency: true])
"100,000,000.00 $"
iex> money = Monetized.Money.make(-9950, [currency: "USD", units: true])
...> Monetized.Money.to_string(money, [show_currency: true])
"$ -99.50"
"""
@spec to_string(money, list) :: String.t
def to_string(money, options \\ []) do
delimiter = option_or_config(config, options, :delimiter)
separator = option_or_config(config, options, :separator)
{base, decimal} = Integer.to_string(money.units)
|> String.split_at(-2)
number = String.to_integer(base)
|> delimit_integer(delimiter)
|> String.Chars.to_string
currency = if options[:show_currency] do
get_currency(money.currency).symbol
else
""
end
option_or_config(config, options, :format)
|> String.replace(~r/%c/, currency)
|> String.replace(~r/%n/, number)
|> String.replace(~r/%s/, separator)
|> String.replace(~r/%d/, decimal)
|> String.strip
end
@doc """
Creates a money struct from any of the supported
types for amount.
It uses the default currency ("USD") if one isn't
configured.
Passing `currency` in the options will make it use that
despite of configured or default.
This function exists for convenience and despite it taking
a float value, the internal calculations are done on
integers (basic units)
## Examples
iex> Monetized.Money.make("20150.25", [currency: "GBP"])
%Monetized.Money{currency: "GBP", units: 2015025}
iex> Monetized.Money.make(20150.25, [currency: "EUR"])
%Monetized.Money{currency: "EUR", units: 2015025}
iex> Monetized.Money.make(20150)
%Monetized.Money{currency: "USD", units: 2015000}
iex> Monetized.Money.make(-100.50)
%Monetized.Money{currency: "USD", units: -10050}
"""
@spec make(integer | float | String.t, list) :: money
def make(amount, options \\ []) do
do_make(amount, options)
end
defp do_make(amount, options) when is_bitstring(amount) do
from_string(amount, options)
end
defp do_make(amount, options) when is_integer(amount) do
from_integer(amount, options)
end
defp do_make(amount, options) when is_float(amount) do
from_float(amount, options)
end
@doc """
Creates a money struct from a string value.
It uses the default currency ("USD") if one isn't
configured.
Passing currency in the options will make it use that
despite of configured or default.
## Examples
iex> Monetized.Money.from_string("10.52", [currency: "GBP"])
%Monetized.Money{currency: "GBP", units: 1052}
iex> Monetized.Money.from_string("100", [currency: "EUR"])
%Monetized.Money{currency: "EUR", units: 10000}
"""
@spec from_string(String.t, list) :: money
def from_string(amount, options) do
{base, remainder} = Integer.parse(amount)
from_string(base, remainder, options)
end
defp from_string(amount, "", options) do
create(amount, options)
end
defp from_string(base, remainder, options) do
decimal = String.strip(remainder, ?.) |> String.to_integer
create(base, decimal, options)
end
@doc """
Creates a money struct from a integer value.
It uses the default currency ("USD") if one isn't
configured.
Passing currency in the options will make it use that
despite of configured or default.
## Examples
iex> Monetized.Money.from_integer(152, [currency: "GBP"])
%Monetized.Money{currency: "GBP", units: 15200}
iex> Monetized.Money.from_integer(152, [currency: "GBP"])
%Monetized.Money{currency: "GBP", units: 15200}
"""
@spec from_integer(integer, list) :: money
def from_integer(amount, options) do
create(amount, options)
end
@doc """
Creates a money struct from a float value.
It uses the default currency ("USD") if one isn't
configured.
Passing currency in the options will make it use that
despite of configured or default.
This function exists for convenience and despite it taking
a float value, the internal calculations are done on
integers (basic units)
## Examples
iex> Monetized.Money.from_float(100.00, [currency: "EUR"])
%Monetized.Money{currency: "EUR", units: 10000}
iex> Monetized.Money.from_float(150.52)
%Monetized.Money{currency: "USD", units: 15052}
iex> Monetized.Money.from_float(20.50)
%Monetized.Money{currency: "USD", units: 2050}
"""
@spec from_float(float, list) :: money
def from_float(amount, options \\ []) when is_float(amount) do
Float.to_string(amount, [decimals: 2])
|> from_string(options)
end
defp create(base, decimal, options) do
currency_key = option_or_config(config, options, :currency)
currency = get_currency(currency_key)
case is_negative?(base) do
true ->
amount = if options[:units], do: nf(base, decimal), else: nf(base, decimal, currency.to_unit)
false ->
amount = if options[:units], do: pf(base, decimal), else: pf(base, decimal, currency.to_unit)
end
do_create(amount, currency_key)
end
defp create(amount, options) do
currency_key = option_or_config(config, options, :currency)
currency = get_currency(currency_key)
unless options[:units] do
amount = amount * currency.to_unit
end
do_create(amount, currency_key)
end
defp do_create(amount, currency_key) do
%Monetized.Money{currency: currency_key, units: amount}
end
defp config do
defaults = [
delimiter: ",",
separator: ".",
currency: "USD",
format: "%c %n%s%d"
]
Dict.merge(defaults, Application.get_env(:Monetized_money, :config, []))
end
end