changed
CHANGELOG.md
|
@@ -2,6 2,20 @@
|
2
2
|
|
3
3
|
**Note** `ex_money` 5.17.0 and later is supported on Elixir 1.12 and later versions only.
|
4
4
|
|
5
|
## Money v5.17.1
|
6
|
|
7
|
This is the changelog for Money v5.17.1 released on September 6th, 2024. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/money/tags)
|
8
|
|
9
|
### Bug Fixes
|
10
|
|
11
|
* Update `poison` optional dependency to allow `~> 6.0`.
|
12
|
|
13
|
* Update `stream_data` test dependency to `~> 1.0`.
|
14
|
|
15
|
### Enhancements
|
16
|
|
17
|
* Improve specs and broaden dialyzer configuration.
|
18
|
|
5
19
|
## Money v5.17.0
|
6
20
|
|
7
21
|
This is the changelog for Money v5.17.0 released on May 28th, 2024. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/money/tags)
|
|
@@ -24,9 38,9 @@ This is the changelog for Money v5.16.0 released on April 21st, 2024. For older
|
24
38
|
|
25
39
|
### Bug Fixes
|
26
40
|
|
27
|
- * WHen parsing numbers, use the localized number system separators where they exist. Thanks to @pshoukry for the report. Closes #167.
|
41
|
* When parsing numbers, use the localized number system separators where they exist. Thanks to @pshoukry for the report. Closes #167.
|
28
42
|
|
29
|
- * Surface errors when starting the exchange rates retrieveer. Thanks to @danschultzer for the PR. Closes #165.
|
43
|
* Surface errors when starting the exchange rates retriever. Thanks to @danschultzer for the PR. Closes #165.
|
30
44
|
|
31
45
|
### Enhancements
|
32
46
|
|
|
@@ -84,7 98,7 @@ This is the changelog for Money v5.14.1 released on July 23rd, 2023. For older
|
84
98
|
|
85
99
|
### Bug Fixes
|
86
100
|
|
87
|
- * Fix `Looger.warn/1` warnings by moving to `Logger.warning/1`.
|
101
|
* Fix `Logger.warn/1` warnings by moving to `Logger.warning/1`.
|
88
102
|
|
89
103
|
* Fix failing test case.
|
changed
hex_metadata.config
|
@@ -1,11 1,11 @@
|
1
1
|
{<<"links">>,
|
2
2
|
[{<<"Changelog">>,
|
3
|
- <<"https://github.com/kipcole9/money/blob/v5.17.0/CHANGELOG.md">>},
|
3
|
<<"https://github.com/kipcole9/money/blob/v5.17.1/CHANGELOG.md">>},
|
4
4
|
{<<"GitHub">>,<<"https://github.com/kipcole9/money">>},
|
5
5
|
{<<"Readme">>,
|
6
|
- <<"https://github.com/kipcole9/money/blob/v5.17.0/README.md">>}]}.
|
6
|
<<"https://github.com/kipcole9/money/blob/v5.17.1/README.md">>}]}.
|
7
7
|
{<<"name">>,<<"ex_money">>}.
|
8
|
- {<<"version">>,<<"5.17.0">>}.
|
8
|
{<<"version">>,<<"5.17.1">>}.
|
9
9
|
{<<"description">>,
|
10
10
|
<<"Money functions for operations on and localization of a money data type with support\nfor ISO 4217 currencies and ISO 24165 digial tokens (crypto currencies).">>}.
|
11
11
|
{<<"elixir">>,<<"~> 1.12">>}.
|
|
@@ -54,7 54,7 @@
|
54
54
|
[{<<"name">>,<<"poison">>},
|
55
55
|
{<<"app">>,<<"poison">>},
|
56
56
|
{<<"optional">>,true},
|
57
|
- {<<"requirement">>,<<"~> 3.0 or ~> 4.0 or ~> 5.0">>},
|
57
|
{<<"requirement">>,<<"~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0">>},
|
58
58
|
{<<"repository">>,<<"hexpm">>}],
|
59
59
|
[{<<"name">>,<<"phoenix_html">>},
|
60
60
|
{<<"app">>,<<"phoenix_html">>},
|
changed
lib/money.ex
|
@@ -760,7 760,7 @@ defmodule Money do
|
760
760
|
|
761
761
|
"""
|
762
762
|
@spec to_string(Money.t(), Keyword.t() | Cldr.Number.Format.Options.t()) ::
|
763
|
- {:ok, String.t()} | {:error, {atom, String.t()}}
|
763
|
{:ok, String.t()} | {:error, {module, String.t()}}
|
764
764
|
|
765
765
|
def to_string(money, options \\ [])
|
766
766
|
|
|
@@ -1595,7 1595,7 @@ defmodule Money do
|
1595
1595
|
Money.new(:JPY, "124")
|
1596
1596
|
|
1597
1597
|
"""
|
1598
|
- @spec round(Money.t(), Keyword.t()) :: Money.t()
|
1598
|
@spec round(Money.t(), Keyword.t()) :: Money.t() | {:error, {module(), binary()}}
|
1599
1599
|
def round(money, opts \\ [])
|
1600
1600
|
|
1601
1601
|
# Digital tokens don't have rounding
|
|
@@ -2164,24 2164,28 @@ defmodule Money do
|
2164
2164
|
defp digits_from_options(currency_data, options) when is_list(options) do
|
2165
2165
|
{fractional_digits, options} = Keyword.pop(options, :fractional_digits)
|
2166
2166
|
|
2167
|
- with {:ok, digits} <- digits_from_options(currency_data, fractional_digits) do
|
2167
|
with {:ok, digits} <- do_digits_from_options(currency_data, fractional_digits) do
|
2168
2168
|
{:ok, -digits, options}
|
2169
|
else
|
2170
|
:error -> {:error, invalid_digits_error(fractional_digits)}
|
2171
|
other -> other
|
2169
2172
|
end
|
2170
2173
|
end
|
2171
2174
|
|
2172
|
- defp digits_from_options(currency_data, :iso), do: Map.fetch(currency_data, :iso_digits)
|
2173
|
- defp digits_from_options(currency_data, nil), do: Map.fetch(currency_data, :iso_digits)
|
2174
|
- defp digits_from_options(currency_data, :cash), do: Map.fetch(currency_data, :cash_digits)
|
2175
|
- defp digits_from_options(currency_data, :accounting), do: Map.fetch(currency_data, :digits)
|
2175
|
defp do_digits_from_options(currency_data, :iso), do: Map.fetch(currency_data, :iso_digits)
|
2176
|
defp do_digits_from_options(currency_data, nil), do: Map.fetch(currency_data, :iso_digits)
|
2177
|
defp do_digits_from_options(currency_data, :cash), do: Map.fetch(currency_data, :cash_digits)
|
2178
|
defp do_digits_from_options(currency_data, :accounting), do: Map.fetch(currency_data, :digits)
|
2176
2179
|
|
2177
|
- defp digits_from_options(_currency_data, integer) when is_integer(integer) and integer >= 0,
|
2180
|
defp do_digits_from_options(_currency_data, integer) when is_integer(integer) and integer >= 0,
|
2178
2181
|
do: {:ok, integer}
|
2179
2182
|
|
2180
|
- defp digits_from_options(_currency_data, other),
|
2181
|
- do:
|
2182
|
- {:error,
|
2183
|
- {Money.InvalidDigitsError,
|
2184
|
- "Unknown or invalid :fractional_digits option found: #{inspect(other)}"}}
|
2183
|
defp do_digits_from_options(_currency_data, other),
|
2184
|
do: {:error, invalid_digits_error(other)}
|
2185
|
|
2186
|
defp invalid_digits_error(other), do:
|
2187
|
{Money.InvalidDigitsError,
|
2188
|
"Unknown or invalid :fractional_digits option found: #{inspect(other)}"}
|
2185
2189
|
|
2186
2190
|
@doc """
|
2187
2191
|
Return a zero amount `t:Money.t/0` in the given currency.
|
|
@@ -2189,7 2193,7 @@ defmodule Money do
|
2189
2193
|
## Arguments
|
2190
2194
|
|
2191
2195
|
* `money_or_currency` is either a `t:Money.t/0` or
|
2192
|
- a currency code
|
2196
|
a currency code.
|
2193
2197
|
|
2194
2198
|
* `options` is a keyword list of options passed
|
2195
2199
|
to `Money.new/3`. The default is `[]`.
|
|
@@ -2207,7 2211,7 @@ defmodule Money do
|
2207
2211
|
{:error, {Cldr.UnknownCurrencyError, "The currency :ZZZ is invalid"}}
|
2208
2212
|
|
2209
2213
|
"""
|
2210
|
- @spec zero(currency_code | Money.t()) :: Money.t()
|
2214
|
@spec zero(currency_code | Money.t()) :: Money.t() | {:error, {module(), binary()}}
|
2211
2215
|
|
2212
2216
|
def zero(money_or_currency, options \\ [])
|
changed
lib/money/backend.ex
|
@@ -539,13 539,13 @@ defmodule Money.Backend do
|
539
539
|
## Options
|
540
540
|
|
541
541
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
542
|
- by `Money.new/2`
|
542
|
by `Money.new/2`.
|
543
543
|
|
544
544
|
## Returns
|
545
545
|
|
546
546
|
* `{:ok, money}` or
|
547
547
|
|
548
|
- * `{:error, reason}`
|
548
|
* `{:error, reason}`.
|
549
549
|
|
550
550
|
## Example
|
551
551
|
|
|
@@ -568,7 568,7 @@ defmodule Money.Backend do
|
568
568
|
## Arguments
|
569
569
|
|
570
570
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
571
|
- by `Money.new/2`
|
571
|
by `Money.new/2`.
|
572
572
|
|
573
573
|
## Returns
|
574
574
|
|
|
@@ -598,9 598,9 @@ defmodule Money.Backend do
|
598
598
|
## Arguments
|
599
599
|
|
600
600
|
* `money` is any valid `t:Money.t/0` type returned
|
601
|
- by `Money.new/2`
|
601
|
by `Money.new/2`.
|
602
602
|
|
603
|
- * `number` is an integer, float or `t:Decimal.t/0`
|
603
|
* `number` is an integer, float or `t:Decimal.t/0`.
|
604
604
|
|
605
605
|
> Note that multipling one `t:Money.t/0` by another is not supported.
|
606
606
|
|
|
@@ -632,7 632,7 @@ defmodule Money.Backend do
|
632
632
|
## Arguments
|
633
633
|
|
634
634
|
* `money` is any valid `t:Money.t/0` types returned
|
635
|
- by `Money.new/2`
|
635
|
by `Money.new/2`.
|
636
636
|
|
637
637
|
* `number` is an integer, float or `Decimal.t`
|
638
638
|
|
|
@@ -662,10 662,10 @@ defmodule Money.Backend do
|
662
662
|
|
663
663
|
## Arguments
|
664
664
|
|
665
|
- * `money` is any valid `t:Money.t/0` types returned
|
665
|
* `money` is any valid `t:Money.t/0` types returned.
|
666
666
|
by `Money.new/2`
|
667
667
|
|
668
|
- * `number` is an integer, float or `t:Decimal.t/0`
|
668
|
* `number` is an integer, float or `t:Decimal.t/0`.
|
669
669
|
|
670
670
|
> Note that dividing one `t:Money.t/0` by another is not supported.
|
671
671
|
|
|
@@ -699,7 699,7 @@ defmodule Money.Backend do
|
699
699
|
* `money` is any valid `t:Money.t/0` types returned
|
700
700
|
by `Money.new/2`
|
701
701
|
|
702
|
- * `number` is an integer, float or `Decimal.t`
|
702
|
* `number` is an integer, float or `t:Decimal.t/0`
|
703
703
|
|
704
704
|
## Returns
|
705
705
|
|
|
@@ -754,13 754,13 @@ defmodule Money.Backend do
|
754
754
|
## Arguments
|
755
755
|
|
756
756
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
757
|
- by `Money.new/2`
|
757
|
by `Money.new/2`.
|
758
758
|
|
759
759
|
## Returns
|
760
760
|
|
761
761
|
* `:gt` | `:eq` | `:lt` or
|
762
762
|
|
763
|
- * `{:error, {module(), String.t}}`
|
763
|
* `{:error, {module(), String.t}}`.
|
764
764
|
|
765
765
|
## Examples
|
766
766
|
|
|
@@ -791,13 791,13 @@ defmodule Money.Backend do
|
791
791
|
## Arguments
|
792
792
|
|
793
793
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
794
|
- by `Money.new/2`
|
794
|
by `Money.new/2`.
|
795
795
|
|
796
796
|
## Returns
|
797
797
|
|
798
798
|
* `:gt` | `:eq` | `:lt` or
|
799
799
|
|
800
|
- * raises an exception
|
800
|
* raises an exception.
|
801
801
|
|
802
802
|
## Examples
|
803
803
|
|
|
@@ -817,7 817,7 @@ defmodule Money.Backend do
|
817
817
|
## Arguments
|
818
818
|
|
819
819
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
820
|
- by `Money.new/2`
|
820
|
by `Money.new/2`.
|
821
821
|
|
822
822
|
## Returns
|
823
823
|
|
|
@@ -854,13 854,13 @@ defmodule Money.Backend do
|
854
854
|
## Arguments
|
855
855
|
|
856
856
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
857
|
- by `Money.new/2`
|
857
|
by `Money.new/2`.
|
858
858
|
|
859
859
|
## Returns
|
860
860
|
|
861
861
|
* `-1` | `0` | `1` or
|
862
862
|
|
863
|
- * raises an exception
|
863
|
* raises an exception.
|
864
864
|
|
865
865
|
## Examples
|
866
866
|
|
|
@@ -887,12 887,12 @@ defmodule Money.Backend do
|
887
887
|
derived as follows:
|
888
888
|
|
889
889
|
1. Round the money amount to the required currency precision using
|
890
|
- `Money.round/1`
|
890
|
`Money.round/1`.
|
891
891
|
|
892
|
- 2. Divide the result of step 1 by the integer divisor
|
892
|
2. Divide the result of step 1 by the integer divisor.
|
893
893
|
|
894
894
|
3. Round the result of the division to the precision of the currency
|
895
|
- using `Money.round/1`
|
895
|
using `Money.round/1`.
|
896
896
|
|
897
897
|
4. Return two numbers: the result of the division and any remainder
|
898
898
|
that could not be applied given the precision of the currency.
|
|
@@ -921,15 921,15 @@ defmodule Money.Backend do
|
921
921
|
|
922
922
|
## Arguments
|
923
923
|
|
924
|
- * `money` is a ``t:Money.t/0`` struct
|
924
|
* `money` is a `t:Money.t/0` struct.
|
925
925
|
|
926
|
- * `opts` is a keyword list of options
|
926
|
* `options` is a keyword list of options.
|
927
927
|
|
928
928
|
## Options
|
929
929
|
|
930
930
|
* `:rounding_mode` that defines how the number will be rounded. See
|
931
931
|
`Decimal.Context`. The default is `:half_even` which is also known
|
932
|
- as "banker's rounding"
|
932
|
as "banker's rounding".
|
933
933
|
|
934
934
|
* `:currency_digits` which determines the rounding increment.
|
935
935
|
The valid options are `:cash`, `:accounting` and `:iso` or
|
|
@@ -940,13 940,13 @@ defmodule Money.Backend do
|
940
940
|
|
941
941
|
There are two kinds of rounding applied:
|
942
942
|
|
943
|
- 1. Round to the appropriate number of fractional digits
|
943
|
1. Round to the appropriate number of fractional digits.
|
944
944
|
|
945
945
|
3. Apply an appropriate rounding increment. Most currencies
|
946
946
|
round to the same precision as the number of decimal digits, but some
|
947
947
|
such as `:CHF` round to a minimum such as `0.05` when its a cash
|
948
948
|
amount. The rounding increment is applied when the option
|
949
|
- `:currency_digits` is set to `:cash`
|
949
|
`:currency_digits` is set to `:cash`.
|
950
950
|
|
951
951
|
## Examples
|
952
952
|
|
|
@@ -963,7 963,9 @@ defmodule Money.Backend do
|
963
963
|
Money.new(:JPY, "124")
|
964
964
|
|
965
965
|
"""
|
966
|
- @spec round(Elixir.Money.t(), Keyword.t()) :: Elixir.Money.t()
|
966
|
@spec round(Elixir.Money.t(), Keyword.t()) ::
|
967
|
Elixir.Money.t() | {:error, {module(), binary()}}
|
968
|
|
967
969
|
def round(%Elixir.Money{} = money, options \\ []) do
|
968
970
|
Elixir.Money.round(money, options)
|
969
971
|
end
|
|
@@ -1266,7 1268,7 @@ defmodule Money.Backend do
|
1266
1268
|
|
1267
1269
|
"""
|
1268
1270
|
@spec from_integer(integer, Elixir.Money.currency_code(), Keyword.t()) ::
|
1269
|
- Elixir.Money.t() | {:error, module(), String.t()}
|
1271
|
Elixir.Money.t() | {:error, {module(), String.t()}}
|
1270
1272
|
|
1271
1273
|
def from_integer(amount, currency, options \\ []) when is_integer(amount) do
|
1272
1274
|
Elixir.Money.from_integer(amount, currency, options)
|
|
@@ -1297,7 1299,7 @@ defmodule Money.Backend do
|
1297
1299
|
|
1298
1300
|
"""
|
1299
1301
|
@spec zero(Elixir.Money.currency_code() | Elixir.Money.t(), Keyword.t()) ::
|
1300
|
- Elixir.Money.t()
|
1302
|
Elixir.Money.t() | {:error, {module(), binary()}}
|
1301
1303
|
|
1302
1304
|
def zero(money, options \\ [])
|
changed
lib/money/financial.ex
|
@@ -209,13 209,12 @@ defmodule Money.Financial do
|
209
209
|
Money.new(:USD, "7731.466833737959119743127888")
|
210
210
|
|
211
211
|
"""
|
212
|
- @spec net_present_value(Money.t(), number, number) :: Money.t()
|
213
|
-
|
212
|
@spec net_present_value(Money.t(), float, number) :: Money.t()
|
214
213
|
def net_present_value(%Money{currency: currency} = future_value, interest_rate, periods) do
|
215
214
|
net_present_value(future_value, interest_rate, periods, Money.new(currency, 0))
|
216
215
|
end
|
217
216
|
|
218
|
- @spec net_present_value(Money.t(), number, number, Money.t()) :: Money.t()
|
217
|
@spec net_present_value(Money.t(), float, number, Money.t()) :: Money.t()
|
219
218
|
def net_present_value(%Money{} = future_value, interest_rate, periods, %Money{} = investment) do
|
220
219
|
present_value(future_value, interest_rate, periods)
|
221
220
|
|> Money.sub!(investment)
|
|
@@ -228,7 227,7 @@ defmodule Money.Financial do
|
228
227
|
represented as a tuple of the form `{period, %Money{}}`
|
229
228
|
|
230
229
|
"""
|
231
|
- @spec internal_rate_of_return(list({integer, Money.t()})) :: number()
|
230
|
@spec internal_rate_of_return(list({integer, Money.t()})) :: float()
|
232
231
|
def internal_rate_of_return([{_period, %Money{}} | _other_flows] = flows) do
|
233
232
|
# estimate_m = sum_of_inflows(flows)
|
234
233
|
# |> Kernel./(abs(Math.to_float(amount)))
|
changed
lib/money/sigil.ex
|
@@ -14,7 14,7 @@ defmodule Money.Sigil do
|
14
14
|
Money.new(:USD, "1000.34")
|
15
15
|
|
16
16
|
"""
|
17
|
- @spec sigil_M(binary, list) :: Money.t() | {:error, {Exception.t(), String.t()}}
|
17
|
@spec sigil_M(binary, list(char)) :: Money.t() | {:error, {module(), String.t()}}
|
18
18
|
def sigil_M(amount, [_, _, _] = currency) do
|
19
19
|
Money.new(to_decimal(amount), atomize(currency))
|
20
20
|
end
|
changed
lib/money/subscription.ex
|
@@ -263,7 263,8 @@ defmodule Money.Subscription do
|
263
263
|
|
264
264
|
"""
|
265
265
|
# @doc since: "2.3.0"
|
266
|
- @spec current_plan(Subscription.t() | map, Keyword.t()) :: Plan.t() | nil
|
266
|
@spec current_plan(Subscription.t() | map, Keyword.t()) ::
|
267
|
Plan.t() | {Change.t(), Plan.t()} | nil
|
267
268
|
|
268
269
|
def current_plan(subscription, options \\ [])
|
269
270
|
|
|
@@ -988,6 989,7 @@ defmodule Money.Subscription do
|
988
989
|
end
|
989
990
|
end
|
990
991
|
|
992
|
@dialyzer {:nowarn_function, raise_change_plan_options_error: 1}
|
991
993
|
defp raise_change_plan_options_error(opt) do
|
992
994
|
raise ArgumentError, "change_plan requires the the option #{inspect(opt)}"
|
993
995
|
end
|
changed
mix.exs
|
@@ -1,7 1,7 @@
|
1
1
|
defmodule Money.Mixfile do
|
2
2
|
use Mix.Project
|
3
3
|
|
4
|
- @version "5.17.0"
|
4
|
@version "5.17.1"
|
5
5
|
|
6
6
|
def project do
|
7
7
|
[
|
|
@@ -21,7 21,14 @@ defmodule Money.Mixfile do
|
21
21
|
elixirc_paths: elixirc_paths(Mix.env()),
|
22
22
|
dialyzer: [
|
23
23
|
ignore_warnings: ".dialyzer_ignore_warnings",
|
24
|
- plt_add_apps: ~w(inets jason mix phoenix_html gringotts)a
|
24
|
plt_add_apps: ~w(inets jason mix phoenix_html gringotts)a,
|
25
|
flags: [
|
26
|
:error_handling,
|
27
|
:unknown,
|
28
|
:underspecs,
|
29
|
:extra_return,
|
30
|
:missing_return
|
31
|
]
|
25
32
|
],
|
26
33
|
compilers: Mix.compilers()
|
27
34
|
]
|
|
@@ -88,14 95,14 @@ defmodule Money.Mixfile do
|
88
95
|
{:ex_cldr_numbers, "~> 2.33"},
|
89
96
|
{:nimble_parsec, "~> 0.5 or ~> 1.0"},
|
90
97
|
{:decimal, "~> 1.6 or ~> 2.0"},
|
91
|
- {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", optional: true},
|
98
|
{:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", optional: true},
|
92
99
|
{:phoenix_html, "~> 2.0 or ~> 3.0 or ~> 4.0", optional: true},
|
93
100
|
{:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false},
|
94
101
|
{:jason, "~> 1.0", optional: true},
|
95
|
- {:stream_data, "~> 0.4", only: [:dev, :test]},
|
102
|
{:stream_data, "~> 1.0", only: [:dev, :test]},
|
96
103
|
{:benchee, "~> 1.0", optional: true, only: :dev},
|
97
104
|
{:exprof, "~> 0.2", only: :dev, runtime: false},
|
98
|
- {:ex_doc, "0.30.9", only: [:dev, :release]},
|
105
|
{:ex_doc, "~> 0.31", only: [:dev, :release]},
|
99
106
|
|
100
107
|
{:gringotts, "~> 1.1", optional: true}
|
101
108
|
]
|