@intelact/driveup 2.5.0 → 2.5.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/lib/utils/money.d.ts +23 -36
- package/lib/utils/money.js +25 -42
- package/lib/utils/money.js.map +1 -1
- package/package.json +1 -1
package/lib/utils/money.d.ts
CHANGED
|
@@ -5,11 +5,17 @@ type Amount = number | string | Money;
|
|
|
5
5
|
/**
|
|
6
6
|
* An amount of money, and the rules amounts follow
|
|
7
7
|
* - Every calculation is done in decimal, never in binary: a JavaScript number cannot hold 0.1 + 0.2 or 30 / 28, and an
|
|
8
|
-
* amount rounded after such a calculation can land on the wrong
|
|
9
|
-
* - Immutable: every operation returns a new Money. An amount becomes a number again
|
|
10
|
-
*
|
|
8
|
+
* amount rounded after such a calculation can land on the wrong cent
|
|
9
|
+
* - Immutable: every operation returns a new Money. An amount becomes a number again through `toNumber`, which is the
|
|
10
|
+
* one place an amount is rounded, to the cent
|
|
11
11
|
* - Amounts are rounded at the end of a calculation, never per item along the way: the items of a bill or an invoice are
|
|
12
|
-
* added up
|
|
12
|
+
* added up at full precision, and only the total they add up to becomes a number
|
|
13
|
+
* - DO keep one calculation in one chain, and call `toNumber` once, at the very end of it
|
|
14
|
+
* - DON'T turn a Money back into a number in the middle and carry on from there. Every `toNumber` rounds, so a
|
|
15
|
+
* calculation broken into steps rounds at every step and drifts away from the right answer:
|
|
16
|
+
*
|
|
17
|
+
* Money.of(35).multiply(8).divide(31).toNumber() // 9.03, one rounding, correct
|
|
18
|
+
* Money.of(Money.of(35).divide(31).toNumber()).multiply(8) // 9.04, rounded twice, a cent too much
|
|
13
19
|
* - This class exists twice, identically: here in @intelact/driveup (used by driveup-api) and in @driveup/common
|
|
14
20
|
* (used by driveup-schema), because neither package may depend on the other. Change one, change the other
|
|
15
21
|
*/
|
|
@@ -17,15 +23,10 @@ export declare class Money {
|
|
|
17
23
|
#private;
|
|
18
24
|
private readonly amount;
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
26
|
+
* The decimals an amount is held to. The cent is the smallest unit of money in the system: every amount, whether it
|
|
27
|
+
* is stored per item or charged as a total, lands on one, and every money column holds 2 decimals
|
|
21
28
|
*/
|
|
22
|
-
static readonly
|
|
23
|
-
/**
|
|
24
|
-
* The step an amount is kept to when it is stored per item rather than charged: a bill item, an invoice line. Money
|
|
25
|
-
* columns hold 2 decimals, so an amount has to land on a cent before it is saved, while the totals it adds up to are
|
|
26
|
-
* rounded to {@link step}
|
|
27
|
-
*/
|
|
28
|
-
static readonly cent = "0.01";
|
|
29
|
+
private static readonly decimals;
|
|
29
30
|
/**
|
|
30
31
|
* The decimals a rounding looks at. Anything beyond is noise rather than money: our smallest real amount is a
|
|
31
32
|
* millionth (an amount of 2 decimals times a tax rate of 2 decimals), while the binary noise a JavaScript number
|
|
@@ -36,8 +37,8 @@ export declare class Money {
|
|
|
36
37
|
/**
|
|
37
38
|
* Reads an amount: a number, the string a DECIMAL column is read as, or another Money
|
|
38
39
|
* - A number is read to 15 significant digits, the precision a JavaScript number really has. What lies beyond is
|
|
39
|
-
* binary noise, and it must not survive: 0.1 + 0.2 is 0.30000000000000004, which
|
|
40
|
-
*
|
|
40
|
+
* binary noise, and it must not survive: 0.1 + 0.2 is 0.30000000000000004, which must read as 0.3. A string is
|
|
41
|
+
* read exactly, digit for digit
|
|
41
42
|
* - null, undefined and an empty string read as 0
|
|
42
43
|
* @param value Amount - The amount to read
|
|
43
44
|
* @returns Money - The amount
|
|
@@ -73,28 +74,14 @@ export declare class Money {
|
|
|
73
74
|
*/
|
|
74
75
|
tax(vat: Amount): Money;
|
|
75
76
|
/**
|
|
76
|
-
* The amount
|
|
77
|
-
* -
|
|
78
|
-
*
|
|
79
|
-
* - Only the first {@link noiseFreeDecimals} decimals decide
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
|
|
84
|
-
round(step?: Amount): number;
|
|
85
|
-
/**
|
|
86
|
-
* The amount rounded down to the multiple of 0.05 at or below it
|
|
87
|
-
* - The exception to rounding up, for an amount that is taken rather than charged: what a company pays is rounded up,
|
|
88
|
-
* but the credit taken from it is rounded down, so we never take credit it does not have. 12.29 of credit is taken
|
|
89
|
-
* as 12.25, and the 0.04 stays in the company's credit
|
|
90
|
-
* - A negative amount is rounded towards zero the same way, and noise is dropped first, as in {@link round}
|
|
91
|
-
* @param step Amount - The step to round to, 0.05 by default
|
|
92
|
-
* @returns number - The amount, a multiple of the step
|
|
93
|
-
*/
|
|
94
|
-
roundDown(step?: Amount): number;
|
|
95
|
-
/**
|
|
96
|
-
* The amount as a number, unrounded
|
|
97
|
-
* @returns number - The amount
|
|
77
|
+
* The amount as a number, to the cent
|
|
78
|
+
* - This is the one place an amount is rounded, and half a cent goes up: 2.234 becomes 2.23, 2.235 becomes 2.24. No
|
|
79
|
+
* amount is ever nudged to a coin: the cent is the smallest unit there is
|
|
80
|
+
* - Only the first {@link noiseFreeDecimals} decimals decide, so noise left by an earlier calculation cannot flip an
|
|
81
|
+
* amount that sits exactly on half a cent
|
|
82
|
+
* - Call it once, at the end of the whole calculation. Calling it halfway and feeding the number back in rounds
|
|
83
|
+
* twice: `35 / 31 * 8` is 9.03 in one chain, but 9.04 if the division is turned into a number first
|
|
84
|
+
* @returns number - The amount, to 2 decimals
|
|
98
85
|
*/
|
|
99
86
|
toNumber(): number;
|
|
100
87
|
}
|
package/lib/utils/money.js
CHANGED
|
@@ -12,16 +12,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.Money = void 0;
|
|
13
13
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
14
14
|
// 40 significant digits, far more than any amount needs: a division such as 30 / 28 keeps every digit that could
|
|
15
|
-
// still change
|
|
15
|
+
// still change the cent an amount lands on, and our amounts carry at most 15
|
|
16
16
|
decimal_js_1.default.set({ precision: 40, rounding: decimal_js_1.default.ROUND_HALF_UP });
|
|
17
17
|
/**
|
|
18
18
|
* An amount of money, and the rules amounts follow
|
|
19
19
|
* - Every calculation is done in decimal, never in binary: a JavaScript number cannot hold 0.1 + 0.2 or 30 / 28, and an
|
|
20
|
-
* amount rounded after such a calculation can land on the wrong
|
|
21
|
-
* - Immutable: every operation returns a new Money. An amount becomes a number again
|
|
22
|
-
*
|
|
20
|
+
* amount rounded after such a calculation can land on the wrong cent
|
|
21
|
+
* - Immutable: every operation returns a new Money. An amount becomes a number again through `toNumber`, which is the
|
|
22
|
+
* one place an amount is rounded, to the cent
|
|
23
23
|
* - Amounts are rounded at the end of a calculation, never per item along the way: the items of a bill or an invoice are
|
|
24
|
-
* added up
|
|
24
|
+
* added up at full precision, and only the total they add up to becomes a number
|
|
25
|
+
* - DO keep one calculation in one chain, and call `toNumber` once, at the very end of it
|
|
26
|
+
* - DON'T turn a Money back into a number in the middle and carry on from there. Every `toNumber` rounds, so a
|
|
27
|
+
* calculation broken into steps rounds at every step and drifts away from the right answer:
|
|
28
|
+
*
|
|
29
|
+
* Money.of(35).multiply(8).divide(31).toNumber() // 9.03, one rounding, correct
|
|
30
|
+
* Money.of(Money.of(35).divide(31).toNumber()).multiply(8) // 9.04, rounded twice, a cent too much
|
|
25
31
|
* - This class exists twice, identically: here in @intelact/driveup (used by driveup-api) and in @driveup/common
|
|
26
32
|
* (used by driveup-schema), because neither package may depend on the other. Change one, change the other
|
|
27
33
|
*/
|
|
@@ -33,8 +39,8 @@ class Money {
|
|
|
33
39
|
/**
|
|
34
40
|
* Reads an amount: a number, the string a DECIMAL column is read as, or another Money
|
|
35
41
|
* - A number is read to 15 significant digits, the precision a JavaScript number really has. What lies beyond is
|
|
36
|
-
* binary noise, and it must not survive: 0.1 + 0.2 is 0.30000000000000004, which
|
|
37
|
-
*
|
|
42
|
+
* binary noise, and it must not survive: 0.1 + 0.2 is 0.30000000000000004, which must read as 0.3. A string is
|
|
43
|
+
* read exactly, digit for digit
|
|
38
44
|
* - null, undefined and an empty string read as 0
|
|
39
45
|
* @param value Amount - The amount to read
|
|
40
46
|
* @returns Money - The amount
|
|
@@ -96,35 +102,17 @@ class Money {
|
|
|
96
102
|
return this.multiply(vat).divide(100);
|
|
97
103
|
}
|
|
98
104
|
/**
|
|
99
|
-
* The amount
|
|
100
|
-
* -
|
|
101
|
-
*
|
|
102
|
-
* - Only the first {@link noiseFreeDecimals} decimals decide
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
round(step = _a.step) {
|
|
108
|
-
return __classPrivateFieldGet(this, _Money_instances, "m", _Money_withoutNoise).call(this).toNearest(_a.of(step).amount, decimal_js_1.default.ROUND_UP).toNumber();
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* The amount rounded down to the multiple of 0.05 at or below it
|
|
112
|
-
* - The exception to rounding up, for an amount that is taken rather than charged: what a company pays is rounded up,
|
|
113
|
-
* but the credit taken from it is rounded down, so we never take credit it does not have. 12.29 of credit is taken
|
|
114
|
-
* as 12.25, and the 0.04 stays in the company's credit
|
|
115
|
-
* - A negative amount is rounded towards zero the same way, and noise is dropped first, as in {@link round}
|
|
116
|
-
* @param step Amount - The step to round to, 0.05 by default
|
|
117
|
-
* @returns number - The amount, a multiple of the step
|
|
118
|
-
*/
|
|
119
|
-
roundDown(step = _a.step) {
|
|
120
|
-
return __classPrivateFieldGet(this, _Money_instances, "m", _Money_withoutNoise).call(this).toNearest(_a.of(step).amount, decimal_js_1.default.ROUND_DOWN).toNumber();
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* The amount as a number, unrounded
|
|
124
|
-
* @returns number - The amount
|
|
105
|
+
* The amount as a number, to the cent
|
|
106
|
+
* - This is the one place an amount is rounded, and half a cent goes up: 2.234 becomes 2.23, 2.235 becomes 2.24. No
|
|
107
|
+
* amount is ever nudged to a coin: the cent is the smallest unit there is
|
|
108
|
+
* - Only the first {@link noiseFreeDecimals} decimals decide, so noise left by an earlier calculation cannot flip an
|
|
109
|
+
* amount that sits exactly on half a cent
|
|
110
|
+
* - Call it once, at the end of the whole calculation. Calling it halfway and feeding the number back in rounds
|
|
111
|
+
* twice: `35 / 31 * 8` is 9.03 in one chain, but 9.04 if the division is turned into a number first
|
|
112
|
+
* @returns number - The amount, to 2 decimals
|
|
125
113
|
*/
|
|
126
114
|
toNumber() {
|
|
127
|
-
return this.
|
|
115
|
+
return __classPrivateFieldGet(this, _Money_instances, "m", _Money_withoutNoise).call(this).toDecimalPlaces(_a.decimals, decimal_js_1.default.ROUND_HALF_UP).toNumber();
|
|
128
116
|
}
|
|
129
117
|
}
|
|
130
118
|
exports.Money = Money;
|
|
@@ -132,15 +120,10 @@ _a = Money, _Money_instances = new WeakSet(), _Money_withoutNoise = function _Mo
|
|
|
132
120
|
return this.amount.toDecimalPlaces(_a.noiseFreeDecimals, decimal_js_1.default.ROUND_HALF_UP);
|
|
133
121
|
};
|
|
134
122
|
/**
|
|
135
|
-
*
|
|
136
|
-
|
|
137
|
-
Money.step = '0.05';
|
|
138
|
-
/**
|
|
139
|
-
* The step an amount is kept to when it is stored per item rather than charged: a bill item, an invoice line. Money
|
|
140
|
-
* columns hold 2 decimals, so an amount has to land on a cent before it is saved, while the totals it adds up to are
|
|
141
|
-
* rounded to {@link step}
|
|
123
|
+
* The decimals an amount is held to. The cent is the smallest unit of money in the system: every amount, whether it
|
|
124
|
+
* is stored per item or charged as a total, lands on one, and every money column holds 2 decimals
|
|
142
125
|
*/
|
|
143
|
-
Money.
|
|
126
|
+
Money.decimals = 2;
|
|
144
127
|
/**
|
|
145
128
|
* The decimals a rounding looks at. Anything beyond is noise rather than money: our smallest real amount is a
|
|
146
129
|
* millionth (an amount of 2 decimals times a tax rate of 2 decimals), while the binary noise a JavaScript number
|
package/lib/utils/money.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"money.js","sourceRoot":"","sources":["../../src/utils/money.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,4DAAiC;AAEjC,iHAAiH;AACjH,
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../../src/utils/money.ts"],"names":[],"mappings":"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"}
|