Skip to content

Commit

Permalink
feat: added custom_display_options configuration (#225)
Browse files Browse the repository at this point in the history
* feat: added custom_display_options configuration

* chore: updated code to receive a boolean in the custom_display_options symbol

* added more tests

* updated README file

* Update README.md

Co-authored-by: Arturo Medina Merino <contact@arturomedina.es>

* added more tests

---------

Co-authored-by: Gabriel Perales <gabriel.perales@cabify.com>
Co-authored-by: Arturo Medina Merino <contact@arturomedina.es>
  • Loading branch information
3 people authored Jul 9, 2024
1 parent c2d7414 commit 4ad07e9
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 17 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 38 additions & 16 deletions lib/money/display_options.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
51 changes: 50 additions & 1 deletion test/money_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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, ".")
Expand Down

0 comments on commit 4ad07e9

Please sign in to comment.