@blamejs/blamejs-shop 0.3.44 → 0.3.46
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/CHANGELOG.md +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/catalog.js +508 -9
- package/lib/product-bulk-ops.js +10 -6
- package/lib/product-qa.js +40 -0
- package/lib/sales-tax-filings.js +8 -12
- package/lib/storefront.js +250 -244
- package/lib/subscription-analytics.js +30 -17
- package/lib/tax.js +20 -25
- package/lib/wishlist-alerts.js +3 -2
- package/lib/wishlist-digest.js +4 -1
- package/package.json +1 -1
|
@@ -185,6 +185,18 @@ var CADENCE_TO_MONTHLY = {
|
|
|
185
185
|
year: 1 / 12,
|
|
186
186
|
};
|
|
187
187
|
|
|
188
|
+
// Exact rational [numerator, denominator] forms of the cadence factors
|
|
189
|
+
// above, used by _monthlyMinor so the monthly-normalized minor-unit
|
|
190
|
+
// figure is computed in BigInt (half-even) with no float intermediate.
|
|
191
|
+
// The float CADENCE_TO_MONTHLY view above is retained only for the
|
|
192
|
+
// display-only dashboard export.
|
|
193
|
+
var CADENCE_TO_MONTHLY_RATIO = {
|
|
194
|
+
day: [30n, 1n],
|
|
195
|
+
week: [30n, 7n],
|
|
196
|
+
month: [1n, 1n],
|
|
197
|
+
year: [1n, 12n],
|
|
198
|
+
};
|
|
199
|
+
|
|
188
200
|
// Active = (status in active/trialing) AND not cancelled AND not paused.
|
|
189
201
|
var ACTIVE_STATUSES = ["active", "trialing"];
|
|
190
202
|
|
|
@@ -325,27 +337,28 @@ function _canonicalJson(value) {
|
|
|
325
337
|
|
|
326
338
|
// ---- cadence normalization ---------------------------------------------
|
|
327
339
|
//
|
|
328
|
-
// Plan amount × quantity normalized to a single monthly figure.
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
var floor = Math.floor(x);
|
|
335
|
-
var diff = x - floor;
|
|
336
|
-
if (diff < 0.5) return floor;
|
|
337
|
-
if (diff > 0.5) return floor + 1;
|
|
338
|
-
// diff === 0.5 — round to even
|
|
339
|
-
return floor % 2 === 0 ? floor : floor + 1;
|
|
340
|
-
}
|
|
340
|
+
// Plan amount × quantity normalized to a single monthly figure. The
|
|
341
|
+
// cadence factor (e.g. week → 30/7, year → 1/12) is applied as an
|
|
342
|
+
// exact rational in BigInt half-even via b.money, so the monthly
|
|
343
|
+
// minor-unit total carries no float rounding and two interval
|
|
344
|
+
// combinations producing e.g. 4499.5 minor units settle on a stable
|
|
345
|
+
// even value rather than oscillating the dashboard by a cent.
|
|
341
346
|
|
|
342
347
|
function _monthlyMinor(amountMinor, interval, intervalCount, quantity) {
|
|
343
|
-
var
|
|
344
|
-
if (
|
|
348
|
+
var ratio = CADENCE_TO_MONTHLY_RATIO[interval];
|
|
349
|
+
if (ratio == null) return 0;
|
|
350
|
+
if (!Number.isInteger(amountMinor)) return 0;
|
|
345
351
|
var qty = Number.isInteger(quantity) && quantity > 0 ? quantity : 1;
|
|
346
352
|
var ic = Number.isInteger(intervalCount) && intervalCount > 0 ? intervalCount : 1;
|
|
347
|
-
|
|
348
|
-
|
|
353
|
+
// monthly = amount × qty × (num/den) / ic
|
|
354
|
+
// = amount × qty × num / (den × ic)
|
|
355
|
+
var num = BigInt(amountMinor) * BigInt(qty) * ratio[0];
|
|
356
|
+
var den = ratio[1] * BigInt(ic);
|
|
357
|
+
return Number(
|
|
358
|
+
b.money.fromMinorUnits(num >= 0n ? num : 0n, "USD")
|
|
359
|
+
.multiply([1n, den], { rounding: "half-even" })
|
|
360
|
+
.toMinorUnits()
|
|
361
|
+
);
|
|
349
362
|
}
|
|
350
363
|
|
|
351
364
|
// ---- active-at predicate (in SQL) --------------------------------------
|
package/lib/tax.js
CHANGED
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
* additive `lines[].tax_class` field.
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
+
var b = require("./vendor/blamejs");
|
|
41
|
+
|
|
40
42
|
var COUNTRY_RE = /^[A-Z]{2}$/;
|
|
41
43
|
var STATE_RE = /^[A-Z0-9]{1,5}$/;
|
|
42
44
|
var POSTAL_RE = /^[A-Za-z0-9 -]{1,16}$/;
|
|
@@ -72,6 +74,19 @@ function _nonNegInt(n, label) {
|
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
// Multiply a non-negative minor-unit integer by the rational num/den
|
|
78
|
+
// using the framework's BigInt money primitive (half-even). The result
|
|
79
|
+
// is an integer minor-unit count in the SAME unit as `minor` — the
|
|
80
|
+
// divisor is currency-exponent-independent, so a fixed carrier currency
|
|
81
|
+
// is correct (see lib/quantity-discounts.js for the same technique).
|
|
82
|
+
function _mulRational(minor, num, den) {
|
|
83
|
+
return Number(
|
|
84
|
+
b.money.fromMinorUnits(BigInt(minor), "USD")
|
|
85
|
+
.multiply([BigInt(num), BigInt(den)], { rounding: "half-even" })
|
|
86
|
+
.toMinorUnits()
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
function _validateRule(rule, i) {
|
|
76
91
|
if (!rule || typeof rule !== "object") throw new TypeError("tax: rule[" + i + "] must be an object");
|
|
77
92
|
_country(rule.country, "rule[" + i + "].country");
|
|
@@ -121,17 +136,9 @@ function operatorTable(opts) {
|
|
|
121
136
|
if (_matches(rules[i], ctx.shipTo)) { matched = rules[i]; break; }
|
|
122
137
|
}
|
|
123
138
|
var rateBps = matched ? matched.rate_bps : 0;
|
|
124
|
-
// tax = subtotal × bps / 10000,
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
// so we implement banker's by hand.
|
|
128
|
-
var raw = ctx.subtotal_minor * rateBps / 10000;
|
|
129
|
-
var floor = Math.floor(raw);
|
|
130
|
-
var frac = raw - floor;
|
|
131
|
-
var tax;
|
|
132
|
-
if (frac < 0.5) tax = floor;
|
|
133
|
-
else if (frac > 0.5) tax = floor + 1;
|
|
134
|
-
else tax = (floor % 2 === 0) ? floor : floor + 1; // even
|
|
139
|
+
// tax = subtotal × bps / 10000, half-even via b.money (BigInt —
|
|
140
|
+
// no float intermediate, exact for large subtotals).
|
|
141
|
+
var tax = _mulRational(ctx.subtotal_minor, rateBps, 10000);
|
|
135
142
|
return {
|
|
136
143
|
tax_minor: tax,
|
|
137
144
|
rate_bps: rateBps,
|
|
@@ -171,18 +178,6 @@ function create(opts) {
|
|
|
171
178
|
// primitives compose against. The operator-table adapter still owns
|
|
172
179
|
// jurisdiction-rule selection.
|
|
173
180
|
|
|
174
|
-
// Banker's rounding (round-half-to-even) for non-negative `raw`.
|
|
175
|
-
// JS Math.round is round-half-up, which biases tax totals upward
|
|
176
|
-
// across many small carts; this matches the rounding mode the
|
|
177
|
-
// existing operatorTable adapter uses.
|
|
178
|
-
function _bankersRound(raw) {
|
|
179
|
-
var floor = Math.floor(raw);
|
|
180
|
-
var frac = raw - floor;
|
|
181
|
-
if (frac < 0.5) return floor;
|
|
182
|
-
if (frac > 0.5) return floor + 1;
|
|
183
|
-
return (floor % 2 === 0) ? floor : floor + 1;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
181
|
function _currency(c) {
|
|
187
182
|
if (typeof c !== "string" || !/^[A-Z]{3}$/.test(c)) {
|
|
188
183
|
throw new TypeError("tax: currency must be a 3-letter ISO 4217 code (uppercase), got " + JSON.stringify(c));
|
|
@@ -210,7 +205,7 @@ function calculateInclusive(args) {
|
|
|
210
205
|
_currency(args.currency);
|
|
211
206
|
var gross = args.amount_minor;
|
|
212
207
|
var rate = args.rate_bps;
|
|
213
|
-
var tax =
|
|
208
|
+
var tax = _mulRational(gross, rate, 10000 + rate);
|
|
214
209
|
return {
|
|
215
210
|
net_minor: gross - tax,
|
|
216
211
|
tax_minor: tax,
|
|
@@ -237,7 +232,7 @@ function calculateExclusive(args) {
|
|
|
237
232
|
_currency(args.currency);
|
|
238
233
|
var net = args.amount_minor;
|
|
239
234
|
var rate = args.rate_bps;
|
|
240
|
-
var tax =
|
|
235
|
+
var tax = _mulRational(net, rate, 10000);
|
|
241
236
|
return {
|
|
242
237
|
net_minor: net,
|
|
243
238
|
tax_minor: tax,
|
package/lib/wishlist-alerts.js
CHANGED
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
*/
|
|
90
90
|
|
|
91
91
|
var b = require("./vendor/blamejs");
|
|
92
|
+
var pricing = require("./pricing");
|
|
92
93
|
|
|
93
94
|
var C = b.constants;
|
|
94
95
|
|
|
@@ -770,10 +771,10 @@ function create(opts) {
|
|
|
770
771
|
var dispatchOk = false;
|
|
771
772
|
try {
|
|
772
773
|
var oldPriceStr = v.payload.old_amount_minor != null
|
|
773
|
-
?
|
|
774
|
+
? pricing.format(v.payload.old_amount_minor, v.payload.currency)
|
|
774
775
|
: "—";
|
|
775
776
|
var newPriceStr = v.payload.new_amount_minor != null
|
|
776
|
-
?
|
|
777
|
+
? pricing.format(v.payload.new_amount_minor, v.payload.currency)
|
|
777
778
|
: "—";
|
|
778
779
|
var pctStr = v.payload.discount_bps != null
|
|
779
780
|
? String(Math.floor(v.payload.discount_bps / 100)) + "%"
|
package/lib/wishlist-digest.js
CHANGED
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
*/
|
|
112
112
|
|
|
113
113
|
var b = require("./vendor/blamejs");
|
|
114
|
+
var pricing = require("./pricing");
|
|
114
115
|
|
|
115
116
|
var C = b.constants;
|
|
116
117
|
|
|
@@ -781,7 +782,9 @@ function create(opts) {
|
|
|
781
782
|
}
|
|
782
783
|
var priceStr = "—";
|
|
783
784
|
if (price && typeof price.amount_minor === "number") {
|
|
784
|
-
|
|
785
|
+
try {
|
|
786
|
+
priceStr = pricing.format(price.amount_minor, price.currency || currency);
|
|
787
|
+
} catch (_efmt) { priceStr = "—"; }
|
|
785
788
|
}
|
|
786
789
|
|
|
787
790
|
// Stock change — best-effort. The catalog dep doesn't guarantee
|
package/package.json
CHANGED