changed
CHANGELOG.md
|
@@ -2,6 2,14 @@
|
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.18.0
|
6
|
|
7
|
This is the changelog for Money v5.18.0 released on September 18th, 2024. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/money/tags)
|
8
|
|
9
|
### Enhancements
|
10
|
|
11
|
* Adds `min/2`, `max/2`, `min!/2`, `max!/2`, `clamp/3`, `clamp!/3`, `negate/1`, `negate!/1` and `within?/3`.
|
12
|
|
5
13
|
## Money v5.17.2
|
6
14
|
|
7
15
|
This is the changelog for Money v5.17.2 released on September 18th, 2024. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/money/tags)
|
changed
hex_metadata.config
|
@@ -1,11 1,11 @@
|
1
1
|
{<<"links">>,
|
2
2
|
[{<<"Changelog">>,
|
3
|
- <<"https://github.com/kipcole9/money/blob/v5.17.2/CHANGELOG.md">>},
|
3
|
<<"https://github.com/kipcole9/money/blob/v5.18.0/CHANGELOG.md">>},
|
4
4
|
{<<"GitHub">>,<<"https://github.com/kipcole9/money">>},
|
5
5
|
{<<"Readme">>,
|
6
|
- <<"https://github.com/kipcole9/money/blob/v5.17.2/README.md">>}]}.
|
6
|
<<"https://github.com/kipcole9/money/blob/v5.18.0/README.md">>}]}.
|
7
7
|
{<<"name">>,<<"ex_money">>}.
|
8
|
- {<<"version">>,<<"5.17.2">>}.
|
8
|
{<<"version">>,<<"5.18.0">>}.
|
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">>}.
|
changed
lib/money.ex
|
@@ -604,7 604,7 @@ defmodule Money do
|
604
604
|
{:error,
|
605
605
|
{Money.Invalid,
|
606
606
|
"A currency code, symbol or description must be specified but " <>
|
607
|
- "was not found in #{inspect(string)}"}}
|
607
|
"was not found in #{inspect(string)}"}}
|
608
608
|
end
|
609
609
|
|
610
610
|
# No currency was in the string so we'll derive it from
|
|
@@ -906,7 906,7 @@ defmodule Money do
|
906
906
|
end
|
907
907
|
|
908
908
|
@doc """
|
909
|
- The absolute value of a `Money` amount.
|
909
|
The absolute value of a `t:Money.t/0` amount.
|
910
910
|
Returns a `t:Money.t/0` type with a positive sign for the amount.
|
911
911
|
|
912
912
|
## Arguments
|
|
@@ -936,7 936,7 @@ defmodule Money do
|
936
936
|
## Arguments
|
937
937
|
|
938
938
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
939
|
- by `Money.new/2`
|
939
|
by `Money.new/2`.
|
940
940
|
|
941
941
|
## Returns
|
942
942
|
|
|
@@ -976,12 976,12 @@ defmodule Money do
|
976
976
|
end
|
977
977
|
|
978
978
|
@doc """
|
979
|
- Add two `Money` values and raise on error.
|
979
|
Add two `t:Money.t/0` values or raise on error.
|
980
980
|
|
981
981
|
## Arguments
|
982
982
|
|
983
983
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
984
|
- by `Money.new/2`
|
984
|
by `Money.new/2`.
|
985
985
|
|
986
986
|
## Returns
|
987
987
|
|
|
@@ -1008,18 1008,18 @@ defmodule Money do
|
1008
1008
|
end
|
1009
1009
|
|
1010
1010
|
@doc """
|
1011
|
- Subtract one `Money` value struct from another.
|
1011
|
Subtract one `t:Money.t/0` value struct from another.
|
1012
1012
|
|
1013
1013
|
## Options
|
1014
1014
|
|
1015
1015
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
1016
|
- by `Money.new/2`
|
1016
|
by `Money.new/2`.
|
1017
1017
|
|
1018
1018
|
## Returns
|
1019
1019
|
|
1020
1020
|
* `{:ok, money}` or
|
1021
1021
|
|
1022
|
- * `{:error, reason}`
|
1022
|
* `{:error, reason}`.
|
1023
1023
|
|
1024
1024
|
## Example
|
1025
1025
|
|
|
@@ -1052,13 1052,13 @@ defmodule Money do
|
1052
1052
|
## Arguments
|
1053
1053
|
|
1054
1054
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
1055
|
- by `Money.new/2`
|
1055
|
by `Money.new/2`.
|
1056
1056
|
|
1057
1057
|
## Returns
|
1058
1058
|
|
1059
1059
|
* a `t:Money.t/0` struct or
|
1060
1060
|
|
1061
|
- * raises an exception
|
1061
|
* raises an exception.
|
1062
1062
|
|
1063
1063
|
## Examples
|
1064
1064
|
|
|
@@ -1084,11 1084,11 @@ defmodule Money do
|
1084
1084
|
## Arguments
|
1085
1085
|
|
1086
1086
|
* `money` is any valid `t:Money.t/0` type returned
|
1087
|
- by `Money.new/2`
|
1087
|
by `Money.new/2`.
|
1088
1088
|
|
1089
|
- * `number` is an integer, float or `Decimal.t`
|
1089
|
* `number` is an integer, float or `t:Decimal.t/0`.
|
1090
1090
|
|
1091
|
- > Note that multipling one %Money{} by another is not supported.
|
1091
|
> Note that multipling one `t:Money.t/0` by another is not supported.
|
1092
1092
|
|
1093
1093
|
## Returns
|
1094
1094
|
|
|
@@ -1129,10 1129,10 @@ defmodule Money do
|
1129
1129
|
|
1130
1130
|
## Arguments
|
1131
1131
|
|
1132
|
- * `money` is any valid `t:Money.t/0` types returned
|
1133
|
- by `Money.new/2`
|
1132
|
* `money` is any valid `t:Money.t/0` type returned
|
1133
|
by `Money.new/2`.
|
1134
1134
|
|
1135
|
- * `number` is an integer, float or `Decimal.t`
|
1135
|
* `number` is an integer, float or `t:Decimal.t/0`.
|
1136
1136
|
|
1137
1137
|
## Returns
|
1138
1138
|
|
|
@@ -1238,6 1238,409 @@ defmodule Money do
|
1238
1238
|
end
|
1239
1239
|
end
|
1240
1240
|
|
1241
|
@doc """
|
1242
|
Return the minimum of two `t:Money.t/0` amounts.
|
1243
|
|
1244
|
## Arguments
|
1245
|
|
1246
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
1247
|
by `Money.new/2`. `money_1` and `money_2` should be of the same
|
1248
|
currency.
|
1249
|
|
1250
|
## Returns
|
1251
|
|
1252
|
* `{:ok, minimum_money}` or
|
1253
|
|
1254
|
* `{:error, reason}`
|
1255
|
|
1256
|
## Example
|
1257
|
|
1258
|
iex> Money.min(Money.new(:USD, 200), Money.new(:USD, 300))
|
1259
|
{:ok, Money.new(:USD, 200)}
|
1260
|
|
1261
|
iex> Money.min(Money.new(:USD, 200), Money.new(:AUD, 200))
|
1262
|
{:error,
|
1263
|
{ArgumentError, "Cannot compare monies with different currencies. Received :USD and :AUD."}}
|
1264
|
|
1265
|
"""
|
1266
|
@doc since: "5.18.0"
|
1267
|
|
1268
|
@spec min(money_1 :: Money.t(), money_2 :: Money.t()) ::
|
1269
|
{:ok, Money.t()} | {:error, {module(), String.t()}}
|
1270
|
|
1271
|
def min(%Money{currency: same_currency} = money_1, %Money{currency: same_currency} = money_2) do
|
1272
|
case compare(money_1, money_2) do
|
1273
|
:gt -> {:ok, money_2}
|
1274
|
:eq -> {:ok, money_2}
|
1275
|
:lt -> {:ok, money_1}
|
1276
|
{:error, reason} -> {:error, reason}
|
1277
|
end
|
1278
|
end
|
1279
|
|
1280
|
def min(%Money{currency: code_a}, %Money{currency: code_b}) do
|
1281
|
{:error, compare_error(code_a, code_b)}
|
1282
|
end
|
1283
|
|
1284
|
@doc """
|
1285
|
Return the maximum of two `t:Money.t/0` amounts.
|
1286
|
|
1287
|
## Arguments
|
1288
|
|
1289
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
1290
|
by `Money.new/2`. `money_1` and `money_2` should be of the same
|
1291
|
currency.
|
1292
|
|
1293
|
## Returns
|
1294
|
|
1295
|
* `{:ok, maximum_money}` or
|
1296
|
|
1297
|
* `{:error, reason}`.
|
1298
|
|
1299
|
## Example
|
1300
|
|
1301
|
iex> Money.max(Money.new(:USD, 200), Money.new(:USD, 300))
|
1302
|
{:ok, Money.new(:USD, 300)}
|
1303
|
|
1304
|
iex> Money.max(Money.new(:USD, 200), Money.new(:AUD, 200))
|
1305
|
{:error,
|
1306
|
{ArgumentError, "Cannot compare monies with different currencies. Received :USD and :AUD."}}
|
1307
|
|
1308
|
"""
|
1309
|
@doc since: "5.18.0"
|
1310
|
|
1311
|
@spec max(money_1 :: Money.t(), money_2 :: Money.t()) ::
|
1312
|
{:ok, Money.t()} | {:error, {module(), String.t()}}
|
1313
|
|
1314
|
def max(%Money{currency: same_currency} = money_1, %Money{currency: same_currency} = money_2) do
|
1315
|
case compare(money_1, money_2) do
|
1316
|
:lt -> {:ok, money_2}
|
1317
|
:eq -> {:ok, money_2}
|
1318
|
:gt -> {:ok, money_1}
|
1319
|
{:error, reason} -> {:error, reason}
|
1320
|
end
|
1321
|
end
|
1322
|
|
1323
|
def max(%Money{currency: code_a}, %Money{currency: code_b}) do
|
1324
|
{:error, compare_error(code_a, code_b)}
|
1325
|
end
|
1326
|
|
1327
|
@doc """
|
1328
|
Return the minimum of two `t:Money.t/0` amounts or
|
1329
|
raises an exception.
|
1330
|
|
1331
|
## Arguments
|
1332
|
|
1333
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
1334
|
by `Money.new/2`. `money_1` and `money_2` should be of the same
|
1335
|
currency.
|
1336
|
|
1337
|
## Returns
|
1338
|
|
1339
|
* `minimum_money` or
|
1340
|
|
1341
|
* raises an exception.
|
1342
|
|
1343
|
## Example
|
1344
|
|
1345
|
iex> Money.min!(Money.new(:USD, 200), Money.new(:USD, 300))
|
1346
|
Money.new(:USD, 200)
|
1347
|
|
1348
|
iex> Money.min!(Money.new(:USD, 200), Money.new(:AUD, 200))
|
1349
|
** (ArgumentError) Cannot compare monies with different currencies. Received :USD and :AUD.
|
1350
|
|
1351
|
"""
|
1352
|
@doc since: "5.18.0"
|
1353
|
|
1354
|
@spec min!(money_1 :: Money.t(), money_2 :: Money.t()) ::
|
1355
|
Money.t() | no_return()
|
1356
|
|
1357
|
def min!(%Money{currency: same_currency} = money_1, %Money{currency: same_currency} = money_2) do
|
1358
|
case compare(money_1, money_2) do
|
1359
|
:gt -> money_2
|
1360
|
:eq -> money_2
|
1361
|
:lt -> money_1
|
1362
|
{:error, {exception, reason}} -> raise exception, reason
|
1363
|
end
|
1364
|
end
|
1365
|
|
1366
|
def min!(%Money{currency: code_a}, %Money{currency: code_b}) do
|
1367
|
{exception, reason} = compare_error(code_a, code_b)
|
1368
|
raise exception, reason
|
1369
|
end
|
1370
|
|
1371
|
@doc """
|
1372
|
Return the maximum of two `t:Money.t/0` amounts or
|
1373
|
raises an exception.
|
1374
|
|
1375
|
## Arguments
|
1376
|
|
1377
|
* `money_1` and `money_2` are any valid `t:Money.t/0` types returned
|
1378
|
by `Money.new/2`. `money_1` and `money_2` should be of the same
|
1379
|
currency.
|
1380
|
|
1381
|
## Returns
|
1382
|
|
1383
|
* `maximum_money` or
|
1384
|
|
1385
|
* raises an exception.
|
1386
|
|
1387
|
## Example
|
1388
|
|
1389
|
iex> Money.max!(Money.new(:USD, 200), Money.new(:USD, 300))
|
1390
|
Money.new(:USD, 300)
|
1391
|
|
1392
|
iex> Money.max!(Money.new(:USD, 200), Money.new(:AUD, 200))
|
1393
|
** (ArgumentError) Cannot compare monies with different currencies. Received :USD and :AUD.
|
1394
|
|
1395
|
"""
|
1396
|
@doc since: "5.18.0"
|
1397
|
|
1398
|
@spec max!(money_1 :: Money.t(), money_2 :: Money.t()) ::
|
1399
|
Money.t() | no_return()
|
1400
|
|
1401
|
def max!(%Money{currency: same_currency} = money_1, %Money{currency: same_currency} = money_2) do
|
1402
|
case compare(money_1, money_2) do
|
1403
|
:lt -> money_2
|
1404
|
:eq -> money_2
|
1405
|
:gt -> money_1
|
1406
|
{:error, {exception, reason}} -> raise exception, reason
|
1407
|
end
|
1408
|
end
|
1409
|
|
1410
|
def max!(%Money{currency: code_a}, %Money{currency: code_b}) do
|
1411
|
{exception, reason} = compare_error(code_a, code_b)
|
1412
|
raise exception, reason
|
1413
|
end
|
1414
|
|
1415
|
@doc """
|
1416
|
Clamps a `t:Money.t/0` to be in the range of `minimum`
|
1417
|
to `maximum`.
|
1418
|
|
1419
|
### Arguments
|
1420
|
|
1421
|
* `money`, `minimum` and `maximum` are any valid `t:Money.t/0` types returned
|
1422
|
by `Money.new/2`. They should be of the same currency.
|
1423
|
|
1424
|
### Returns
|
1425
|
|
1426
|
* `{:ok, money]` where `money` is clamped to the `minimum` or `maximum` if required.
|
1427
|
* If `money` is within the range `minimum..maximum` then `money` is returned unchanged.
|
1428
|
* If `money` is less than `minimum` then `minimum` is returned.
|
1429
|
* If `money` is greater than `maximum` then `maximum` is returned.
|
1430
|
|
1431
|
* or `{:error, reason}`.
|
1432
|
|
1433
|
### Examples
|
1434
|
|
1435
|
iex> Money.clamp(Money.new(:USD, 100), Money.new(:USD, 50), Money.new(:USD, 200))
|
1436
|
{:ok, Money.new(:USD, 100)}
|
1437
|
|
1438
|
iex> Money.clamp(Money.new(:USD, 300), Money.new(:USD, 50), Money.new(:USD, 200))
|
1439
|
{:ok, Money.new(:USD, 200)}
|
1440
|
|
1441
|
iex> Money.clamp(Money.new(:USD, 10), Money.new(:USD, 50), Money.new(:USD, 200))
|
1442
|
{:ok, Money.new(:USD, 50)}
|
1443
|
|
1444
|
iex> Money.clamp(Money.new(:USD, 10), Money.new(:USD, 300), Money.new(:USD, 200))
|
1445
|
{:error,
|
1446
|
{ArgumentError,
|
1447
|
"Minimum must be less than maximum. Found Money.new(:USD, \\"300\\") and Money.new(:USD, \\"200\\")"}}
|
1448
|
|
1449
|
iex> Money.clamp(Money.new(:USD, 10), Money.new(:AUD, 300), Money.new(:EUR, 200))
|
1450
|
{:error, {ArgumentError, "Cannot compare monies with different currencies. Received :USD, :AUD and :EUR"}}
|
1451
|
|
1452
|
"""
|
1453
|
@doc since: "5.18.0"
|
1454
|
|
1455
|
@spec clamp(money :: Money.t(), minimum :: Money.t(), maximum :: Money.t()) ::
|
1456
|
{:ok, Money.t()} | {:error, {module(), String.t()}}
|
1457
|
|
1458
|
def clamp(
|
1459
|
%__MODULE__{currency: same_currency} = money,
|
1460
|
%__MODULE__{currency: same_currency} = minimum,
|
1461
|
%__MODULE__{currency: same_currency} = maximum
|
1462
|
) do
|
1463
|
if compare(minimum, maximum) == :lt do
|
1464
|
Money.max(minimum, Money.min!(maximum, money))
|
1465
|
else
|
1466
|
{:error,
|
1467
|
{ArgumentError,
|
1468
|
"Minimum must be less than maximum. Found #{inspect(minimum)} and #{inspect(maximum)}"}}
|
1469
|
end
|
1470
|
end
|
1471
|
|
1472
|
def clamp(%Money{currency: code_a}, %Money{currency: code_b}, %Money{currency: code_c}) do
|
1473
|
{:error, compare_error(code_a, code_b, code_c)}
|
1474
|
end
|
1475
|
|
1476
|
@doc """
|
1477
|
Clamps a `t:Money.t/0` to be in the range of `minimum`
|
1478
|
to `maximum` or raises an exception.
|
1479
|
|
1480
|
### Arguments
|
1481
|
|
1482
|
* `money`, `minimum` and `maximum` are any valid `t:Money.t/0` types returned
|
1483
|
by `Money.new/2`. They should be of the same currency.
|
1484
|
|
1485
|
### Returns
|
1486
|
|
1487
|
* `money` where `money` is clamped to the `minimum` or `maximum` if required.
|
1488
|
* If `money` is within the range `minimum..maximum` then `money` is returned unchanged.
|
1489
|
* If `money` is less than `minimum` then `minimum` is returned.
|
1490
|
* If `money` is greater than `maximum` then `maximum` is returned.
|
1491
|
|
1492
|
* or `{:error, reason}`.
|
1493
|
|
1494
|
### Examples
|
1495
|
|
1496
|
iex> Money.clamp!(Money.new(:USD, 100), Money.new(:USD, 50), Money.new(:USD, 200))
|
1497
|
Money.new(:USD, 100)
|
1498
|
|
1499
|
iex> Money.clamp!(Money.new(:USD, 300), Money.new(:USD, 50), Money.new(:USD, 200))
|
1500
|
Money.new(:USD, 200)
|
1501
|
|
1502
|
iex> Money.clamp!(Money.new(:USD, 10), Money.new(:USD, 50), Money.new(:USD, 200))
|
1503
|
Money.new(:USD, 50)
|
1504
|
|
1505
|
iex> Money.clamp!(Money.new(:USD, 10), Money.new(:USD, 300), Money.new(:USD, 200))
|
1506
|
** (ArgumentError) Minimum must be less than maximum. Found Money.new(:USD, "300") and Money.new(:USD, "200")
|
1507
|
|
1508
|
iex> Money.clamp!(Money.new(:USD, 10), Money.new(:AUD, 300), Money.new(:EUR, 200))
|
1509
|
** (ArgumentError) Cannot compare monies with different currencies. Received :USD, :AUD and :EUR
|
1510
|
|
1511
|
"""
|
1512
|
@doc since: "5.18.0"
|
1513
|
|
1514
|
@spec clamp!(money :: Money.t(), minimum :: Money.t(), maximum :: Money.t()) ::
|
1515
|
Money.t() | no_return()
|
1516
|
|
1517
|
def clamp!(
|
1518
|
%__MODULE__{currency: same_currency} = money,
|
1519
|
%__MODULE__{currency: same_currency} = minimum,
|
1520
|
%__MODULE__{currency: same_currency} = maximum
|
1521
|
) do
|
1522
|
if compare(minimum, maximum) == :lt do
|
1523
|
Money.max!(minimum, Money.min!(maximum, money))
|
1524
|
else
|
1525
|
raise ArgumentError,
|
1526
|
"Minimum must be less than maximum. Found #{inspect(minimum)} and #{inspect(maximum)}"
|
1527
|
end
|
1528
|
end
|
1529
|
|
1530
|
def clamp!(%Money{currency: code_a}, %Money{currency: code_b}, %Money{currency: code_c}) do
|
1531
|
{exception, reason} = compare_error(code_a, code_b, code_c)
|
1532
|
raise exception, reason
|
1533
|
end
|
1534
|
|
1535
|
@doc """
|
1536
|
Returns a boolean indicating if the `t:Money.t/0` is in the
|
1537
|
range `minimum..maximum`.
|
1538
|
|
1539
|
### Arguments
|
1540
|
|
1541
|
* `money`, `minimum` and `maximum` are any valid `t:Money.t/0` types returned
|
1542
|
by `Money.new/2`. They should be of the same currency.
|
1543
|
|
1544
|
### Returns
|
1545
|
|
1546
|
* `true` or `false`.
|
1547
|
|
1548
|
### Examples
|
1549
|
|
1550
|
iex> Money.within?(Money.new(:USD, 100), Money.new(:USD, 50), Money.new(:USD, 200))
|
1551
|
true
|
1552
|
|
1553
|
iex> Money.within?(Money.new(:USD, 10), Money.new(:USD, 50), Money.new(:USD, 200))
|
1554
|
false
|
1555
|
|
1556
|
iex> Money.within?(Money.new(:USD, 100), Money.new(:USD, 300), Money.new(:USD, 200))
|
1557
|
** (ArgumentError) Minimum must be less than maximum. Found Money.new(:USD, "300") and Money.new(:USD, "200")
|
1558
|
|
1559
|
iex> Money.within?(Money.new(:USD, 10), Money.new(:AUD, 300), Money.new(:EUR, 200))
|
1560
|
** (ArgumentError) Cannot compare monies with different currencies. Received :USD, :AUD and :EUR
|
1561
|
|
1562
|
"""
|
1563
|
@doc since: "5.18.0"
|
1564
|
|
1565
|
@spec within?(money :: Money.t(), minimum :: Money.t(), maximum :: Money.t()) :: boolean()
|
1566
|
|
1567
|
def within?(
|
1568
|
%__MODULE__{currency: same_currency} = money,
|
1569
|
%__MODULE__{currency: same_currency} = minimum,
|
1570
|
%__MODULE__{currency: same_currency} = maximum
|
1571
|
) do
|
1572
|
if compare(minimum, maximum) == :lt do
|
1573
|
compare(money, minimum) in [:gt, :eq] && compare(money, maximum) in [:lt, :eq]
|
1574
|
else
|
1575
|
raise ArgumentError,
|
1576
|
"Minimum must be less than maximum. Found #{inspect(minimum)} and #{inspect(maximum)}"
|
1577
|
end
|
1578
|
end
|
1579
|
|
1580
|
def within?(%Money{currency: code_a}, %Money{currency: code_b}, %Money{currency: code_c}) do
|
1581
|
{exception, reason} = compare_error(code_a, code_b, code_c)
|
1582
|
raise exception, reason
|
1583
|
end
|
1584
|
|
1585
|
@doc """
|
1586
|
Negate a `t:Money.t/0` value.
|
1587
|
|
1588
|
### Argument
|
1589
|
|
1590
|
* `money_1` is any valid `t:Money.t/0` type.
|
1591
|
|
1592
|
### Returns
|
1593
|
|
1863
|
* `{:ok, negated_money}` with the amount negated.
|
1595
|
|
1596
|
### Example
|
1597
|
|
1598
|
iex> Money.negate(Money.new(:USD, 200))
|
1599
|
{:ok, Money.new(:USD, -200)}
|
1600
|
|
1601
|
iex> Money.negate(Money.new(:USD, -200))
|
1602
|
{:ok, Money.new(:USD, 200)}
|
1603
|
|
1604
|
"""
|
1605
|
@doc since: "5.18.0"
|
1606
|
|
1607
|
@spec negate(money :: Money.t()) :: {:ok, Money.t()}
|
1608
|
|
1609
|
def negate(%__MODULE__{amount: amount} = money) do
|
1610
|
{:ok, Map.put(money, :amount, Decimal.negate(amount))}
|
1611
|
end
|
1612
|
|
1613
|
@doc """
|
1614
|
Negate a `t:Money.t/0` value or raises an
|
1615
|
exception.
|
1616
|
|
1617
|
### Argument
|
1618
|
|
1619
|
* `money_1` is any valid `t:Money.t/0` type.
|
1620
|
|
1621
|
### Returns
|
1622
|
|
1623
|
* `negated_money` with the amount negated or
|
1624
|
|
1625
|
* raises an exception.
|
1626
|
|
1627
|
### Example
|
1628
|
|
1629
|
iex> Money.negate!(Money.new(:USD, 200))
|
1630
|
Money.new(:USD, -200)
|
1631
|
|
1632
|
iex> Money.negate!(Money.new(:USD, -200))
|
1633
|
Money.new(:USD, 200)
|
1634
|
|
1635
|
"""
|
1636
|
@doc since: "5.18.0"
|
1637
|
|
1638
|
@spec negate!(money :: Money.t()) :: Money.t() | no_return()
|
1639
|
|
1640
|
def negate!(%__MODULE__{amount: amount} = money) do
|
1641
|
Map.put(money, :amount, Decimal.negate(amount))
|
1642
|
end
|
1643
|
|
1241
1644
|
@doc """
|
1242
1645
|
Returns a boolean indicating if two `Money` values are equal
|
1243
1646
|
|
|
@@ -1309,8 1712,11 @@ defmodule Money do
|
1309
1712
|
|
1310
1713
|
"""
|
1311
1714
|
@doc since: "5.3.0"
|
1312
|
- @spec sum([t(), ...], ExchangeRates.t() | {:ok, ExchangeRates.t()} | {:error, {module(), String.t()}}) ::
|
1313
|
- {:ok, t} | {:error, {module(), String.t()}}
|
1715
|
@spec sum(
|
1716
|
[t(), ...],
|
1717
|
ExchangeRates.t() | {:ok, ExchangeRates.t()} | {:error, {module(), String.t()}}
|
1718
|
) ::
|
1719
|
{:ok, t} | {:error, {module(), String.t()}}
|
1314
1720
|
|
1315
1721
|
def sum(money_list, rates \\ latest_rates_or_empty_map())
|
1316
1722
|
|
|
@@ -1384,10 1790,19 @@ defmodule Money do
|
1384
1790
|
end
|
1385
1791
|
|
1386
1792
|
def compare(%Money{currency: code_a}, %Money{currency: code_b}) do
|
1387
|
- {:error,
|
1388
|
- {ArgumentError,
|
1389
|
- "Cannot compare monies with different currencies. " <>
|
1390
|
- "Received #{inspect(code_a)} and #{inspect(code_b)}."}}
|
1793
|
{:error, compare_error(code_a, code_b)}
|
1794
|
end
|
1795
|
|
1796
|
defp compare_error(code_a, code_b) do
|
1797
|
{ArgumentError,
|
1798
|
"Cannot compare monies with different currencies. " <>
|
1799
|
"Received #{inspect(code_a)} and #{inspect(code_b)}."}
|
1800
|
end
|
1801
|
|
1802
|
defp compare_error(code_a, code_b, code_c) do
|
1803
|
{ArgumentError,
|
1804
|
"Cannot compare monies with different currencies. " <>
|
1805
|
"Received #{inspect(code_a)}, #{inspect(code_b)} and #{inspect(code_c)}"}
|
1391
1806
|
end
|
1392
1807
|
|
1393
1808
|
@doc """
|
|
@@ -1833,9 2248,9 @@ defmodule Money do
|
1833
2248
|
end
|
1834
2249
|
|
1835
2250
|
def to_currency(%Money{currency: from_currency, amount: amount} = money, to_currency, rates)
|
1836
|
- when is_atom(to_currency) or is_digital_token(to_currency) and is_map(rates) do
|
2251
|
when is_atom(to_currency) or (is_digital_token(to_currency) and is_map(rates)) do
|
1837
2252
|
with {:ok, to_currency_code} <- validate_currency(to_currency),
|
1838
|
- {:ok, cross_rate} <- cross_rate(from_currency, to_currency_code, rates) do
|
2253
|
{:ok, cross_rate} <- cross_rate(from_currency, to_currency_code, rates) do
|
1839
2254
|
converted_amount = Decimal.mult(amount, cross_rate)
|
1840
2255
|
{:ok, %{money | currency: to_currency, amount: converted_amount}}
|
1841
2256
|
end
|
|
@@ -2183,9 2598,10 @@ defmodule Money do
|
2183
2598
|
defp do_digits_from_options(_currency_data, other),
|
2184
2599
|
do: {:error, invalid_digits_error(other)}
|
2185
2600
|
|
2186
|
- defp invalid_digits_error(other), do:
|
2187
|
- {Money.InvalidDigitsError,
|
2188
|
- "Unknown or invalid :fractional_digits option found: #{inspect(other)}"}
|
2601
|
defp invalid_digits_error(other),
|
2602
|
do:
|
2603
|
{Money.InvalidDigitsError,
|
2604
|
"Unknown or invalid :fractional_digits option found: #{inspect(other)}"}
|
2189
2605
|
|
2190
2606
|
@doc """
|
2191
2607
|
Return a zero amount `t:Money.t/0` in the given currency.
|
|
@@ -2285,7 2701,9 @@ defmodule Money do
|
2285
2701
|
else
|
2286
2702
|
def integer?(%{amount: �cimal{coef: :NaN}}), do: false
|
2287
2703
|
def integer?(%{amount: �cimal{coef: :inf}}), do: false
|
2288
|
- def integer?(%{amount: �cimal{coef: coef, exp: exp}}), do: exp >= 0 or zero_after_dot?(coef, exp)
|
2704
|
|
2705
|
def integer?(%{amount: �cimal{coef: coef, exp: exp}}),
|
2706
|
do: exp >= 0 or zero_after_dot?(coef, exp)
|
2289
2707
|
|
2290
2708
|
defp zero_after_dot?(coef, exp) when coef >= 10 and exp < 0,
|
2291
2709
|
do: Kernel.rem(coef, 10) == 0 and zero_after_dot?(Kernel.div(coef, 10), exp 1)
|
|
@@ -2456,7 2874,7 @@ defmodule Money do
|
2456
2874
|
end
|
2457
2875
|
|
2458
2876
|
defp get_rate(currency, rates) do
|
2459
|
- keys = is_atom(currency) && [currency, Atom.to_string(currency)] || [currency]
|
2877
|
keys = (is_atom(currency) && [currency, Atom.to_string(currency)]) || [currency]
|
2460
2878
|
|
2461
2879
|
rates
|
2462
2880
|
|> Map.take(keys)
|
changed
lib/money/backend.ex
|
@@ -964,7 964,7 @@ defmodule Money.Backend do
|
964
964
|
|
965
965
|
"""
|
966
966
|
@spec round(Elixir.Money.t(), Keyword.t()) ::
|
967
|
- Elixir.Money.t() | {:error, {module(), binary()}}
|
967
|
Elixir.Money.t() | {:error, {module(), binary()}}
|
968
968
|
|
969
969
|
def round(%Elixir.Money{} = money, options \\ []) do
|
970
970
|
Elixir.Money.round(money, options)
|
changed
lib/money/exchange_rates/exchange_rates_retriever.ex
|
@@ -213,7 213,7 @@ defmodule Money.ExchangeRates.Retriever do
|
213
213
|
end
|
214
214
|
|
215
215
|
defp process_response({:error, reason}, _url, _config) do
|
216
|
- {:error, {Money.ExchangeRateError, "#{inspect reason}"}}
|
216
|
{:error, {Money.ExchangeRateError, "#{inspect(reason)}"}}
|
217
217
|
end
|
218
218
|
|
219
219
|
defp if_none_match_header(url) do
|
|
@@ -473,7 473,9 @@ defmodule Money.ExchangeRates.Retriever do
|
473
473
|
defp seconds(milliseconds) do
|
474
474
|
seconds = div(milliseconds, 1000)
|
475
475
|
plural = if seconds == 1, do: "second", else: "seconds"
|
476
|
- {:ok, formatted_seconds} = Cldr.Number.to_string(seconds, backend: Money.get_env(:default_cldr_backend))
|
476
|
|
477
|
{:ok, formatted_seconds} =
|
478
|
Cldr.Number.to_string(seconds, backend: Money.get_env(:default_cldr_backend))
|
477
479
|
|
478
480
|
{formatted_seconds, plural}
|
479
481
|
end
|
|
@@ -481,5 483,4 @@ defmodule Money.ExchangeRates.Retriever do
|
481
483
|
defp exchange_rate_service_error do
|
482
484
|
{Money.ExchangeRateError, "Exchange rate service does not appear to be running"}
|
483
485
|
end
|
484
|
-
|
485
486
|
end
|
changed
lib/money/exchange_rates/exchange_rates_supervisor.ex
|
@@ -193,7 193,7 @@ defmodule Money.ExchangeRates.Supervisor do
|
193
193
|
defp start_retriever! do
|
194
194
|
case ExchangeRates.Retriever.start() do
|
195
195
|
{:ok, _pid} -> :ok
|
196
|
- {:error, reason} -> raise "Unhandled error starting retriever; #{inspect reason}"
|
196
|
{:error, reason} -> raise "Unhandled error starting retriever; #{inspect(reason)}"
|
197
197
|
end
|
198
198
|
end
|
199
199
|
end
|
changed
lib/money/protocol/inspect.ex
|
@@ -14,7 14,7 @@ defimpl Inspect, for: Money do
|
14
14
|
end
|
15
15
|
|
16
16
|
def do_inspect(%Money{format_options: []} = money, _opts) do
|
17
|
- "Money.new(#{inspect(money.currency)}, #{inspect Decimal.to_string(money.amount)})"
|
17
|
"Money.new(#{inspect(money.currency)}, #{inspect(Decimal.to_string(money.amount))})"
|
18
18
|
end
|
19
19
|
|
20
20
|
def do_inspect(money, _opts) do
|
|
@@ -24,6 24,6 @@ defimpl Inspect, for: Money do
|
24
24
|
|> String.trim_leading("[")
|
25
25
|
|> String.trim_trailing("]")
|
26
26
|
|
27
|
- "Money.new(#{inspect money.currency}, #{inspect Decimal.to_string(money.amount)}, #{format_options})"
|
27
|
"Money.new(#{inspect(money.currency)}, #{inspect(Decimal.to_string(money.amount))}, #{format_options})"
|
28
28
|
end
|
29
|
- end
|
|
\ No newline at end of file
|
29
|
end
|
changed
lib/money/protocol/phoenix_html_safe.ex
|
@@ -5,4 5,4 @@ if Cldr.Config.ensure_compiled?(Phoenix.HTML.Safe) &&
|
5
5
|
Phoenix.HTML.Safe.to_iodata(Money.to_string!(money))
|
6
6
|
end
|
7
7
|
end
|
8
|
- end
|
|
\ No newline at end of file
|
8
|
end
|
changed
lib/money/subscription.ex
|
@@ -264,7 264,7 @@ defmodule Money.Subscription do
|
264
264
|
"""
|
265
265
|
# @doc since: "2.3.0"
|
266
266
|
@spec current_plan(Subscription.t() | map, Keyword.t()) ::
|
267
|
- Plan.t() | {Change.t(), Plan.t()} | nil
|
267
|
Plan.t() | {Change.t(), Plan.t()} | nil
|
268
268
|
|
269
269
|
def current_plan(subscription, options \\ [])
|
changed
mix.exs
|
@@ -1,7 1,7 @@
|
1
1
|
defmodule Money.Mixfile do
|
2
2
|
use Mix.Project
|
3
3
|
|
4
|
- @version "5.17.2"
|
4
|
@version "5.18.0"
|
5
5
|
|
6
6
|
def project do
|
7
7
|
[
|
|
@@ -103,7 103,6 @@ defmodule Money.Mixfile do
|
103
103
|
{:benchee, "~> 1.0", optional: true, only: :dev},
|
104
104
|
{:exprof, "~> 0.2", only: :dev, runtime: false},
|
105
105
|
{:ex_doc, "~> 0.31", only: [:dev, :release]},
|
106
|
-
|
107
106
|
{:gringotts, "~> 1.1", optional: true}
|
108
107
|
]
|
109
108
|
|> Enum.reject(&is_nil/1)
|