dinero.js 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +7 -0
- package/README.md +316 -0
- package/build/amd/dinero.js +1659 -0
- package/build/amd/dinero.min.js +1 -0
- package/build/amd/dinero.polyfilled.js +3964 -0
- package/build/amd/dinero.polyfilled.min.js +1 -0
- package/build/cjs/dinero.js +1657 -0
- package/build/cjs/dinero.min.js +1 -0
- package/build/cjs/dinero.polyfilled.js +3962 -0
- package/build/cjs/dinero.polyfilled.min.js +1 -0
- package/build/esm/dinero.js +1655 -0
- package/build/esm/dinero.min.js +1 -0
- package/build/esm/dinero.polyfilled.js +3960 -0
- package/build/esm/dinero.polyfilled.min.js +1 -0
- package/build/umd/dinero.js +1663 -0
- package/build/umd/dinero.min.js +1 -0
- package/build/umd/dinero.polyfilled.js +3968 -0
- package/build/umd/dinero.polyfilled.min.js +1 -0
- package/package.json +83 -0
- package/src/dinero-polyfilled.js +13 -0
- package/src/dinero.js +970 -0
- package/src/services/assert.js +59 -0
- package/src/services/calculator.js +122 -0
- package/src/services/currency-converter.js +38 -0
- package/src/services/format.js +76 -0
- package/src/services/helpers.js +193 -0
- package/src/services/settings.js +62 -0
- package/src/services/static.js +103 -0
package/src/dinero.js
ADDED
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
import { Defaults, Globals } from './services/settings'
|
|
2
|
+
import Static from './services/static'
|
|
3
|
+
import Format from './services/format'
|
|
4
|
+
import Calculator from './services/calculator'
|
|
5
|
+
import CurrencyConverter from './services/currency-converter'
|
|
6
|
+
import {
|
|
7
|
+
assert,
|
|
8
|
+
assertPercentage,
|
|
9
|
+
assertValidRatios,
|
|
10
|
+
assertInteger
|
|
11
|
+
} from './services/assert'
|
|
12
|
+
import { isUndefined } from './services/helpers'
|
|
13
|
+
|
|
14
|
+
const calculator = Calculator()
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A Dinero object is an immutable data structure representing a specific monetary value.
|
|
18
|
+
* It comes with methods for creating, parsing, manipulating, testing, transforming and formatting them.
|
|
19
|
+
*
|
|
20
|
+
* A Dinero object has:
|
|
21
|
+
*
|
|
22
|
+
* * An `amount`, expressed in minor currency units, as an integer.
|
|
23
|
+
* * A `currency`, expressed as an {@link https://en.wikipedia.org/wiki/ISO_4217#Active_codes ISO 4217 currency code}.
|
|
24
|
+
* * A `precision`, expressed as an integer, to represent the number of decimal places in the `amount`.
|
|
25
|
+
* This is helpful when you want to represent fractional minor currency units (e.g.: $10.4545).
|
|
26
|
+
* You can also use it to represent a currency with a different [exponent](https://en.wikipedia.org/wiki/ISO_4217#Treatment_of_minor_currency_units_.28the_.22exponent.22.29) than `2` (e.g.: Iraqi dinar with 1000 fils in 1 dinar (exponent of `3`), Japanese yen with no sub-units (exponent of `0`)).
|
|
27
|
+
* * An optional `locale` property that affects how output strings are formatted.
|
|
28
|
+
*
|
|
29
|
+
* Here's an overview of the public API:
|
|
30
|
+
*
|
|
31
|
+
* * **Access:** {@link module:Dinero~getAmount getAmount}, {@link module:Dinero~getCurrency getCurrency}, {@link module:Dinero~getLocale getLocale} and {@link module:Dinero~getPrecision getPrecision}.
|
|
32
|
+
* * **Manipulation:** {@link module:Dinero~add add}, {@link module:Dinero~subtract subtract}, {@link module:Dinero~multiply multiply}, {@link module:Dinero~divide divide}, {@link module:Dinero~percentage percentage}, {@link module:Dinero~allocate allocate} and {@link module:Dinero~convert convert}.
|
|
33
|
+
* * **Testing:** {@link module:Dinero~equalsTo equalsTo}, {@link module:Dinero~lessThan lessThan}, {@link module:Dinero~lessThanOrEqual lessThanOrEqual}, {@link module:Dinero~greaterThan greaterThan}, {@link module:Dinero~greaterThanOrEqual greaterThanOrEqual}, {@link module:Dinero~isZero isZero}, {@link module:Dinero~isPositive isPositive}, {@link module:Dinero~isNegative isNegative}, {@link module:Dinero~hasSubUnits hasSubUnits}, {@link module:Dinero~hasSameCurrency hasSameCurrency} and {@link module:Dinero~hasSameAmount hasSameAmount}.
|
|
34
|
+
* * **Configuration:** {@link module:Dinero~setLocale setLocale}.
|
|
35
|
+
* * **Conversion & formatting:** {@link module:Dinero~toFormat toFormat}, {@link module:Dinero~toUnit toUnit}, {@link module:Dinero~toRoundedUnit toRoundedUnit}, {@link module:Dinero~toObject toObject}, {@link module:Dinero~toJSON toJSON}, {@link module:Dinero~convertPrecision convertPrecision} and {@link module:Dinero.normalizePrecision normalizePrecision}.
|
|
36
|
+
*
|
|
37
|
+
* Dinero.js uses `number`s under the hood, so it's constrained by the [double-precision floating-point format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format). Using values over [`Number.MAX_SAFE_INTEGER`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Number/MAX_SAFE_INTEGER) or below [`Number.MIN_SAFE_INTEGER`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Number/MIN_SAFE_INTEGER) will yield unpredictable results.
|
|
38
|
+
* Same goes with performing calculations: once the internal `amount` value exceeds those limits, precision is no longer guaranteed.
|
|
39
|
+
*
|
|
40
|
+
* @module Dinero
|
|
41
|
+
* @param {Number} [options.amount=0] - The amount in minor currency units (as an integer).
|
|
42
|
+
* @param {String} [options.currency='USD'] - An ISO 4217 currency code.
|
|
43
|
+
* @param {String} [options.precision=2] - The number of decimal places to represent.
|
|
44
|
+
*
|
|
45
|
+
* @throws {TypeError} If `amount` or `precision` is invalid. Integers over [`Number.MAX_SAFE_INTEGER`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Number/MAX_SAFE_INTEGER) or below [`Number.MIN_SAFE_INTEGER`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Number/MIN_SAFE_INTEGER) are considered valid, even though they can lead to imprecise amounts.
|
|
46
|
+
*
|
|
47
|
+
* @return {Object}
|
|
48
|
+
*/
|
|
49
|
+
const Dinero = options => {
|
|
50
|
+
const { amount, currency, precision } = Object.assign(
|
|
51
|
+
{},
|
|
52
|
+
{
|
|
53
|
+
amount: Dinero.defaultAmount,
|
|
54
|
+
currency: Dinero.defaultCurrency,
|
|
55
|
+
precision: Dinero.defaultPrecision
|
|
56
|
+
},
|
|
57
|
+
options
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
assertInteger(amount)
|
|
61
|
+
assertInteger(precision)
|
|
62
|
+
|
|
63
|
+
const {
|
|
64
|
+
globalLocale,
|
|
65
|
+
globalFormat,
|
|
66
|
+
globalRoundingMode,
|
|
67
|
+
globalFormatRoundingMode
|
|
68
|
+
} = Dinero
|
|
69
|
+
|
|
70
|
+
const globalExchangeRatesApi = Object.assign(
|
|
71
|
+
{},
|
|
72
|
+
Dinero.globalExchangeRatesApi
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Uses ES5 function notation so `this` can be passed through call, apply and bind
|
|
77
|
+
* @ignore
|
|
78
|
+
*/
|
|
79
|
+
const create = function(options) {
|
|
80
|
+
const obj = Object.assign(
|
|
81
|
+
{},
|
|
82
|
+
Object.assign({}, { amount, currency, precision }, options),
|
|
83
|
+
Object.assign({}, { locale: this.locale }, options)
|
|
84
|
+
)
|
|
85
|
+
return Object.assign(
|
|
86
|
+
Dinero({
|
|
87
|
+
amount: obj.amount,
|
|
88
|
+
currency: obj.currency,
|
|
89
|
+
precision: obj.precision
|
|
90
|
+
}),
|
|
91
|
+
{
|
|
92
|
+
locale: obj.locale
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Uses ES5 function notation so `this` can be passed through call, apply and bind
|
|
99
|
+
* @ignore
|
|
100
|
+
*/
|
|
101
|
+
const assertSameCurrency = function(comparator) {
|
|
102
|
+
assert(
|
|
103
|
+
this.hasSameCurrency(comparator),
|
|
104
|
+
'You must provide a Dinero instance with the same currency.',
|
|
105
|
+
TypeError
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
/**
|
|
111
|
+
* Returns the amount.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // returns 500
|
|
115
|
+
* Dinero({ amount: 500 }).getAmount()
|
|
116
|
+
*
|
|
117
|
+
* @return {Number}
|
|
118
|
+
*/
|
|
119
|
+
getAmount() {
|
|
120
|
+
return amount
|
|
121
|
+
},
|
|
122
|
+
/**
|
|
123
|
+
* Returns the currency.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // returns 'EUR'
|
|
127
|
+
* Dinero({ currency: 'EUR' }).getCurrency()
|
|
128
|
+
*
|
|
129
|
+
* @return {String}
|
|
130
|
+
*/
|
|
131
|
+
getCurrency() {
|
|
132
|
+
return currency
|
|
133
|
+
},
|
|
134
|
+
/**
|
|
135
|
+
* Returns the locale.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* // returns 'fr-FR'
|
|
139
|
+
* Dinero().setLocale('fr-FR').getLocale()
|
|
140
|
+
*
|
|
141
|
+
* @return {String}
|
|
142
|
+
*/
|
|
143
|
+
getLocale() {
|
|
144
|
+
return this.locale || globalLocale
|
|
145
|
+
},
|
|
146
|
+
/**
|
|
147
|
+
* Returns a new Dinero object with an embedded locale.
|
|
148
|
+
*
|
|
149
|
+
* @param {String} newLocale - The new locale as an {@link http://tools.ietf.org/html/rfc5646 BCP 47 language tag}.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* // Returns a Dinero object with locale 'ja-JP'
|
|
153
|
+
* Dinero().setLocale('ja-JP')
|
|
154
|
+
*
|
|
155
|
+
* @return {Dinero}
|
|
156
|
+
*/
|
|
157
|
+
setLocale(newLocale) {
|
|
158
|
+
return create.call(this, { locale: newLocale })
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* Returns the precision.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // returns 3
|
|
165
|
+
* Dinero({ precision: 3 }).getPrecision()
|
|
166
|
+
*
|
|
167
|
+
* @return {Number}
|
|
168
|
+
*/
|
|
169
|
+
getPrecision() {
|
|
170
|
+
return precision
|
|
171
|
+
},
|
|
172
|
+
/**
|
|
173
|
+
* Returns a new Dinero object with a new precision and a converted amount.
|
|
174
|
+
*
|
|
175
|
+
* By default, fractional minor currency units are rounded using the **half to even** rule ([banker's rounding](http://wiki.c2.com/?BankersRounding)).
|
|
176
|
+
* This can be necessary when you need to convert objects to a smaller precision.
|
|
177
|
+
*
|
|
178
|
+
* Rounding *can* lead to accuracy issues as you chain many times. Consider a minimal amount of subsequent conversions for safer results.
|
|
179
|
+
* You can also specify a different `roundingMode` to better fit your needs.
|
|
180
|
+
*
|
|
181
|
+
* @param {Number} newPrecision - The new precision.
|
|
182
|
+
* @param {String} [roundingMode='HALF_EVEN'] - The rounding mode to use: `'HALF_ODD'`, `'HALF_EVEN'`, `'HALF_UP'`, `'HALF_DOWN'`, `'HALF_TOWARDS_ZERO'`, `'HALF_AWAY_FROM_ZERO'` or `'DOWN'`.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* // Returns a Dinero object with precision 3 and amount 1000
|
|
186
|
+
* Dinero({ amount: 100, precision: 2 }).convertPrecision(3)
|
|
187
|
+
*
|
|
188
|
+
* @throws {TypeError} If `newPrecision` is invalid.
|
|
189
|
+
*
|
|
190
|
+
* @return {Dinero}
|
|
191
|
+
*/
|
|
192
|
+
convertPrecision(newPrecision, roundingMode = globalFormatRoundingMode) {
|
|
193
|
+
assertInteger(newPrecision)
|
|
194
|
+
const precision = this.getPrecision()
|
|
195
|
+
const isNewPrecisionLarger = newPrecision > precision
|
|
196
|
+
const operation = isNewPrecisionLarger
|
|
197
|
+
? calculator.multiply
|
|
198
|
+
: calculator.divide
|
|
199
|
+
const terms = isNewPrecisionLarger
|
|
200
|
+
? [newPrecision, precision]
|
|
201
|
+
: [precision, newPrecision]
|
|
202
|
+
const factor = Math.pow(10, calculator.subtract(...terms))
|
|
203
|
+
return create.call(this, {
|
|
204
|
+
amount: calculator.round(
|
|
205
|
+
operation(this.getAmount(), factor),
|
|
206
|
+
roundingMode
|
|
207
|
+
),
|
|
208
|
+
precision: newPrecision
|
|
209
|
+
})
|
|
210
|
+
},
|
|
211
|
+
/**
|
|
212
|
+
* Returns a new Dinero object that represents the sum of this and an other Dinero object.
|
|
213
|
+
*
|
|
214
|
+
* If Dinero objects have a different `precision`, they will be first converted to the highest.
|
|
215
|
+
*
|
|
216
|
+
* @param {Dinero} addend - The Dinero object to add.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* // returns a Dinero object with amount 600
|
|
220
|
+
* Dinero({ amount: 400 }).add(Dinero({ amount: 200 }))
|
|
221
|
+
* @example
|
|
222
|
+
* // returns a Dinero object with amount 144545 and precision 4
|
|
223
|
+
* Dinero({ amount: 400 }).add(Dinero({ amount: 104545, precision: 4 }))
|
|
224
|
+
*
|
|
225
|
+
* @throws {TypeError} If `addend` has a different currency.
|
|
226
|
+
*
|
|
227
|
+
* @return {Dinero}
|
|
228
|
+
*/
|
|
229
|
+
add(addend) {
|
|
230
|
+
assertSameCurrency.call(this, addend)
|
|
231
|
+
const addends = Dinero.normalizePrecision([this, addend])
|
|
232
|
+
return create.call(this, {
|
|
233
|
+
amount: calculator.add(addends[0].getAmount(), addends[1].getAmount()),
|
|
234
|
+
precision: addends[0].getPrecision()
|
|
235
|
+
})
|
|
236
|
+
},
|
|
237
|
+
/**
|
|
238
|
+
* Returns a new Dinero object that represents the difference of this and an other Dinero object.
|
|
239
|
+
*
|
|
240
|
+
* If Dinero objects have a different `precision`, they will be first converted to the highest.
|
|
241
|
+
*
|
|
242
|
+
* @param {Dinero} subtrahend - The Dinero object to subtract.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* // returns a Dinero object with amount 200
|
|
246
|
+
* Dinero({ amount: 400 }).subtract(Dinero({ amount: 200 }))
|
|
247
|
+
* @example
|
|
248
|
+
* // returns a Dinero object with amount 64545 and precision 4
|
|
249
|
+
* Dinero({ amount: 104545, precision: 4 }).subtract(Dinero({ amount: 400 }))
|
|
250
|
+
*
|
|
251
|
+
* @throws {TypeError} If `subtrahend` has a different currency.
|
|
252
|
+
*
|
|
253
|
+
* @return {Dinero}
|
|
254
|
+
*/
|
|
255
|
+
subtract(subtrahend) {
|
|
256
|
+
assertSameCurrency.call(this, subtrahend)
|
|
257
|
+
const subtrahends = Dinero.normalizePrecision([this, subtrahend])
|
|
258
|
+
return create.call(this, {
|
|
259
|
+
amount: calculator.subtract(
|
|
260
|
+
subtrahends[0].getAmount(),
|
|
261
|
+
subtrahends[1].getAmount()
|
|
262
|
+
),
|
|
263
|
+
precision: subtrahends[0].getPrecision()
|
|
264
|
+
})
|
|
265
|
+
},
|
|
266
|
+
/**
|
|
267
|
+
* Returns a new Dinero object that represents the multiplied value by the given factor.
|
|
268
|
+
*
|
|
269
|
+
* By default, fractional minor currency units are rounded using the **half to even** rule ([banker's rounding](http://wiki.c2.com/?BankersRounding)).
|
|
270
|
+
*
|
|
271
|
+
* Rounding *can* lead to accuracy issues as you chain many times. Consider a minimal amount of subsequent calculations for safer results.
|
|
272
|
+
* You can also specify a different `roundingMode` to better fit your needs.
|
|
273
|
+
*
|
|
274
|
+
* @param {Number} multiplier - The factor to multiply by.
|
|
275
|
+
* @param {String} [roundingMode='HALF_EVEN'] - The rounding mode to use: `'HALF_ODD'`, `'HALF_EVEN'`, `'HALF_UP'`, `'HALF_DOWN'`, `'HALF_TOWARDS_ZERO'`, `'HALF_AWAY_FROM_ZERO'` or `'DOWN'`.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* // returns a Dinero object with amount 1600
|
|
279
|
+
* Dinero({ amount: 400 }).multiply(4)
|
|
280
|
+
* @example
|
|
281
|
+
* // returns a Dinero object with amount 800
|
|
282
|
+
* Dinero({ amount: 400 }).multiply(2.001)
|
|
283
|
+
* @example
|
|
284
|
+
* // returns a Dinero object with amount 801
|
|
285
|
+
* Dinero({ amount: 400 }).multiply(2.00125, 'HALF_UP')
|
|
286
|
+
*
|
|
287
|
+
* @return {Dinero}
|
|
288
|
+
*/
|
|
289
|
+
multiply(multiplier, roundingMode = globalRoundingMode) {
|
|
290
|
+
return create.call(this, {
|
|
291
|
+
amount: calculator.round(
|
|
292
|
+
calculator.multiply(this.getAmount(), multiplier),
|
|
293
|
+
roundingMode
|
|
294
|
+
)
|
|
295
|
+
})
|
|
296
|
+
},
|
|
297
|
+
/**
|
|
298
|
+
* Returns a new Dinero object that represents the divided value by the given factor.
|
|
299
|
+
*
|
|
300
|
+
* By default, fractional minor currency units are rounded using the **half to even** rule ([banker's rounding](http://wiki.c2.com/?BankersRounding)).
|
|
301
|
+
*
|
|
302
|
+
* Rounding *can* lead to accuracy issues as you chain many times. Consider a minimal amount of subsequent calculations for safer results.
|
|
303
|
+
* You can also specify a different `roundingMode` to better fit your needs.
|
|
304
|
+
*
|
|
305
|
+
* As rounding is applied, precision may be lost in the process. If you want to accurately split a Dinero object, use {@link module:Dinero~allocate allocate} instead.
|
|
306
|
+
*
|
|
307
|
+
* @param {Number} divisor - The factor to divide by.
|
|
308
|
+
* @param {String} [roundingMode='HALF_EVEN'] - The rounding mode to use: `'HALF_ODD'`, `'HALF_EVEN'`, `'HALF_UP'`, `'HALF_DOWN'`, `'HALF_TOWARDS_ZERO'`, `'HALF_AWAY_FROM_ZERO'` or `'DOWN'`.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* // returns a Dinero object with amount 100
|
|
312
|
+
* Dinero({ amount: 400 }).divide(4)
|
|
313
|
+
* @example
|
|
314
|
+
* // returns a Dinero object with amount 52
|
|
315
|
+
* Dinero({ amount: 105 }).divide(2)
|
|
316
|
+
* @example
|
|
317
|
+
* // returns a Dinero object with amount 53
|
|
318
|
+
* Dinero({ amount: 105 }).divide(2, 'HALF_UP')
|
|
319
|
+
*
|
|
320
|
+
* @return {Dinero}
|
|
321
|
+
*/
|
|
322
|
+
divide(divisor, roundingMode = globalRoundingMode) {
|
|
323
|
+
return create.call(this, {
|
|
324
|
+
amount: calculator.round(
|
|
325
|
+
calculator.divide(this.getAmount(), divisor),
|
|
326
|
+
roundingMode
|
|
327
|
+
)
|
|
328
|
+
})
|
|
329
|
+
},
|
|
330
|
+
/**
|
|
331
|
+
* Returns a new Dinero object that represents a percentage of this.
|
|
332
|
+
*
|
|
333
|
+
* As rounding is applied, precision may be lost in the process. If you want to accurately split a Dinero object, use {@link module:Dinero~allocate allocate} instead.
|
|
334
|
+
*
|
|
335
|
+
* @param {Number} percentage - The percentage to extract (between 0 and 100).
|
|
336
|
+
* @param {String} [roundingMode='HALF_EVEN'] - The rounding mode to use: `'HALF_ODD'`, `'HALF_EVEN'`, `'HALF_UP'`, `'HALF_DOWN'`, `'HALF_TOWARDS_ZERO'`, `'HALF_AWAY_FROM_ZERO'` or `'DOWN'`.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* // returns a Dinero object with amount 5000
|
|
340
|
+
* Dinero({ amount: 10000 }).percentage(50)
|
|
341
|
+
* @example
|
|
342
|
+
* // returns a Dinero object with amount 29
|
|
343
|
+
* Dinero({ amount: 57 }).percentage(50, "HALF_ODD")
|
|
344
|
+
*
|
|
345
|
+
* @throws {RangeError} If `percentage` is out of range.
|
|
346
|
+
*
|
|
347
|
+
* @return {Dinero}
|
|
348
|
+
*/
|
|
349
|
+
percentage(percentage, roundingMode = globalRoundingMode) {
|
|
350
|
+
assertPercentage(percentage)
|
|
351
|
+
return this.multiply(calculator.divide(percentage, 100), roundingMode)
|
|
352
|
+
},
|
|
353
|
+
/**
|
|
354
|
+
* Allocates the amount of a Dinero object according to a list of ratios.
|
|
355
|
+
*
|
|
356
|
+
* Sometimes you need to split monetary values but percentages can't cut it without adding or losing pennies.
|
|
357
|
+
* A good example is invoicing: let's say you need to bill $1,000.03 and you want a 50% downpayment.
|
|
358
|
+
* If you use {@link module:Dinero~percentage percentage}, you'll get an accurate Dinero object but the amount won't be billable: you can't split a penny.
|
|
359
|
+
* If you round it, you'll bill a penny extra.
|
|
360
|
+
* With {@link module:Dinero~allocate allocate}, you can split a monetary amount then distribute the remainder as evenly as possible.
|
|
361
|
+
*
|
|
362
|
+
* You can use percentage style or ratio style for `ratios`: `[25, 75]` and `[1, 3]` will do the same thing.
|
|
363
|
+
*
|
|
364
|
+
* Since v1.8.0, you can use zero ratios (such as [0, 50, 50]). If there's a remainder to distribute, zero ratios are skipped and return a Dinero object with amount zero.
|
|
365
|
+
*
|
|
366
|
+
* @param {Number[]} ratios - The ratios to allocate the money to.
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* // returns an array of two Dinero objects
|
|
370
|
+
* // the first one with an amount of 502
|
|
371
|
+
* // the second one with an amount of 501
|
|
372
|
+
* Dinero({ amount: 1003 }).allocate([50, 50])
|
|
373
|
+
* @example
|
|
374
|
+
* // returns an array of two Dinero objects
|
|
375
|
+
* // the first one with an amount of 25
|
|
376
|
+
* // the second one with an amount of 75
|
|
377
|
+
* Dinero({ amount: 100 }).allocate([1, 3])
|
|
378
|
+
* @example
|
|
379
|
+
* // since version 1.8.0
|
|
380
|
+
* // returns an array of three Dinero objects
|
|
381
|
+
* // the first one with an amount of 0
|
|
382
|
+
* // the second one with an amount of 502
|
|
383
|
+
* // the third one with an amount of 501
|
|
384
|
+
* Dinero({ amount: 1003 }).allocate([0, 50, 50])
|
|
385
|
+
*
|
|
386
|
+
* @throws {TypeError} If ratios are invalid.
|
|
387
|
+
*
|
|
388
|
+
* @return {Dinero[]}
|
|
389
|
+
*/
|
|
390
|
+
allocate(ratios) {
|
|
391
|
+
assertValidRatios(ratios)
|
|
392
|
+
|
|
393
|
+
const total = ratios.reduce((a, b) => calculator.add(a, b))
|
|
394
|
+
let remainder = this.getAmount()
|
|
395
|
+
|
|
396
|
+
const shares = ratios.map(ratio => {
|
|
397
|
+
const share = Math.floor(
|
|
398
|
+
calculator.divide(calculator.multiply(this.getAmount(), ratio), total)
|
|
399
|
+
)
|
|
400
|
+
remainder = calculator.subtract(remainder, share)
|
|
401
|
+
return create.call(this, { amount: share })
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
let i = 0
|
|
405
|
+
while (remainder > 0) {
|
|
406
|
+
if (ratios[i] > 0) {
|
|
407
|
+
shares[i] = shares[i].add(create.call(this, { amount: 1 }))
|
|
408
|
+
remainder = calculator.subtract(remainder, 1)
|
|
409
|
+
}
|
|
410
|
+
i += 1
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return shares
|
|
414
|
+
},
|
|
415
|
+
/**
|
|
416
|
+
* Returns a Promise containing a new Dinero object converted to another currency.
|
|
417
|
+
*
|
|
418
|
+
* You have two options to provide the exchange rates:
|
|
419
|
+
*
|
|
420
|
+
* 1. **Use an exchange rate REST API, and let Dinero handle the fetching and conversion.**
|
|
421
|
+
* This is a simple option if you have access to an exchange rate REST API and want Dinero to do the rest.
|
|
422
|
+
* 2. **Fetch the exchange rates on your own and provide them directly.**
|
|
423
|
+
* This is useful if you're fetching your rates from somewhere else (a file, a database), use a different protocol or query language than REST (SOAP, GraphQL) or want to fetch rates once and cache them instead of making new requests every time.
|
|
424
|
+
*
|
|
425
|
+
* **If you want to use a REST API**, you must provide a third-party endpoint yourself. Dinero doesn't come bundled with an exchange rates endpoint.
|
|
426
|
+
*
|
|
427
|
+
* Here are some exchange rate APIs you can use:
|
|
428
|
+
*
|
|
429
|
+
* * [Fixer](https://fixer.io)
|
|
430
|
+
* * [Open Exchange Rates](https://openexchangerates.org)
|
|
431
|
+
* * [Coinbase](https://api.coinbase.com/v2/exchange-rates)
|
|
432
|
+
* * More [foreign](https://github.com/toddmotto/public-apis#currency-exchange) and [crypto](https://github.com/toddmotto/public-apis#cryptocurrency) exchange rate APIs.
|
|
433
|
+
*
|
|
434
|
+
* **If you want to fetch your own rates and provide them directly**, you need to pass a promise that resolves to the exchanges rates.
|
|
435
|
+
*
|
|
436
|
+
* In both cases, you need to specify at least:
|
|
437
|
+
*
|
|
438
|
+
* * a **destination currency**: the currency in which you want to convert your Dinero object. You can specify it with `currency`.
|
|
439
|
+
* * an **endpoint**: the API URL to query exchange rates, with parameters, or a promise that resolves to the exchange rates. You can specify it with `options.endpoint`.
|
|
440
|
+
* * a **property path**: the path to access the wanted rate in your API's JSON response (or the custom promise's payload). For example, with a response of:
|
|
441
|
+
* ```json
|
|
442
|
+
* {
|
|
443
|
+
* "data": {
|
|
444
|
+
* "base": "USD",
|
|
445
|
+
* "destination": "EUR",
|
|
446
|
+
* "rate": "0.827728919"
|
|
447
|
+
* }
|
|
448
|
+
* }
|
|
449
|
+
* ```
|
|
450
|
+
* Then the property path is `'data.rate'`. You can specify it with `options.propertyPath`.
|
|
451
|
+
*
|
|
452
|
+
* The base currency (the one of your Dinero object) and the destination currency can be used as "merge tags" with the mustache syntax, respectively `{{from}}` and `{{to}}`.
|
|
453
|
+
* You can use these tags to refer to these values in `options.endpoint` and `options.propertyPath`.
|
|
454
|
+
*
|
|
455
|
+
* For example, if you need to specify the base currency as a query parameter, you can do the following:
|
|
456
|
+
*
|
|
457
|
+
* ```js
|
|
458
|
+
* {
|
|
459
|
+
* endpoint: 'https://yourexchangerates.api/latest?base={{from}}'
|
|
460
|
+
* }
|
|
461
|
+
* ```
|
|
462
|
+
*
|
|
463
|
+
* @param {String} currency - The destination currency, expressed as an {@link https://en.wikipedia.org/wiki/ISO_4217#Active_codes ISO 4217 currency code}.
|
|
464
|
+
* @param {(String|Promise)} options.endpoint - The API endpoint to retrieve exchange rates. You can substitute this with a promise that resolves to the exchanges rates if you already have them.
|
|
465
|
+
* @param {String} [options.propertyPath='rates.{{to}}'] - The property path to the rate.
|
|
466
|
+
* @param {Object} [options.headers] - The HTTP headers to provide, if needed.
|
|
467
|
+
* @param {String} [options.roundingMode='HALF_EVEN'] - The rounding mode to use: `'HALF_ODD'`, `'HALF_EVEN'`, `'HALF_UP'`, `'HALF_DOWN'`, `'HALF_TOWARDS_ZERO'`, `'HALF_AWAY_FROM_ZERO'` or `'DOWN'`.
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* // your global API parameters
|
|
471
|
+
* Dinero.globalExchangeRatesApi = { ... }
|
|
472
|
+
*
|
|
473
|
+
* // returns a Promise containing a Dinero object with the destination currency
|
|
474
|
+
* // and the initial amount converted to the new currency.
|
|
475
|
+
* Dinero({ amount: 500 }).convert('EUR')
|
|
476
|
+
* @example
|
|
477
|
+
* // returns a Promise containing a Dinero object,
|
|
478
|
+
* // with specific API parameters and rounding mode for this specific instance.
|
|
479
|
+
* Dinero({ amount: 500 })
|
|
480
|
+
* .convert('XBT', {
|
|
481
|
+
* endpoint: 'https://yourexchangerates.api/latest?base={{from}}',
|
|
482
|
+
* propertyPath: 'data.rates.{{to}}',
|
|
483
|
+
* headers: {
|
|
484
|
+
* 'user-key': 'xxxxxxxxx'
|
|
485
|
+
* },
|
|
486
|
+
* roundingMode: 'HALF_UP'
|
|
487
|
+
* })
|
|
488
|
+
* @example
|
|
489
|
+
* // usage with exchange rates provided as a custom promise
|
|
490
|
+
* // using the default `propertyPath` format (so it doesn't have to be specified)
|
|
491
|
+
* const rates = {
|
|
492
|
+
* rates: {
|
|
493
|
+
* EUR: 0.81162
|
|
494
|
+
* }
|
|
495
|
+
* }
|
|
496
|
+
*
|
|
497
|
+
* Dinero({ amount: 500 })
|
|
498
|
+
* .convert('EUR', {
|
|
499
|
+
* endpoint: new Promise(resolve => resolve(rates))
|
|
500
|
+
* })
|
|
501
|
+
* @example
|
|
502
|
+
* // usage with Promise.prototype.then and Promise.prototype.catch
|
|
503
|
+
* Dinero({ amount: 500 })
|
|
504
|
+
* .convert('EUR')
|
|
505
|
+
* .then(dinero => {
|
|
506
|
+
* dinero.getCurrency() // returns 'EUR'
|
|
507
|
+
* })
|
|
508
|
+
* .catch(err => {
|
|
509
|
+
* // handle errors
|
|
510
|
+
* })
|
|
511
|
+
* @example
|
|
512
|
+
* // usage with async/await
|
|
513
|
+
* (async () => {
|
|
514
|
+
* const price = await Dinero({ amount: 500 }).convert('EUR')
|
|
515
|
+
* price.getCurrency() // returns 'EUR'
|
|
516
|
+
* })()
|
|
517
|
+
*
|
|
518
|
+
* @return {Promise}
|
|
519
|
+
*/
|
|
520
|
+
convert(
|
|
521
|
+
currency,
|
|
522
|
+
{
|
|
523
|
+
endpoint = globalExchangeRatesApi.endpoint,
|
|
524
|
+
propertyPath = globalExchangeRatesApi.propertyPath || 'rates.{{to}}',
|
|
525
|
+
headers = globalExchangeRatesApi.headers,
|
|
526
|
+
roundingMode = globalRoundingMode
|
|
527
|
+
} = {}
|
|
528
|
+
) {
|
|
529
|
+
const options = Object.assign(
|
|
530
|
+
{},
|
|
531
|
+
{
|
|
532
|
+
endpoint,
|
|
533
|
+
propertyPath,
|
|
534
|
+
headers,
|
|
535
|
+
roundingMode
|
|
536
|
+
}
|
|
537
|
+
)
|
|
538
|
+
return CurrencyConverter(options)
|
|
539
|
+
.getExchangeRate(this.getCurrency(), currency)
|
|
540
|
+
.then(rate => {
|
|
541
|
+
assert(
|
|
542
|
+
!isUndefined(rate),
|
|
543
|
+
`No rate was found for the destination currency "${currency}".`,
|
|
544
|
+
TypeError
|
|
545
|
+
)
|
|
546
|
+
return create.call(this, {
|
|
547
|
+
amount: calculator.round(
|
|
548
|
+
calculator.multiply(this.getAmount(), parseFloat(rate)),
|
|
549
|
+
options.roundingMode
|
|
550
|
+
),
|
|
551
|
+
currency
|
|
552
|
+
})
|
|
553
|
+
})
|
|
554
|
+
},
|
|
555
|
+
/**
|
|
556
|
+
* Checks whether the value represented by this object equals to the other.
|
|
557
|
+
*
|
|
558
|
+
* @param {Dinero} comparator - The Dinero object to compare to.
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* // returns true
|
|
562
|
+
* Dinero({ amount: 500, currency: 'EUR' }).equalsTo(Dinero({ amount: 500, currency: 'EUR' }))
|
|
563
|
+
* @example
|
|
564
|
+
* // returns false
|
|
565
|
+
* Dinero({ amount: 500, currency: 'EUR' }).equalsTo(Dinero({ amount: 800, currency: 'EUR' }))
|
|
566
|
+
* @example
|
|
567
|
+
* // returns false
|
|
568
|
+
* Dinero({ amount: 500, currency: 'USD' }).equalsTo(Dinero({ amount: 500, currency: 'EUR' }))
|
|
569
|
+
* @example
|
|
570
|
+
* // returns false
|
|
571
|
+
* Dinero({ amount: 500, currency: 'USD' }).equalsTo(Dinero({ amount: 800, currency: 'EUR' }))
|
|
572
|
+
* @example
|
|
573
|
+
* // returns true
|
|
574
|
+
* Dinero({ amount: 1000, currency: 'EUR', precision: 2 }).equalsTo(Dinero({ amount: 10000, currency: 'EUR', precision: 3 }))
|
|
575
|
+
* @example
|
|
576
|
+
* // returns false
|
|
577
|
+
* Dinero({ amount: 10000, currency: 'EUR', precision: 2 }).equalsTo(Dinero({ amount: 10000, currency: 'EUR', precision: 3 }))
|
|
578
|
+
*
|
|
579
|
+
* @return {Boolean}
|
|
580
|
+
*/
|
|
581
|
+
equalsTo(comparator) {
|
|
582
|
+
return this.hasSameAmount(comparator) && this.hasSameCurrency(comparator)
|
|
583
|
+
},
|
|
584
|
+
/**
|
|
585
|
+
* Checks whether the value represented by this object is less than the other.
|
|
586
|
+
*
|
|
587
|
+
* @param {Dinero} comparator - The Dinero object to compare to.
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* // returns true
|
|
591
|
+
* Dinero({ amount: 500 }).lessThan(Dinero({ amount: 800 }))
|
|
592
|
+
* @example
|
|
593
|
+
* // returns false
|
|
594
|
+
* Dinero({ amount: 800 }).lessThan(Dinero({ amount: 500 }))
|
|
595
|
+
* @example
|
|
596
|
+
* // returns true
|
|
597
|
+
* Dinero({ amount: 5000, precision: 3 }).lessThan(Dinero({ amount: 800 }))
|
|
598
|
+
* @example
|
|
599
|
+
* // returns false
|
|
600
|
+
* Dinero({ amount: 800 }).lessThan(Dinero({ amount: 5000, precision: 3 }))
|
|
601
|
+
*
|
|
602
|
+
* @throws {TypeError} If `comparator` has a different currency.
|
|
603
|
+
*
|
|
604
|
+
* @return {Boolean}
|
|
605
|
+
*/
|
|
606
|
+
lessThan(comparator) {
|
|
607
|
+
assertSameCurrency.call(this, comparator)
|
|
608
|
+
const comparators = Dinero.normalizePrecision([this, comparator])
|
|
609
|
+
return comparators[0].getAmount() < comparators[1].getAmount()
|
|
610
|
+
},
|
|
611
|
+
/**
|
|
612
|
+
* Checks whether the value represented by this object is less than or equal to the other.
|
|
613
|
+
*
|
|
614
|
+
* @param {Dinero} comparator - The Dinero object to compare to.
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* // returns true
|
|
618
|
+
* Dinero({ amount: 500 }).lessThanOrEqual(Dinero({ amount: 800 }))
|
|
619
|
+
* @example
|
|
620
|
+
* // returns true
|
|
621
|
+
* Dinero({ amount: 500 }).lessThanOrEqual(Dinero({ amount: 500 }))
|
|
622
|
+
* @example
|
|
623
|
+
* // returns false
|
|
624
|
+
* Dinero({ amount: 500 }).lessThanOrEqual(Dinero({ amount: 300 }))
|
|
625
|
+
* @example
|
|
626
|
+
* // returns true
|
|
627
|
+
* Dinero({ amount: 5000, precision: 3 }).lessThanOrEqual(Dinero({ amount: 800 }))
|
|
628
|
+
* @example
|
|
629
|
+
* // returns true
|
|
630
|
+
* Dinero({ amount: 5000, precision: 3 }).lessThanOrEqual(Dinero({ amount: 500 }))
|
|
631
|
+
* @example
|
|
632
|
+
* // returns false
|
|
633
|
+
* Dinero({ amount: 800 }).lessThanOrEqual(Dinero({ amount: 5000, precision: 3 }))
|
|
634
|
+
*
|
|
635
|
+
* @throws {TypeError} If `comparator` has a different currency.
|
|
636
|
+
*
|
|
637
|
+
* @return {Boolean}
|
|
638
|
+
*/
|
|
639
|
+
lessThanOrEqual(comparator) {
|
|
640
|
+
assertSameCurrency.call(this, comparator)
|
|
641
|
+
const comparators = Dinero.normalizePrecision([this, comparator])
|
|
642
|
+
return comparators[0].getAmount() <= comparators[1].getAmount()
|
|
643
|
+
},
|
|
644
|
+
/**
|
|
645
|
+
* Checks whether the value represented by this object is greater than the other.
|
|
646
|
+
*
|
|
647
|
+
* @param {Dinero} comparator - The Dinero object to compare to.
|
|
648
|
+
*
|
|
649
|
+
* @example
|
|
650
|
+
* // returns false
|
|
651
|
+
* Dinero({ amount: 500 }).greaterThan(Dinero({ amount: 800 }))
|
|
652
|
+
* @example
|
|
653
|
+
* // returns true
|
|
654
|
+
* Dinero({ amount: 800 }).greaterThan(Dinero({ amount: 500 }))
|
|
655
|
+
* @example
|
|
656
|
+
* // returns true
|
|
657
|
+
* Dinero({ amount: 800 }).greaterThan(Dinero({ amount: 5000, precision: 3 }))
|
|
658
|
+
* @example
|
|
659
|
+
* // returns false
|
|
660
|
+
* Dinero({ amount: 5000, precision: 3 }).greaterThan(Dinero({ amount: 800 }))
|
|
661
|
+
*
|
|
662
|
+
* @throws {TypeError} If `comparator` has a different currency.
|
|
663
|
+
*
|
|
664
|
+
* @return {Boolean}
|
|
665
|
+
*/
|
|
666
|
+
greaterThan(comparator) {
|
|
667
|
+
assertSameCurrency.call(this, comparator)
|
|
668
|
+
const comparators = Dinero.normalizePrecision([this, comparator])
|
|
669
|
+
return comparators[0].getAmount() > comparators[1].getAmount()
|
|
670
|
+
},
|
|
671
|
+
/**
|
|
672
|
+
* Checks whether the value represented by this object is greater than or equal to the other.
|
|
673
|
+
*
|
|
674
|
+
* @param {Dinero} comparator - The Dinero object to compare to.
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* // returns true
|
|
678
|
+
* Dinero({ amount: 500 }).greaterThanOrEqual(Dinero({ amount: 300 }))
|
|
679
|
+
* @example
|
|
680
|
+
* // returns true
|
|
681
|
+
* Dinero({ amount: 500 }).greaterThanOrEqual(Dinero({ amount: 500 }))
|
|
682
|
+
* @example
|
|
683
|
+
* // returns false
|
|
684
|
+
* Dinero({ amount: 500 }).greaterThanOrEqual(Dinero({ amount: 800 }))
|
|
685
|
+
* @example
|
|
686
|
+
* // returns true
|
|
687
|
+
* Dinero({ amount: 800 }).greaterThanOrEqual(Dinero({ amount: 5000, precision: 3 }))
|
|
688
|
+
* @example
|
|
689
|
+
* // returns true
|
|
690
|
+
* Dinero({ amount: 500 }).greaterThanOrEqual(Dinero({ amount: 5000, precision: 3 }))
|
|
691
|
+
* @example
|
|
692
|
+
* // returns false
|
|
693
|
+
* Dinero({ amount: 5000, precision: 3 }).greaterThanOrEqual(Dinero({ amount: 800 }))
|
|
694
|
+
*
|
|
695
|
+
* @throws {TypeError} If `comparator` has a different currency.
|
|
696
|
+
*
|
|
697
|
+
* @return {Boolean}
|
|
698
|
+
*/
|
|
699
|
+
greaterThanOrEqual(comparator) {
|
|
700
|
+
assertSameCurrency.call(this, comparator)
|
|
701
|
+
const comparators = Dinero.normalizePrecision([this, comparator])
|
|
702
|
+
return comparators[0].getAmount() >= comparators[1].getAmount()
|
|
703
|
+
},
|
|
704
|
+
/**
|
|
705
|
+
* Checks if the value represented by this object is zero.
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* // returns true
|
|
709
|
+
* Dinero({ amount: 0 }).isZero()
|
|
710
|
+
* @example
|
|
711
|
+
* // returns false
|
|
712
|
+
* Dinero({ amount: 100 }).isZero()
|
|
713
|
+
*
|
|
714
|
+
* @return {Boolean}
|
|
715
|
+
*/
|
|
716
|
+
isZero() {
|
|
717
|
+
return this.getAmount() === 0
|
|
718
|
+
},
|
|
719
|
+
/**
|
|
720
|
+
* Checks if the value represented by this object is positive.
|
|
721
|
+
*
|
|
722
|
+
* @example
|
|
723
|
+
* // returns false
|
|
724
|
+
* Dinero({ amount: -10 }).isPositive()
|
|
725
|
+
* @example
|
|
726
|
+
* // returns true
|
|
727
|
+
* Dinero({ amount: 10 }).isPositive()
|
|
728
|
+
* @example
|
|
729
|
+
* // returns true
|
|
730
|
+
* Dinero({ amount: 0 }).isPositive()
|
|
731
|
+
*
|
|
732
|
+
* @return {Boolean}
|
|
733
|
+
*/
|
|
734
|
+
isPositive() {
|
|
735
|
+
return this.getAmount() >= 0
|
|
736
|
+
},
|
|
737
|
+
/**
|
|
738
|
+
* Checks if the value represented by this object is negative.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* // returns true
|
|
742
|
+
* Dinero({ amount: -10 }).isNegative()
|
|
743
|
+
* @example
|
|
744
|
+
* // returns false
|
|
745
|
+
* Dinero({ amount: 10 }).isNegative()
|
|
746
|
+
* @example
|
|
747
|
+
* // returns false
|
|
748
|
+
* Dinero({ amount: 0 }).isNegative()
|
|
749
|
+
*
|
|
750
|
+
* @return {Boolean}
|
|
751
|
+
*/
|
|
752
|
+
isNegative() {
|
|
753
|
+
return this.getAmount() < 0
|
|
754
|
+
},
|
|
755
|
+
/**
|
|
756
|
+
* Checks if this has minor currency units.
|
|
757
|
+
* Deprecates {@link module:Dinero~hasCents hasCents}.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* // returns false
|
|
761
|
+
* Dinero({ amount: 1100 }).hasSubUnits()
|
|
762
|
+
* @example
|
|
763
|
+
* // returns true
|
|
764
|
+
* Dinero({ amount: 1150 }).hasSubUnits()
|
|
765
|
+
*
|
|
766
|
+
* @return {Boolean}
|
|
767
|
+
*/
|
|
768
|
+
hasSubUnits() {
|
|
769
|
+
return calculator.modulo(this.getAmount(), Math.pow(10, precision)) !== 0
|
|
770
|
+
},
|
|
771
|
+
/**
|
|
772
|
+
* Checks if this has minor currency units.
|
|
773
|
+
*
|
|
774
|
+
* @deprecated since version 1.4.0, will be removed in 2.0.0
|
|
775
|
+
* Use {@link module:Dinero~hasSubUnits hasSubUnits} instead.
|
|
776
|
+
*
|
|
777
|
+
* @example
|
|
778
|
+
* // returns false
|
|
779
|
+
* Dinero({ amount: 1100 }).hasCents()
|
|
780
|
+
* @example
|
|
781
|
+
* // returns true
|
|
782
|
+
* Dinero({ amount: 1150 }).hasCents()
|
|
783
|
+
*
|
|
784
|
+
* @return {Boolean}
|
|
785
|
+
*/
|
|
786
|
+
hasCents() {
|
|
787
|
+
return calculator.modulo(this.getAmount(), Math.pow(10, precision)) !== 0
|
|
788
|
+
},
|
|
789
|
+
/**
|
|
790
|
+
* Checks whether the currency represented by this object equals to the other.
|
|
791
|
+
*
|
|
792
|
+
* @param {Dinero} comparator - The Dinero object to compare to.
|
|
793
|
+
*
|
|
794
|
+
* @example
|
|
795
|
+
* // returns true
|
|
796
|
+
* Dinero({ amount: 2000, currency: 'EUR' }).hasSameCurrency(Dinero({ amount: 1000, currency: 'EUR' }))
|
|
797
|
+
* @example
|
|
798
|
+
* // returns false
|
|
799
|
+
* Dinero({ amount: 1000, currency: 'EUR' }).hasSameCurrency(Dinero({ amount: 1000, currency: 'USD' }))
|
|
800
|
+
*
|
|
801
|
+
* @return {Boolean}
|
|
802
|
+
*/
|
|
803
|
+
hasSameCurrency(comparator) {
|
|
804
|
+
return this.getCurrency() === comparator.getCurrency()
|
|
805
|
+
},
|
|
806
|
+
/**
|
|
807
|
+
* Checks whether the amount represented by this object equals to the other.
|
|
808
|
+
*
|
|
809
|
+
* @param {Dinero} comparator - The Dinero object to compare to.
|
|
810
|
+
*
|
|
811
|
+
* @example
|
|
812
|
+
* // returns true
|
|
813
|
+
* Dinero({ amount: 1000, currency: 'EUR' }).hasSameAmount(Dinero({ amount: 1000 }))
|
|
814
|
+
* @example
|
|
815
|
+
* // returns false
|
|
816
|
+
* Dinero({ amount: 2000, currency: 'EUR' }).hasSameAmount(Dinero({ amount: 1000, currency: 'EUR' }))
|
|
817
|
+
* @example
|
|
818
|
+
* // returns true
|
|
819
|
+
* Dinero({ amount: 1000, currency: 'EUR', precision: 2 }).hasSameAmount(Dinero({ amount: 10000, precision: 3 }))
|
|
820
|
+
* @example
|
|
821
|
+
* // returns false
|
|
822
|
+
* Dinero({ amount: 10000, currency: 'EUR', precision: 2 }).hasSameAmount(Dinero({ amount: 10000, precision: 3 }))
|
|
823
|
+
*
|
|
824
|
+
* @return {Boolean}
|
|
825
|
+
*/
|
|
826
|
+
hasSameAmount(comparator) {
|
|
827
|
+
const comparators = Dinero.normalizePrecision([this, comparator])
|
|
828
|
+
return comparators[0].getAmount() === comparators[1].getAmount()
|
|
829
|
+
},
|
|
830
|
+
/**
|
|
831
|
+
* Returns this object formatted as a string.
|
|
832
|
+
*
|
|
833
|
+
* The format is a mask which defines how the output string will be formatted.
|
|
834
|
+
* It defines whether to display a currency, in what format, how many fraction digits to display and whether to use grouping separators.
|
|
835
|
+
* The output is formatted according to the applying locale.
|
|
836
|
+
*
|
|
837
|
+
* Object | Format | String
|
|
838
|
+
* :--------------------------- | :---------------- | :---
|
|
839
|
+
* `Dinero({ amount: 500050 })` | `'$0,0.00'` | $5,000.50
|
|
840
|
+
* `Dinero({ amount: 500050 })` | `'$0,0'` | $5,001
|
|
841
|
+
* `Dinero({ amount: 500050 })` | `'$0'` | $5001
|
|
842
|
+
* `Dinero({ amount: 500050 })` | `'$0.0'` | $5000.5
|
|
843
|
+
* `Dinero({ amount: 500050 })` | `'USD0,0.0'` | USD5,000.5
|
|
844
|
+
* `Dinero({ amount: 500050 })` | `'0,0.0 dollar'` | 5,000.5 dollars
|
|
845
|
+
*
|
|
846
|
+
* Don't try to substitute the `$` sign or the `USD` code with your target currency, nor adapt the format string to the exact format you want.
|
|
847
|
+
* The format is a mask which defines a pattern and returns a valid, localized currency string.
|
|
848
|
+
* If you want to display the object in a custom way, either use {@link module:Dinero~getAmount getAmount}, {@link module:Dinero~toUnit toUnit} or {@link module:Dinero~toRoundedUnit toRoundedUnit} and manipulate the output string as you wish.
|
|
849
|
+
*
|
|
850
|
+
* {@link module:Dinero~toFormat toFormat} wraps around `Number.prototype.toLocaleString`. For that reason, **format will vary depending on how it's implemented in the end user's environment**.
|
|
851
|
+
*
|
|
852
|
+
* You can also use `toLocaleString` directly:
|
|
853
|
+
* `Dinero().toRoundedUnit(digits, roundingMode).toLocaleString(locale, options)`.
|
|
854
|
+
*
|
|
855
|
+
* By default, amounts are rounded using the **half away from zero** rule ([commercial rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)).
|
|
856
|
+
* You can also specify a different `roundingMode` to better fit your needs.
|
|
857
|
+
*
|
|
858
|
+
* @param {String} [format='$0,0.00'] - The format mask to format to.
|
|
859
|
+
* @param {String} [roundingMode='HALF_AWAY_FROM_ZERO'] - The rounding mode to use: `'HALF_ODD'`, `'HALF_EVEN'`, `'HALF_UP'`, `'HALF_DOWN'`, `'HALF_TOWARDS_ZERO'`, `'HALF_AWAY_FROM_ZERO'` or `'DOWN'`.
|
|
860
|
+
*
|
|
861
|
+
* @example
|
|
862
|
+
* // returns $2,000
|
|
863
|
+
* Dinero({ amount: 200000 }).toFormat('$0,0')
|
|
864
|
+
* @example
|
|
865
|
+
* // returns €50.5
|
|
866
|
+
* Dinero({ amount: 5050, currency: 'EUR' }).toFormat('$0,0.0')
|
|
867
|
+
* @example
|
|
868
|
+
* // returns 100 euros
|
|
869
|
+
* Dinero({ amount: 10000, currency: 'EUR' }).setLocale('fr-FR').toFormat('0,0 dollar')
|
|
870
|
+
* @example
|
|
871
|
+
* // returns 2000
|
|
872
|
+
* Dinero({ amount: 200000, currency: 'EUR' }).toFormat()
|
|
873
|
+
* @example
|
|
874
|
+
* // returns $10
|
|
875
|
+
* Dinero({ amount: 1050 }).toFormat('$0', 'HALF_EVEN')
|
|
876
|
+
*
|
|
877
|
+
* @return {String}
|
|
878
|
+
*/
|
|
879
|
+
toFormat(format = globalFormat, roundingMode = globalFormatRoundingMode) {
|
|
880
|
+
const formatter = Format(format)
|
|
881
|
+
|
|
882
|
+
return this.toRoundedUnit(
|
|
883
|
+
formatter.getMinimumFractionDigits(),
|
|
884
|
+
roundingMode
|
|
885
|
+
).toLocaleString(this.getLocale(), {
|
|
886
|
+
currencyDisplay: formatter.getCurrencyDisplay(),
|
|
887
|
+
useGrouping: formatter.getUseGrouping(),
|
|
888
|
+
minimumFractionDigits: formatter.getMinimumFractionDigits(),
|
|
889
|
+
style: formatter.getStyle(),
|
|
890
|
+
currency: this.getCurrency()
|
|
891
|
+
})
|
|
892
|
+
},
|
|
893
|
+
/**
|
|
894
|
+
* Returns the amount represented by this object in units.
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* // returns 10.5
|
|
898
|
+
* Dinero({ amount: 1050 }).toUnit()
|
|
899
|
+
* @example
|
|
900
|
+
* // returns 10.545
|
|
901
|
+
* Dinero({ amount: 10545, precision: 3 }).toUnit()
|
|
902
|
+
*
|
|
903
|
+
* @return {Number}
|
|
904
|
+
*/
|
|
905
|
+
toUnit() {
|
|
906
|
+
return calculator.divide(this.getAmount(), Math.pow(10, precision))
|
|
907
|
+
},
|
|
908
|
+
/**
|
|
909
|
+
* Returns the amount represented by this object in rounded units.
|
|
910
|
+
*
|
|
911
|
+
* By default, the method uses the **half away from zero** rule ([commercial rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)).
|
|
912
|
+
* You can also specify a different `roundingMode` to better fit your needs.
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* // returns 10.6
|
|
916
|
+
* Dinero({ amount: 1055 }).toRoundedUnit(1)
|
|
917
|
+
* @example
|
|
918
|
+
* // returns 10
|
|
919
|
+
* Dinero({ amount: 1050 }).toRoundedUnit(0, 'HALF_EVEN')
|
|
920
|
+
*
|
|
921
|
+
* @param {Number} digits - The number of fraction digits to round to.
|
|
922
|
+
* @param {String} [roundingMode='HALF_AWAY_FROM_ZERO'] - The rounding mode to use: `'HALF_ODD'`, `'HALF_EVEN'`, `'HALF_UP'`, `'HALF_DOWN'`, `'HALF_TOWARDS_ZERO'`, `'HALF_AWAY_FROM_ZERO'` or `'DOWN'`.
|
|
923
|
+
*
|
|
924
|
+
* @return {Number}
|
|
925
|
+
*/
|
|
926
|
+
toRoundedUnit(digits, roundingMode = globalFormatRoundingMode) {
|
|
927
|
+
const factor = Math.pow(10, digits)
|
|
928
|
+
return calculator.divide(
|
|
929
|
+
calculator.round(
|
|
930
|
+
calculator.multiply(this.toUnit(), factor),
|
|
931
|
+
roundingMode
|
|
932
|
+
),
|
|
933
|
+
factor
|
|
934
|
+
)
|
|
935
|
+
},
|
|
936
|
+
/**
|
|
937
|
+
* Returns the object's data as an object literal.
|
|
938
|
+
*
|
|
939
|
+
* @example
|
|
940
|
+
* // returns { amount: 500, currency: 'EUR', precision: 2 }
|
|
941
|
+
* Dinero({ amount: 500, currency: 'EUR', precision: 2 }).toObject()
|
|
942
|
+
*
|
|
943
|
+
* @return {Object}
|
|
944
|
+
*/
|
|
945
|
+
toObject() {
|
|
946
|
+
return {
|
|
947
|
+
amount,
|
|
948
|
+
currency,
|
|
949
|
+
precision
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
/**
|
|
953
|
+
* Returns the object's data as an object literal.
|
|
954
|
+
*
|
|
955
|
+
* Alias of {@link module:Dinero~toObject toObject}.
|
|
956
|
+
* It is defined so that calling `JSON.stringify` on a Dinero object will automatically extract the relevant data.
|
|
957
|
+
*
|
|
958
|
+
* @example
|
|
959
|
+
* // returns '{"amount":500,"currency":"EUR","precision":2}'
|
|
960
|
+
* JSON.stringify(Dinero({ amount: 500, currency: 'EUR', precision: 2 }))
|
|
961
|
+
*
|
|
962
|
+
* @return {Object}
|
|
963
|
+
*/
|
|
964
|
+
toJSON() {
|
|
965
|
+
return this.toObject()
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
export default Object.assign(Dinero, Defaults, Globals, Static)
|