diff --git a/README.md b/README.md index 20de471..7cb663e 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,18 @@ config :money, ] ``` +### Custom Currencies Display Options + +In some cases, we may need to display the currency in a different way than the default format. For example, we may want to display the currency symbol on the right side of the amount for some currencies. To achieve this, you can add the following configuration: + +```elixir +config :money, + custom_display_options: [ + EUR: %{symbol_on_right: true, symbol_space: true, separator: ".", delimiter: ",", symbol: true}, + JPY: %{symbol_on_right: true, separator: ".", delimiter: ",", symbol: true} + ] +``` + ## Troubleshooting ### Validating amount in Ecto changeset diff --git a/lib/money/display_options.ex b/lib/money/display_options.ex index 425c6c6..b476c8b 100644 --- a/lib/money/display_options.ex +++ b/lib/money/display_options.ex @@ -31,8 +31,29 @@ defmodule Money.DisplayOptions do @enforce_keys @all_fields defstruct @all_fields + defp custom_display_options do + Enum.into(Application.get_env(:money, :custom_display_options, []), %{}) + end + @spec get(Money.t(), Keyword.t()) :: t() def get(%Money{} = money, opts) do + custom_currency_display_options = get_currency_custom_display_options(money.currency) + + opts_with_symbol = + case Keyword.get(opts, :symbol) do + nil -> opts + true -> Keyword.put(opts, :symbol, Currency.symbol(money.currency)) + false -> Keyword.put(opts, :symbol, "") + end + + money + |> get_defaults(opts) + |> Map.merge(custom_currency_display_options) + |> Map.merge(Enum.into(opts_with_symbol, %{})) + end + + @spec get_defaults(Money.t(), Keyword.t()) :: t() + defp get_defaults(%Money{} = money, opts) do %{separator: separator, delimiter: delimiter} = ParseOptions.get(opts) default_symbol = Application.get_env(:money, :symbol, true) @@ -47,27 +68,28 @@ defmodule Money.DisplayOptions do Application.get_env(:money, :strip_insignificant_fractional_unit, false) symbol = if Keyword.get(opts, :symbol, default_symbol), do: Currency.symbol(money), else: "" - symbol_on_right = Keyword.get(opts, :symbol_on_right, default_symbol_on_right) - symbol_space = Keyword.get(opts, :symbol_space, default_symbol_space) - fractional_unit = Keyword.get(opts, :fractional_unit, default_fractional_unit) - strip_insignificant_zeros = Keyword.get(opts, :strip_insignificant_zeros, default_strip_insignificant_zeros) - code = Keyword.get(opts, :code, default_code) - minus_sign_first = Keyword.get(opts, :minus_sign_first, default_minus_sign_first) - - strip_insignificant_fractional_unit = - Keyword.get(opts, :strip_insignificant_fractional_unit, default_strip_insignificant_fractional_unit) %__MODULE__{ separator: separator, delimiter: delimiter, symbol: symbol, - symbol_on_right: symbol_on_right, - symbol_space: symbol_space, - fractional_unit: fractional_unit, - strip_insignificant_zeros: strip_insignificant_zeros, - code: code, - minus_sign_first: minus_sign_first, - strip_insignificant_fractional_unit: strip_insignificant_fractional_unit + symbol_on_right: default_symbol_on_right, + symbol_space: default_symbol_space, + fractional_unit: default_fractional_unit, + strip_insignificant_zeros: default_strip_insignificant_zeros, + code: default_code, + minus_sign_first: default_minus_sign_first, + strip_insignificant_fractional_unit: default_strip_insignificant_fractional_unit } end + + defp get_currency_custom_display_options(currency) do + display_options = Map.get(custom_display_options(), currency, %{}) + + case display_options[:symbol] do + nil -> display_options + true -> Map.put(display_options, :symbol, Currency.symbol(currency)) + false -> Map.put(display_options, :symbol, "") + end + end end diff --git a/test/money_test.exs b/test/money_test.exs index 5ca8f85..3f42e7f 100644 --- a/test/money_test.exs +++ b/test/money_test.exs @@ -3,7 +3,7 @@ defmodule MoneyTest do doctest Money require Money.Currency - import Money.Currency, only: [usd: 1, eur: 1, clf: 1, jpy: 1, omr: 1, xau: 1, zar: 1] + import Money.Currency, only: [usd: 1, eur: 1, clf: 1, jpy: 1, omr: 1, xau: 1, zar: 1, ars: 1, pen: 1, brl: 1] test "new/1 with default currency set" do try do @@ -339,6 +339,55 @@ defmodule MoneyTest do end end + test "to_string configuration with custom_display_options" do + try do + # Default configuration + Application.put_env(:money, :separator, ",") + Application.put_env(:money, :delimeter, ".") + Application.put_env(:money, :symbol, false) + Application.put_env(:money, :symbol_on_right, false) + Application.put_env(:money, :symbol_space, false) + Application.put_env(:money, :fractional_unit, true) + Application.put_env(:money, :strip_insignificant_zeros, false) + + # Custom configuration + Application.put_env(:money, :custom_display_options, + EUR: %{symbol: true, symbol_on_right: true, symbol_space: true, separator: ".", delimiter: ","}, + JPY: %{symbol: true, symbol_on_right: true, separator: ","}, + ARS: %{symbol: true, symbol_on_right: false, separator: ".", delimiter: ","}, + PEN: %{symbol: true, symbol_on_right: false, symbol_space: true, separator: ",", delimiter: "."}, + BRL: %{symbol: false, separator: ".", delimiter: ","} + ) + + # default configuration test + assert Money.to_string(usd(1_234_567_890)) == "12,345,678.90" + + # custom configuration test + assert Money.to_string(eur(1_234_567_890)) == "12.345.678,90 €" + assert Money.to_string(jpy(1_234_567_890)) == "1,234,567,890¥" + assert Money.to_string(ars(1_234_567_890)) == "$12.345.678,90" + assert Money.to_string(pen(1_234_567_890)) == "S/ 12,345,678.90" + assert Money.to_string(brl(1_234_567_8900)) == "123.456.789,00" + + # overwriting with options + assert Money.to_string(eur(1_234_567_890), symbol: false) == "12.345.678,90" + assert Money.to_string(jpy(1_234_567_890), symbol_space: true) == "1,234,567,890 ¥" + assert Money.to_string(ars(1_234_567_890), symbol_on_right: true) == "12.345.678,90$" + assert Money.to_string(pen(1_234_567_890), fractional_unit: false, symbol_space: false) == "S/12,345,678" + assert Money.to_string(brl(1_234_567_8900), symbol: true, strip_insignificant_zeros: true) == "R$123.456.789" + after + Application.delete_env(:money, :separator) + Application.delete_env(:money, :delimeter) + Application.delete_env(:money, :symbol) + Application.delete_env(:money, :symbol_on_right) + Application.delete_env(:money, :symbol_space) + Application.delete_env(:money, :fractional_unit) + Application.delete_env(:money, :strip_insignificant_zeros) + + Application.delete_env(:money, :custom_display_options) + end + end + test "to_string configuration with old delimeter" do try do Application.put_env(:money, :separator, ".")