@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.
@@ -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 side of a rounding step
9
- * - Immutable: every operation returns a new Money. An amount becomes a number again at the end of the calculation,
10
- * through `round`, `roundDown` or `toNumber`
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 as they are, and the total is rounded once
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
- * Every rounded total is a multiple of this step
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 step = "0.05";
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 would otherwise be rounded up a
40
- * whole step. A string is read exactly, digit for digit
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 rounded up to the next multiple of 0.05
77
- * - An amount above a multiple goes up to the next one: 0.06 becomes 0.10, 0.96 becomes 1.00 and 0.056 becomes 0.10.
78
- * An amount that already is a multiple is kept. A negative amount is rounded away from zero
79
- * - Only the first {@link noiseFreeDecimals} decimals decide: an amount is snapped to them first, so noise left by an
80
- * earlier calculation cannot push an amount that sits on a multiple up a whole step
81
- * @param step Amount - The step to round to, 0.05 by default. Pass {@link cent} for an amount stored per item
82
- * @returns number - The amount, a multiple of the step
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
  }
@@ -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 a rounding step, and our amounts carry at most 15
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 side of a rounding step
21
- * - Immutable: every operation returns a new Money. An amount becomes a number again at the end of the calculation,
22
- * through `round`, `roundDown` or `toNumber`
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 as they are, and the total is rounded once
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 would otherwise be rounded up a
37
- * whole step. A string is read exactly, digit for digit
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 rounded up to the next multiple of 0.05
100
- * - An amount above a multiple goes up to the next one: 0.06 becomes 0.10, 0.96 becomes 1.00 and 0.056 becomes 0.10.
101
- * An amount that already is a multiple is kept. A negative amount is rounded away from zero
102
- * - Only the first {@link noiseFreeDecimals} decimals decide: an amount is snapped to them first, so noise left by an
103
- * earlier calculation cannot push an amount that sits on a multiple up a whole step
104
- * @param step Amount - The step to round to, 0.05 by default. Pass {@link cent} for an amount stored per item
105
- * @returns number - The amount, a multiple of the step
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.amount.toNumber();
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
- * Every rounded total is a multiple of this step
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.cent = '0.01';
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
@@ -1 +1 @@
1
- {"version":3,"file":"money.js","sourceRoot":"","sources":["../../src/utils/money.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"money.js","sourceRoot":"","sources":["../../src/utils/money.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intelact/driveup",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "Driveup Common",
5
5
  "author": "Intelact Solutions Ltd.",
6
6
  "main": "lib/index.js",