Money v0.7.0 Money View Source

Money implements a set of functions to store, retrieve and perform arithmetic on a %Money{} type that is composed of a currency code and a currency amount.

Money is very opinionated in the interests of serving as a dependable library that can underpin accounting and financial applications. In its initial release it can be expected that this contract may not be fully met.

How is this opinion expressed:

  1. Money must always have both a amount and a currency code.

  2. The currency code must always be valid.

  3. Money arithmetic can only be performed when both operands are of the same currency.

  4. Money amounts are represented as a Decimal.

  5. Money is serialised to the database as a custom Postgres composite type that includes both the amount and the currency. Therefore for Ecto serialization Postgres is assumed as the data store. Serialization is entirely optional and Ecto is not a package dependency.

  6. All arithmetic functions work in fixed point decimal. No rounding occurs automatically (unless expressly called out for a function).

  7. Explicit rounding obeys the rounding rules for a given currency. The rounding rules are defined by the Unicode consortium in its CLDR repository as implemented by the hex package ex_cldr. These rules define the number of fractional digits for a currency and the rounding increment where appropriate.

Link to this section Summary

Types

t()

Money is composed of an atom representation of an ISO4217 currency code and a Decimal representation of an amount

Functions

Add two Money values

Add two Money values and raise on error

Compares two Money values numerically. If the first number is greater than the second :gt is returned, if less than :lt is returned, if both numbers are equal :eq is returned

Compares two Money values numerically and raises on error

Compares two Money values numerically. If the first number is greater than the second #Integer<1> is returned, if less than Integer<-1> is returned. Otherwise, if both numbers are equal Integer<0> is returned

Compares two Money values numerically and raises on error

Divide a Money value by a number

Divide a Money value by a number and raise on error

Returns a boolean indicating if two Money values are equal

Returns a %Money{} struct from a tuple consistenting of a currency code and a currency amount. The format of the argument is a 2-tuple where

Returns a %Money{} struct from a tuple consistenting of a currency code and a currency amount. Raises an exception if the currency code is invalid

Multiply a Money value by a number

Multiply a Money value by a number and raise on error

Returns a %Money{} struct from a currency code and a currency amount or an error tuple of the form {:error, {exception, message}}

Returns a %Money{} struct from a currency code and a currency amount. Raises an exception if the current code is invalid

Calls Decimal.reduce/1 on the given %Money{}

Round a Money value into the acceptable range for the defined currency

Split a Money value into a number of parts maintaining the currency’s precision and rounding and ensuring that the parts sum to the original amount

Subtract one Money value struct from another

Subtract one Money value struct from another and raise on error

Convert money from one currency to another and raises on error

Returns the amount part of a Money type as a Decimal

Returns a formatted string representation of a Money{}

Returns a formatted string representation of a Money{} or raises if there is an error

Link to this section Types

Link to this type currency_code() View Source
currency_code() :: atom()
Link to this type t() View Source
t() :: %Money{amount: Decimal.t(), currency: atom()}

Money is composed of an atom representation of an ISO4217 currency code and a Decimal representation of an amount.

Link to this section Functions

Add two Money values.

Example

iex> Money.add Money.new(:USD, 200), Money.new(:USD, 100)
{:ok, Money.new(:USD, 300)}

iex> Money.add Money.new(:USD, 200), Money.new(:AUD, 100)
{:error, {ArgumentError, "Cannot add monies with different currencies. " <>
  "Received :USD and :AUD."}}

Add two Money values and raise on error.

Examples

iex> Money.add! Money.new(:USD, 200), Money.new(:USD, 100)
#Money<:USD, 300>

Money.add! Money.new(:USD, 200), Money.new(:CAD, 500)
** (ArgumentError) Cannot add two %Money{} with different currencies. Received :USD and :CAD.
Link to this function cmp(money1, money2) View Source
cmp(Money.t(), Money.t()) ::
  :gt |
  :eq |
  :lt |
  {:error, {Exception.t(), String.t()}}

Compares two Money values numerically. If the first number is greater than the second :gt is returned, if less than :lt is returned, if both numbers are equal :eq is returned.

Examples

iex> Money.cmp Money.new(:USD, 200), Money.new(:USD, 100)
:gt

iex> Money.cmp Money.new(:USD, 200), Money.new(:USD, 200)
:eq

iex> Money.cmp Money.new(:USD, 200), Money.new(:USD, 500)
:lt

iex> Money.cmp Money.new(:USD, 200), Money.new(:CAD, 500)
{:error,
 {ArgumentError,
  "Cannot compare monies with different currencies. Received :USD and :CAD."}}

Compares two Money values numerically and raises on error.

Examples

Money.cmp! Money.new(:USD, 200), Money.new(:CAD, 500)
** (ArgumentError) Cannot compare monies with different currencies. Received :USD and :CAD.
Link to this function compare(money1, money2) View Source
compare(Money.t(), Money.t()) ::
  -1 |
  0 |
  1 |
  {:error, {Exception.t(), String.t()}}

Compares two Money values numerically. If the first number is greater than the second #Integer<1> is returned, if less than Integer<-1> is returned. Otherwise, if both numbers are equal Integer<0> is returned.

Examples

iex> Money.compare Money.new(:USD, 200), Money.new(:USD, 100)
1

iex> Money.compare Money.new(:USD, 200), Money.new(:USD, 200)
0

iex> Money.compare Money.new(:USD, 200), Money.new(:USD, 500)
-1

iex> Money.compare Money.new(:USD, 200), Money.new(:CAD, 500)
{:error,
 {ArgumentError,
  "Cannot compare monies with different currencies. Received :USD and :CAD."}}
Link to this function compare!(money_1, money_2) View Source

Compares two Money values numerically and raises on error.

Examples

Money.compare! Money.new(:USD, 200), Money.new(:CAD, 500)
** (ArgumentError) Cannot compare monies with different currencies. Received :USD and :CAD.

Divide a Money value by a number.

  • money is a %Money{} struct

  • number is an integer or float

Note that dividing one %Money{} by another is not supported.

Example

iex> Money.div Money.new(:USD, 200), 2
{:ok, Money.new(:USD, 100)}

iex> Money.div(Money.new(:USD, 200), "xx")
{:error, {ArgumentError, "Cannot divide money by \"xx\""}}

Divide a Money value by a number and raise on error.

Examples

iex> Money.div Money.new(:USD, 200), 2
{:ok, Money.new(:USD, 100)}

Money.div(Money.new(:USD, 200), "xx")
** (ArgumentError) "Cannot divide money by \"xx\""]}}
Link to this function equal?(arg1, arg2) View Source
equal?(Money.t(), Money.t()) :: boolean()

Returns a boolean indicating if two Money values are equal

Example

iex> Money.equal? Money.new(:USD, 200), Money.new(:USD, 200)
true

iex> Money.equal? Money.new(:USD, 200), Money.new(:USD, 100)
false
Link to this function from_tuple(arg) View Source
from_tuple({binary(), number()}) :: Money.t()

Returns a %Money{} struct from a tuple consistenting of a currency code and a currency amount. The format of the argument is a 2-tuple where:

  • currency_code is an ISO4217 three-character upcased binary

  • amount is an integer, float or Decimal

This function is typically called from Ecto when it’s loading a %Money{} struct from the database.

Example

iex> Money.from_tuple({"USD", 100})
#Money<:USD, 100>

iex> Money.from_tuple({100, "USD"})
#Money<:USD, 100>

Returns a %Money{} struct from a tuple consistenting of a currency code and a currency amount. Raises an exception if the currency code is invalid.

  • currency_code is an ISO4217 three-character upcased binary

  • amount is an integer, float or Decimal

This function is typically called from Ecto when it’s loading a %Money{} struct from the database.

Example

iex> Money.from_tuple!({"USD", 100})
#Money<:USD, 100>

Money.from_tuple!({"NO!", 100})
** (Money.UnknownCurrencyError) Currency "NO!" is not known
    (ex_money) lib/money.ex:130: Money.new!/1
Link to this function get_env(key, default, atom) View Source

Multiply a Money value by a number.

  • money is a %Money{} struct

  • number is an integer or float

Note that multipling one %Money{} by another is not supported.

Returns either {:ok, money} or {:error, reason}.

Example

iex> Money.mult(Money.new(:USD, 200), 2)
{:ok, Money.new(:USD, 400)}

iex> Money.mult(Money.new(:USD, 200), "xx")
{:error, {ArgumentError, "Cannot multiply money by \"xx\""}}

Multiply a Money value by a number and raise on error.

Examples

iex> Money.mult!(Money.new(:USD, 200), 2)
#Money<:USD, 400>

Money.mult!(Money.new(:USD, 200), :invalid)
** (ArgumentError) Cannot multiply money by :invalid
Link to this function new(currency_code, amount) View Source
new(number(), binary()) :: Money.t()

Returns a %Money{} struct from a currency code and a currency amount or an error tuple of the form {:error, {exception, message}}.

  • currency_code is an ISO4217 three-character upcased binary or atom

  • amount is an integer, float or Decimal

Note that the currency_code and amount arguments can be supplied in either order,

Examples

iex> Money.new(:USD, 100)
#Money<:USD, 100>

iex> Money.new(100, :USD)
#Money<:USD, 100>

iex> Money.new("USD", 100)
#Money<:USD, 100>

iex> Money.new("thb", 500)
#Money<:THB, 500>

iex> Money.new(500, "thb")
#Money<:THB, 500>

iex> Money.new("EUR", Decimal.new(100))
#Money<:EUR, 100>

iex> Money.new(:XYZZ, 100)
{:error, {Money.UnknownCurrencyError, "Currency :XYZZ is not known"}}
Link to this function new!(currency_code, amount) View Source

Returns a %Money{} struct from a currency code and a currency amount. Raises an exception if the current code is invalid.

  • currency_code is an ISO4217 three-character upcased binary or atom

  • amount is an integer, float or Decimal

Examples

Money.new!(:XYZZ, 100)
** (Money.UnknownCurrencyError) Currency :XYZZ is not known
  (ex_money) lib/money.ex:177: Money.new!/2

Calls Decimal.reduce/1 on the given %Money{}

This will reduce the coefficient and exponent of the decimal amount in a standard way that may aid in native comparison of %Money{} items.

Example

iex> x = %Money{currency: :USD, amount: %Decimal{sign: 1, coef: 42, exp: 0}}
#Money<:USD, 42>
iex> y = %Money{currency: :USD, amount: %Decimal{sign: 1, coef: 4200000000, exp: -8}}
#Money<:USD, 42.00000000>
iex> x == y
false
iex> y = Money.reduce(x)
#Money<:USD, 42>
iex> x == y
true
Link to this function round(money, opts \\ []) View Source
round(Money.t(), Keyword.t()) :: Money.t()

Round a Money value into the acceptable range for the defined currency.

  • money is a %Money{} struct

  • opts is a keyword list with the following keys:

    • :rounding_mode that defines how the number will be rounded. See Decimal.Context. The default is :half_even which is also known as “banker’s rounding”

    • :cash which determines whether the rounding is being applied to an accounting amount or a cash amount. Some currencies, such as the :AUD and :CHF have a cash unit increment minimum which requires a different rounding increment to an arbitrary accounting amount. The default is false.

There are two kinds of rounding applied:

  1. Round to the appropriate number of fractional digits

  2. Apply an appropriate rounding increment. Most currencies round to the same precision as the number of decimal digits, but some such as :AUD and :CHF round to a minimum such as 0.05 when its a cash amount.

Examples

iex> Money.round Money.new(123.7456, :CHF), cash: true
#Money<:CHF, 125>

iex> Money.round Money.new(123.7456, :CHF)
#Money<:CHF, 123.75>

Money.round Money.new(123.7456, :JPY)
#Money<:JPY, 124>
Link to this function split(money, parts) View Source
split(Money.t(), non_neg_integer()) :: {Money.t(), Money.t()}

Split a Money value into a number of parts maintaining the currency’s precision and rounding and ensuring that the parts sum to the original amount.

  • money is a %Money{} struct

  • parts is an integer number of parts into which the money is split

Returns a tuple {dividend, remainder} as the function result derived as follows:

  1. Round the money amount to the required currency precision using Money.round/1

  2. Divide the result of step 1 by the integer divisor

  3. Round the result of the division to the precision of the currency using Money.round/1

  4. Return two numbers: the result of the division and any remainder that could not be applied given the precision of the currency.

Examples

Money.split Money.new(123.5, :JPY), 3
{¥41, ¥1}

Money.split Money.new(123.4, :JPY), 3
{¥41, ¥0}

Money.split Money.new(123.7, :USD), 9
{$13.74, $0.04}
Link to this function sub(money1, money2) View Source
sub(Money.t(), Money.t()) ::
  {:ok, Money.t()} |
  {:error, {Exception.t(), String.t()}}

Subtract one Money value struct from another.

Returns either {:ok, money} or {:error, reason}.

Example

iex> Money.sub Money.new(:USD, 200), Money.new(:USD, 100)
{:ok, Money.new(:USD, 100)}

Subtract one Money value struct from another and raise on error.

Returns either {:ok, money} or {:error, reason}.

Examaples

iex> Money.sub! Money.new(:USD, 200), Money.new(:USD, 100)
#Money<:USD, 100>

Money.sub! Money.new(:USD, 200), Money.new(:CAD, 500)
** (ArgumentError) Cannot subtract monies with different currencies. Received :USD and :CAD.
Link to this function to_currency(money, to_currency, rates \\ Money.ExchangeRates.latest_rates()) View Source
to_currency(Money.t(), Money.currency_code(), Map.t()) ::
  {:ok, Map.t()} |
  {:error, {Exception.t(), String.t()}}

Convert money from one currency to another.

  • money is a %Money{} struct

  • to_currency is a valid currency code into which the money is converted

  • rates is a Map of currency rates where the map key is an upcased atom or string and the value is a Decimal conversion factor. The default is the latest available exchange rates returned from Money.ExchangeRates.latest_rates()

Examples

Money.to_currency(Money.new(:USD, 100), :AUD, %{USD: Decimal.new(1), AUD: Decimal.new(0.7345)})
{:ok, #Money<:AUD, 73.4500>}

Money.to_currency(Money.new("USD", 100), "AUD", %{"USD" => Decimal.new(1), "AUD" => Decimal.new(0.7345)})
{:ok, #Money<:AUD, 73.4500>}

iex> Money.to_currency Money.new(:USD, 100) , :AUDD, %{USD: Decimal.new(1), AUD: Decimal.new(0.7345)}
{:error, {Cldr.UnknownCurrencyError, "Currency :AUDD is not known"}}

iex> Money.to_currency Money.new(:USD, 100) , :CHF, %{USD: Decimal.new(1), AUD: Decimal.new(0.7345)}
{:error, {Money.ExchangeRateError, "No exchange rate is available for currency :CHF"}}
Link to this function to_currency!(money, currency) View Source

Convert money from one currency to another and raises on error

Examples

iex> Money.to_currency! Money.new(:USD, 100) , :AUD, %{USD: Decimal.new(1), AUD: Decimal.new(0.7345)}
#Money<:AUD, 73.4500>

iex> Money.to_currency! Money.new("USD", 100) , "AUD", %{"USD" => Decimal.new(1), "AUD" => Decimal.new(0.7345)}
#Money<:AUD, 73.4500>

Money.to_currency! Money.new(:USD, 100) , :ZZZ, %{USD: Decimal.new(1), AUD: Decimal.new(0.7345)}
** (Cldr.UnknownCurrencyError) Currency :ZZZ is not known
Link to this function to_currency!(money, currency, rates) View Source
Link to this function to_decimal(money) View Source
to_decimal(Money.t()) :: Decimal.t()

Returns the amount part of a Money type as a Decimal

Example

iex> m = Money.new("USD", 100)
iex> Money.to_decimal(m)
#Decimal<100>
Link to this function to_string(money, options \\ []) View Source

Returns a formatted string representation of a Money{}.

Formatting is performed according to the rules defined by CLDR. See Cldr.Number.to_string/2 for formatting options. The default is to format as a currency which applies the appropriate rounding and fractional digits for the currency.

Examples

iex> Money.to_string Money.new(:USD, 1234)
{:ok, "$1,234.00"}

iex> Money.to_string Money.new(:JPY, 1234)
{:ok, "¥1,234"}

iex> Money.to_string Money.new(:THB, 1234)
{:ok, "THB1,234.00"}

iex> Money.to_string Money.new(:USD, 1234), format: :long
{:ok, "1,234 US dollars"}
Link to this function to_string!(money, options \\ []) View Source

Returns a formatted string representation of a Money{} or raises if there is an error.

Formatting is performed according to the rules defined by CLDR. See Cldr.Number.to_string!/2 for formatting options. The default is to format as a currency which applies the appropriate rounding and fractional digits for the currency.

Examples

iex> Money.to_string! Money.new(:USD, 1234)
"$1,234.00"

iex> Money.to_string! Money.new(:JPY, 1234)
"¥1,234"

iex> Money.to_string! Money.new(:THB, 1234)
"THB1,234.00"

iex> Money.to_string! Money.new(:USD, 1234), format: :long
"1,234 US dollars"
Link to this function validate_currency_code(currency_code) View Source