@labdigital/commercetools-mock 2.62.1 → 2.63.0
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/dist/index.d.mts +134 -134
- package/dist/index.mjs +89 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/repositories/cart/actions.ts +39 -11
- package/src/repositories/cart/helpers.ts +39 -4
- package/src/repositories/cart/index.test.ts +32 -0
- package/src/repositories/cart/index.ts +29 -8
- package/src/repositories/helpers.test.ts +113 -2
- package/src/repositories/helpers.ts +107 -17
- package/src/repositories/order/index.ts +5 -4
- package/src/repositories/shipping-method/helpers.ts +3 -3
- package/src/services/cart.test.ts +162 -0
package/package.json
CHANGED
|
@@ -42,6 +42,7 @@ import type {
|
|
|
42
42
|
ProductVariant,
|
|
43
43
|
} from "@commercetools/platform-sdk";
|
|
44
44
|
import type {
|
|
45
|
+
CartAddDiscountCodeAction,
|
|
45
46
|
CustomLineItem,
|
|
46
47
|
DirectDiscount,
|
|
47
48
|
} from "@commercetools/platform-sdk/dist/declarations/src/generated/models/cart";
|
|
@@ -52,6 +53,7 @@ import type { Writable } from "#src/types.ts";
|
|
|
52
53
|
import type { UpdateHandlerInterface } from "../abstract.ts";
|
|
53
54
|
import { AbstractUpdateHandler, type RepositoryContext } from "../abstract.ts";
|
|
54
55
|
import {
|
|
56
|
+
calculateMoneyTotalCentAmount,
|
|
55
57
|
createAddress,
|
|
56
58
|
createCentPrecisionMoney,
|
|
57
59
|
createCustomFields,
|
|
@@ -61,6 +63,7 @@ import {
|
|
|
61
63
|
calculateCartTotalPrice,
|
|
62
64
|
calculateLineItemTotalPrice,
|
|
63
65
|
createCustomLineItemFromDraft,
|
|
66
|
+
createDiscountCodeInfoFromCode,
|
|
64
67
|
selectPrice,
|
|
65
68
|
} from "./helpers.ts";
|
|
66
69
|
import type { CartRepository } from "./index.ts";
|
|
@@ -183,6 +186,10 @@ export class CartUpdateHandler
|
|
|
183
186
|
`No valid price found for ${productId} for country ${resource.country} and currency ${currency}`,
|
|
184
187
|
);
|
|
185
188
|
}
|
|
189
|
+
const totalPrice = createCentPrecisionMoney({
|
|
190
|
+
currencyCode: price.value.currencyCode,
|
|
191
|
+
centAmount: calculateMoneyTotalCentAmount(price.value, quantity),
|
|
192
|
+
});
|
|
186
193
|
resource.lineItems.push({
|
|
187
194
|
id: uuidv4(),
|
|
188
195
|
key,
|
|
@@ -196,11 +203,7 @@ export class CartUpdateHandler
|
|
|
196
203
|
price: price,
|
|
197
204
|
taxedPricePortions: [],
|
|
198
205
|
perMethodTaxRate: [],
|
|
199
|
-
totalPrice
|
|
200
|
-
...price.value,
|
|
201
|
-
type: "centPrecision",
|
|
202
|
-
centAmount: price.value.centAmount * quantity,
|
|
203
|
-
},
|
|
206
|
+
totalPrice,
|
|
204
207
|
quantity,
|
|
205
208
|
discountedPricePerQuantity: [],
|
|
206
209
|
lineItemMode: "Standard",
|
|
@@ -280,6 +283,25 @@ export class CartUpdateHandler
|
|
|
280
283
|
// and prices on related Products have changed in the meanwhile.
|
|
281
284
|
}
|
|
282
285
|
|
|
286
|
+
addDiscountCode(
|
|
287
|
+
context: RepositoryContext,
|
|
288
|
+
resource: Writable<Cart>,
|
|
289
|
+
{ code }: CartAddDiscountCodeAction,
|
|
290
|
+
) {
|
|
291
|
+
const info = createDiscountCodeInfoFromCode(
|
|
292
|
+
context.projectKey,
|
|
293
|
+
this._storage,
|
|
294
|
+
code,
|
|
295
|
+
);
|
|
296
|
+
if (
|
|
297
|
+
!resource.discountCodes
|
|
298
|
+
.map((dc) => dc.discountCode.id)
|
|
299
|
+
.includes(info.discountCode.id)
|
|
300
|
+
) {
|
|
301
|
+
resource.discountCodes.push(info);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
283
305
|
removeDiscountCode(
|
|
284
306
|
context: RepositoryContext,
|
|
285
307
|
resource: Writable<Cart>,
|
|
@@ -427,8 +449,11 @@ export class CartUpdateHandler
|
|
|
427
449
|
}
|
|
428
450
|
customLineItem.quantity = quantity;
|
|
429
451
|
customLineItem.totalPrice = createCentPrecisionMoney({
|
|
430
|
-
|
|
431
|
-
centAmount: (
|
|
452
|
+
currencyCode: customLineItem.money.currencyCode,
|
|
453
|
+
centAmount: calculateMoneyTotalCentAmount(
|
|
454
|
+
customLineItem.money,
|
|
455
|
+
quantity,
|
|
456
|
+
),
|
|
432
457
|
});
|
|
433
458
|
};
|
|
434
459
|
|
|
@@ -470,8 +495,11 @@ export class CartUpdateHandler
|
|
|
470
495
|
}
|
|
471
496
|
customLineItem.money = createTypedMoney(money);
|
|
472
497
|
customLineItem.totalPrice = createCentPrecisionMoney({
|
|
473
|
-
|
|
474
|
-
centAmount: (
|
|
498
|
+
currencyCode: money.currencyCode,
|
|
499
|
+
centAmount: calculateMoneyTotalCentAmount(
|
|
500
|
+
money,
|
|
501
|
+
customLineItem.quantity,
|
|
502
|
+
),
|
|
475
503
|
});
|
|
476
504
|
};
|
|
477
505
|
|
|
@@ -608,7 +636,7 @@ export class CartUpdateHandler
|
|
|
608
636
|
shippingMethodName,
|
|
609
637
|
price: createCentPrecisionMoney(shippingRate.price),
|
|
610
638
|
shippingRate: {
|
|
611
|
-
price:
|
|
639
|
+
price: createCentPrecisionMoney(shippingRate.price),
|
|
612
640
|
tiers: [],
|
|
613
641
|
},
|
|
614
642
|
taxCategory: tax
|
|
@@ -798,7 +826,7 @@ export class CartUpdateHandler
|
|
|
798
826
|
|
|
799
827
|
const lineItemTotal = calculateLineItemTotalPrice(lineItem);
|
|
800
828
|
lineItem.totalPrice = createCentPrecisionMoney({
|
|
801
|
-
|
|
829
|
+
currencyCode: lineItem.price!.value.currencyCode,
|
|
802
830
|
centAmount: lineItemTotal,
|
|
803
831
|
});
|
|
804
832
|
resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
|
|
@@ -2,15 +2,19 @@ import type {
|
|
|
2
2
|
Cart,
|
|
3
3
|
CustomLineItem,
|
|
4
4
|
CustomLineItemDraft,
|
|
5
|
+
DiscountCodeInfo,
|
|
6
|
+
DiscountCodeNonApplicableError,
|
|
5
7
|
LineItem,
|
|
6
8
|
Price,
|
|
7
9
|
TaxCategory,
|
|
8
10
|
TaxCategoryReference,
|
|
9
11
|
} from "@commercetools/platform-sdk";
|
|
10
12
|
import { v4 as uuidv4 } from "uuid";
|
|
13
|
+
import { CommercetoolsError } from "#src/exceptions.ts";
|
|
11
14
|
import { calculateTaxedPrice } from "#src/lib/tax.ts";
|
|
12
15
|
import type { AbstractStorage } from "#src/storage/abstract.ts";
|
|
13
16
|
import {
|
|
17
|
+
calculateMoneyTotalCentAmount,
|
|
14
18
|
createCentPrecisionMoney,
|
|
15
19
|
createCustomFields,
|
|
16
20
|
createTypedMoney,
|
|
@@ -40,8 +44,13 @@ export const selectPrice = ({
|
|
|
40
44
|
});
|
|
41
45
|
};
|
|
42
46
|
|
|
43
|
-
export const calculateLineItemTotalPrice = (lineItem: LineItem): number =>
|
|
44
|
-
lineItem.price?.value
|
|
47
|
+
export const calculateLineItemTotalPrice = (lineItem: LineItem): number => {
|
|
48
|
+
if (!lineItem.price?.value) {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return calculateMoneyTotalCentAmount(lineItem.price.value, lineItem.quantity);
|
|
53
|
+
};
|
|
45
54
|
|
|
46
55
|
export const calculateCartTotalPrice = (cart: Cart): number => {
|
|
47
56
|
const lineItemsTotal = cart.lineItems.reduce(
|
|
@@ -83,8 +92,8 @@ export const createCustomLineItemFromDraft = (
|
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
const totalPrice = createCentPrecisionMoney({
|
|
86
|
-
|
|
87
|
-
centAmount: (draft.money
|
|
95
|
+
currencyCode: draft.money.currencyCode,
|
|
96
|
+
centAmount: calculateMoneyTotalCentAmount(draft.money, quantity),
|
|
88
97
|
});
|
|
89
98
|
|
|
90
99
|
const taxedPrice = taxCategory
|
|
@@ -121,3 +130,29 @@ export const createCustomLineItemFromDraft = (
|
|
|
121
130
|
taxedPricePortions: [],
|
|
122
131
|
};
|
|
123
132
|
};
|
|
133
|
+
|
|
134
|
+
export const createDiscountCodeInfoFromCode = (
|
|
135
|
+
projectKey: string,
|
|
136
|
+
storage: AbstractStorage,
|
|
137
|
+
code: string,
|
|
138
|
+
): DiscountCodeInfo => {
|
|
139
|
+
const discountCodes = storage.query(projectKey, "discount-code", {
|
|
140
|
+
where: `code="${code}"`,
|
|
141
|
+
});
|
|
142
|
+
// Does not validate anything besides existence of the DiscountCode object
|
|
143
|
+
if (discountCodes.count === 0) {
|
|
144
|
+
throw new CommercetoolsError<DiscountCodeNonApplicableError>({
|
|
145
|
+
code: "DiscountCodeNonApplicable",
|
|
146
|
+
message: `The discount code '${code}' was not found.`,
|
|
147
|
+
reason: "DoesNotExist",
|
|
148
|
+
discountCode: "nonexistent",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
discountCode: {
|
|
153
|
+
typeId: "discount-code",
|
|
154
|
+
id: discountCodes.results[0].id,
|
|
155
|
+
},
|
|
156
|
+
state: "MatchesCart",
|
|
157
|
+
};
|
|
158
|
+
};
|
|
@@ -716,4 +716,36 @@ describe("createShippingInfo", () => {
|
|
|
716
716
|
expect(result.taxedPrice!.totalGross.centAmount).toBe(1204);
|
|
717
717
|
expect(result.taxedPrice!.totalNet.centAmount).toBe(995);
|
|
718
718
|
});
|
|
719
|
+
|
|
720
|
+
test("create cart with discount code", async () => {
|
|
721
|
+
const code = storage.add("dummy", "discount-code", {
|
|
722
|
+
...getBaseResourceProperties(),
|
|
723
|
+
code: "test-1234",
|
|
724
|
+
cartDiscounts: [],
|
|
725
|
+
isActive: true,
|
|
726
|
+
references: [],
|
|
727
|
+
groups: [],
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
const cart: CartDraft = {
|
|
731
|
+
country: "NL",
|
|
732
|
+
currency: "EUR",
|
|
733
|
+
discountCodes: ["test-1234"],
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
|
|
737
|
+
|
|
738
|
+
const result = repository.create(ctx, cart);
|
|
739
|
+
expect(result.id).toBeDefined();
|
|
740
|
+
|
|
741
|
+
expect(result.discountCodes).toEqual([
|
|
742
|
+
{
|
|
743
|
+
discountCode: {
|
|
744
|
+
typeId: "discount-code",
|
|
745
|
+
id: code.id,
|
|
746
|
+
},
|
|
747
|
+
state: "MatchesCart",
|
|
748
|
+
},
|
|
749
|
+
]);
|
|
750
|
+
});
|
|
719
751
|
});
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
BusinessUnit,
|
|
3
3
|
Cart,
|
|
4
4
|
CartDraft,
|
|
5
|
+
DiscountCodeInfo,
|
|
5
6
|
GeneralError,
|
|
6
7
|
InvalidOperationError,
|
|
7
8
|
LineItem,
|
|
@@ -24,11 +25,17 @@ import {
|
|
|
24
25
|
AbstractResourceRepository,
|
|
25
26
|
type RepositoryContext,
|
|
26
27
|
} from "../abstract.ts";
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
calculateMoneyTotalCentAmount,
|
|
30
|
+
createAddress,
|
|
31
|
+
createCentPrecisionMoney,
|
|
32
|
+
createCustomFields,
|
|
33
|
+
} from "../helpers.ts";
|
|
28
34
|
import { CartUpdateHandler } from "./actions.ts";
|
|
29
35
|
import {
|
|
30
36
|
calculateCartTotalPrice,
|
|
31
37
|
createCustomLineItemFromDraft,
|
|
38
|
+
createDiscountCodeInfoFromCode,
|
|
32
39
|
selectPrice,
|
|
33
40
|
} from "./helpers.ts";
|
|
34
41
|
|
|
@@ -87,6 +94,20 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
|
|
|
87
94
|
),
|
|
88
95
|
) ?? [];
|
|
89
96
|
|
|
97
|
+
// Validate that discount codes exist
|
|
98
|
+
const discountCodeInfo: DiscountCodeInfo[] = [];
|
|
99
|
+
if (draft.discountCodes?.length) {
|
|
100
|
+
draft.discountCodes.forEach((code) => {
|
|
101
|
+
discountCodeInfo.push(
|
|
102
|
+
createDiscountCodeInfoFromCode(
|
|
103
|
+
context.projectKey,
|
|
104
|
+
this._storage,
|
|
105
|
+
code,
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
90
111
|
const resource: Writable<Cart> = {
|
|
91
112
|
...getBaseResourceProperties(),
|
|
92
113
|
anonymousId: draft.anonymousId,
|
|
@@ -106,7 +127,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
|
|
|
106
127
|
customerEmail: draft.customerEmail,
|
|
107
128
|
customLineItems,
|
|
108
129
|
directDiscounts: [],
|
|
109
|
-
discountCodes:
|
|
130
|
+
discountCodes: discountCodeInfo,
|
|
110
131
|
inventoryMode: "None",
|
|
111
132
|
itemShippingAddresses: [],
|
|
112
133
|
lineItems,
|
|
@@ -238,6 +259,11 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
|
|
|
238
259
|
);
|
|
239
260
|
}
|
|
240
261
|
|
|
262
|
+
const totalPrice = createCentPrecisionMoney({
|
|
263
|
+
currencyCode: price.value.currencyCode,
|
|
264
|
+
centAmount: calculateMoneyTotalCentAmount(price.value, quant),
|
|
265
|
+
});
|
|
266
|
+
|
|
241
267
|
return {
|
|
242
268
|
id: uuidv4(),
|
|
243
269
|
productId: product.id,
|
|
@@ -247,12 +273,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
|
|
|
247
273
|
name: product.masterData.current.name,
|
|
248
274
|
variant,
|
|
249
275
|
price: price,
|
|
250
|
-
totalPrice
|
|
251
|
-
type: "centPrecision",
|
|
252
|
-
currencyCode: price.value.currencyCode,
|
|
253
|
-
fractionDigits: price.value.fractionDigits,
|
|
254
|
-
centAmount: price.value.centAmount * quant,
|
|
255
|
-
},
|
|
276
|
+
totalPrice,
|
|
256
277
|
taxedPricePortions: [],
|
|
257
278
|
perMethodTaxRate: [],
|
|
258
279
|
quantity: quant,
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BaseAddress,
|
|
3
|
+
HighPrecisionMoneyDraft,
|
|
4
|
+
Money,
|
|
5
|
+
} from "@commercetools/platform-sdk";
|
|
2
6
|
import { describe, expect, test } from "vitest";
|
|
3
7
|
import { InMemoryStorage } from "#src/storage/index.ts";
|
|
4
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
calculateCentAmountFromPreciseAmount,
|
|
10
|
+
calculateMoneyTotalCentAmount,
|
|
11
|
+
createAddress,
|
|
12
|
+
createHighPrecisionMoney,
|
|
13
|
+
} from "./helpers.ts";
|
|
5
14
|
|
|
6
15
|
describe("Helpers", () => {
|
|
7
16
|
const storage = new InMemoryStorage();
|
|
@@ -22,4 +31,106 @@ describe("Helpers", () => {
|
|
|
22
31
|
expect(result?.id).toMatch(/^[a-z0-9]{8}$/);
|
|
23
32
|
});
|
|
24
33
|
});
|
|
34
|
+
|
|
35
|
+
describe("calculateCentAmountFromPreciseAmount", () => {
|
|
36
|
+
test("rounds half even by default", () => {
|
|
37
|
+
const centAmount = calculateCentAmountFromPreciseAmount(1005, 3, "EUR");
|
|
38
|
+
expect(centAmount).toBe(100);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("supports HalfUp and HalfDown rounding modes", () => {
|
|
42
|
+
const halfUp = calculateCentAmountFromPreciseAmount(
|
|
43
|
+
1005,
|
|
44
|
+
3,
|
|
45
|
+
"EUR",
|
|
46
|
+
"HalfUp",
|
|
47
|
+
);
|
|
48
|
+
const halfDown = calculateCentAmountFromPreciseAmount(
|
|
49
|
+
1005,
|
|
50
|
+
3,
|
|
51
|
+
"EUR",
|
|
52
|
+
"HalfDown",
|
|
53
|
+
);
|
|
54
|
+
expect(halfUp).toBe(101);
|
|
55
|
+
expect(halfDown).toBe(100);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("uses currency fraction digits for conversion", () => {
|
|
59
|
+
const centAmount = calculateCentAmountFromPreciseAmount(1234, 2, "JPY");
|
|
60
|
+
expect(centAmount).toBe(12);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("createHighPrecisionMoney", () => {
|
|
65
|
+
test("computes centAmount when missing", () => {
|
|
66
|
+
const money = createHighPrecisionMoney({
|
|
67
|
+
type: "highPrecision",
|
|
68
|
+
currencyCode: "EUR",
|
|
69
|
+
fractionDigits: 3,
|
|
70
|
+
preciseAmount: 1015,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(money.type).toBe("highPrecision");
|
|
74
|
+
expect(money.centAmount).toBe(102);
|
|
75
|
+
expect(money.preciseAmount).toBe(1015);
|
|
76
|
+
expect(money.fractionDigits).toBe(3);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("throws when preciseAmount is missing", () => {
|
|
80
|
+
expect(() =>
|
|
81
|
+
createHighPrecisionMoney({
|
|
82
|
+
type: "highPrecision",
|
|
83
|
+
currencyCode: "EUR",
|
|
84
|
+
fractionDigits: 3,
|
|
85
|
+
} as HighPrecisionMoneyDraft),
|
|
86
|
+
).toThrow("HighPrecisionMoney requires preciseAmount");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("throws when fractionDigits is missing", () => {
|
|
90
|
+
expect(() =>
|
|
91
|
+
createHighPrecisionMoney({
|
|
92
|
+
type: "highPrecision",
|
|
93
|
+
currencyCode: "EUR",
|
|
94
|
+
preciseAmount: 1015,
|
|
95
|
+
} as HighPrecisionMoneyDraft),
|
|
96
|
+
).toThrow("HighPrecisionMoney requires fractionDigits");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("calculateMoneyTotalCentAmount", () => {
|
|
101
|
+
test("uses preciseAmount when available", () => {
|
|
102
|
+
const money: HighPrecisionMoneyDraft = {
|
|
103
|
+
type: "highPrecision",
|
|
104
|
+
currencyCode: "EUR",
|
|
105
|
+
fractionDigits: 5,
|
|
106
|
+
preciseAmount: 101499,
|
|
107
|
+
centAmount: 101,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const totalCentAmount = calculateMoneyTotalCentAmount(money, 2);
|
|
111
|
+
expect(totalCentAmount).toBe(203);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("falls back to centAmount when preciseAmount is missing", () => {
|
|
115
|
+
const money: Money = {
|
|
116
|
+
currencyCode: "EUR",
|
|
117
|
+
centAmount: 123,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const totalCentAmount = calculateMoneyTotalCentAmount(money, 3);
|
|
121
|
+
expect(totalCentAmount).toBe(369);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("supports rounding mode overrides", () => {
|
|
125
|
+
const money: HighPrecisionMoneyDraft = {
|
|
126
|
+
type: "highPrecision",
|
|
127
|
+
currencyCode: "EUR",
|
|
128
|
+
fractionDigits: 3,
|
|
129
|
+
preciseAmount: 1005,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const totalCentAmount = calculateMoneyTotalCentAmount(money, 1, "HalfUp");
|
|
133
|
+
expect(totalCentAmount).toBe(101);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
25
136
|
});
|
|
@@ -28,6 +28,7 @@ import type {
|
|
|
28
28
|
StoreReference,
|
|
29
29
|
StoreResourceIdentifier,
|
|
30
30
|
Type,
|
|
31
|
+
TypedMoney,
|
|
31
32
|
} from "@commercetools/platform-sdk";
|
|
32
33
|
import { Decimal } from "decimal.js/decimal";
|
|
33
34
|
import type { Request } from "express";
|
|
@@ -109,10 +110,9 @@ export const roundDecimal = (decimal: Decimal, roundingMode: RoundingMode) => {
|
|
|
109
110
|
}
|
|
110
111
|
};
|
|
111
112
|
|
|
112
|
-
export const
|
|
113
|
+
export const getCurrencyFractionDigits = (currencyCode: string): number => {
|
|
113
114
|
// Taken from https://docs.adyen.com/development-resources/currency-codes
|
|
114
|
-
|
|
115
|
-
switch (value.currencyCode.toUpperCase()) {
|
|
115
|
+
switch (currencyCode.toUpperCase()) {
|
|
116
116
|
case "BHD":
|
|
117
117
|
case "IQD":
|
|
118
118
|
case "JOD":
|
|
@@ -120,8 +120,7 @@ export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => {
|
|
|
120
120
|
case "LYD":
|
|
121
121
|
case "OMR":
|
|
122
122
|
case "TND":
|
|
123
|
-
|
|
124
|
-
break;
|
|
123
|
+
return 3;
|
|
125
124
|
case "CVE":
|
|
126
125
|
case "DJF":
|
|
127
126
|
case "GNF":
|
|
@@ -137,29 +136,120 @@ export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => {
|
|
|
137
136
|
case "XAF":
|
|
138
137
|
case "XOF":
|
|
139
138
|
case "XPF":
|
|
140
|
-
|
|
141
|
-
break;
|
|
139
|
+
return 0;
|
|
142
140
|
default:
|
|
143
|
-
|
|
141
|
+
return 2;
|
|
144
142
|
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const calculateCentAmountFromPreciseAmount = (
|
|
146
|
+
preciseAmount: number,
|
|
147
|
+
fractionDigits: number,
|
|
148
|
+
currencyCode: string,
|
|
149
|
+
roundingMode: RoundingMode = "HalfEven",
|
|
150
|
+
): number => {
|
|
151
|
+
const centFractionDigits = getCurrencyFractionDigits(currencyCode);
|
|
152
|
+
const diff = fractionDigits - centFractionDigits;
|
|
153
|
+
const scale = new Decimal(10).pow(Math.abs(diff));
|
|
154
|
+
const decimal =
|
|
155
|
+
diff >= 0
|
|
156
|
+
? new Decimal(preciseAmount).div(scale)
|
|
157
|
+
: new Decimal(preciseAmount).mul(scale);
|
|
158
|
+
|
|
159
|
+
return roundDecimal(decimal, roundingMode).toNumber();
|
|
160
|
+
};
|
|
145
161
|
|
|
146
|
-
|
|
147
|
-
|
|
162
|
+
export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => {
|
|
163
|
+
const fractionDigits = getCurrencyFractionDigits(value.currencyCode);
|
|
164
|
+
const preciseValue = value as HighPrecisionMoney & HighPrecisionMoneyDraft;
|
|
165
|
+
let centAmount: number;
|
|
166
|
+
|
|
167
|
+
centAmount = value.centAmount ?? 0;
|
|
168
|
+
|
|
169
|
+
if (
|
|
170
|
+
preciseValue.preciseAmount !== undefined &&
|
|
171
|
+
preciseValue.fractionDigits !== undefined
|
|
172
|
+
) {
|
|
173
|
+
centAmount = calculateCentAmountFromPreciseAmount(
|
|
174
|
+
preciseValue.preciseAmount,
|
|
175
|
+
preciseValue.fractionDigits,
|
|
176
|
+
value.currencyCode,
|
|
177
|
+
"HalfEven",
|
|
178
|
+
);
|
|
148
179
|
}
|
|
149
180
|
|
|
150
181
|
return {
|
|
151
182
|
type: "centPrecision",
|
|
152
|
-
|
|
153
|
-
// fallback to 0
|
|
154
|
-
centAmount: value.centAmount ?? 0,
|
|
183
|
+
centAmount,
|
|
155
184
|
currencyCode: value.currencyCode,
|
|
156
|
-
fractionDigits
|
|
185
|
+
fractionDigits,
|
|
157
186
|
};
|
|
158
187
|
};
|
|
159
188
|
|
|
160
|
-
export const
|
|
161
|
-
|
|
162
|
-
|
|
189
|
+
export const createHighPrecisionMoney = (
|
|
190
|
+
value: HighPrecisionMoney | HighPrecisionMoneyDraft,
|
|
191
|
+
): HighPrecisionMoney => {
|
|
192
|
+
if (value.preciseAmount === undefined) {
|
|
193
|
+
throw new Error("HighPrecisionMoney requires preciseAmount");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (value.fractionDigits === undefined) {
|
|
197
|
+
throw new Error("HighPrecisionMoney requires fractionDigits");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const centAmount =
|
|
201
|
+
value.centAmount ??
|
|
202
|
+
calculateCentAmountFromPreciseAmount(
|
|
203
|
+
value.preciseAmount,
|
|
204
|
+
value.fractionDigits,
|
|
205
|
+
value.currencyCode,
|
|
206
|
+
"HalfEven",
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
type: "highPrecision",
|
|
211
|
+
centAmount,
|
|
212
|
+
currencyCode: value.currencyCode,
|
|
213
|
+
fractionDigits: value.fractionDigits,
|
|
214
|
+
preciseAmount: value.preciseAmount,
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const createTypedMoney = (value: _Money): TypedMoney => {
|
|
219
|
+
const preciseValue = value as HighPrecisionMoney & HighPrecisionMoneyDraft;
|
|
220
|
+
if (
|
|
221
|
+
("type" in value && value.type === "highPrecision") ||
|
|
222
|
+
preciseValue.preciseAmount !== undefined
|
|
223
|
+
) {
|
|
224
|
+
return createHighPrecisionMoney(
|
|
225
|
+
value as HighPrecisionMoney | HighPrecisionMoneyDraft,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return createCentPrecisionMoney(value);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export const calculateMoneyTotalCentAmount = (
|
|
233
|
+
money: _Money,
|
|
234
|
+
quantity: number,
|
|
235
|
+
roundingMode: RoundingMode = "HalfEven",
|
|
236
|
+
): number => {
|
|
237
|
+
const preciseValue = money as HighPrecisionMoney & HighPrecisionMoneyDraft;
|
|
238
|
+
|
|
239
|
+
if (
|
|
240
|
+
preciseValue.preciseAmount === undefined ||
|
|
241
|
+
preciseValue.fractionDigits === undefined
|
|
242
|
+
) {
|
|
243
|
+
return (money.centAmount ?? 0) * quantity;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const totalPrecise = new Decimal(preciseValue.preciseAmount).mul(quantity);
|
|
247
|
+
return calculateCentAmountFromPreciseAmount(
|
|
248
|
+
totalPrecise.toNumber(),
|
|
249
|
+
preciseValue.fractionDigits,
|
|
250
|
+
money.currencyCode,
|
|
251
|
+
roundingMode,
|
|
252
|
+
);
|
|
163
253
|
};
|
|
164
254
|
|
|
165
255
|
export const resolveStoreReference = (
|
|
@@ -35,6 +35,7 @@ import type { Writable } from "#src/types.ts";
|
|
|
35
35
|
import type { RepositoryContext } from "../abstract.ts";
|
|
36
36
|
import { AbstractResourceRepository, type QueryParams } from "../abstract.ts";
|
|
37
37
|
import {
|
|
38
|
+
calculateMoneyTotalCentAmount,
|
|
38
39
|
createAddress,
|
|
39
40
|
createCentPrecisionMoney,
|
|
40
41
|
createCustomFields,
|
|
@@ -260,8 +261,8 @@ export class OrderRepository extends AbstractResourceRepository<"order"> {
|
|
|
260
261
|
|
|
261
262
|
const quantity = draft.quantity ?? 1;
|
|
262
263
|
const totalPrice = createCentPrecisionMoney({
|
|
263
|
-
|
|
264
|
-
centAmount: (draft.price.value
|
|
264
|
+
currencyCode: draft.price.value.currencyCode,
|
|
265
|
+
centAmount: calculateMoneyTotalCentAmount(draft.price.value, quantity),
|
|
265
266
|
});
|
|
266
267
|
|
|
267
268
|
const lineItem: LineItem = {
|
|
@@ -306,8 +307,8 @@ export class OrderRepository extends AbstractResourceRepository<"order"> {
|
|
|
306
307
|
): CustomLineItem {
|
|
307
308
|
const quantity = draft.quantity ?? 1;
|
|
308
309
|
const totalPrice = createCentPrecisionMoney({
|
|
309
|
-
|
|
310
|
-
centAmount: (draft.money
|
|
310
|
+
currencyCode: draft.money.currencyCode,
|
|
311
|
+
centAmount: calculateMoneyTotalCentAmount(draft.money, quantity),
|
|
311
312
|
});
|
|
312
313
|
|
|
313
314
|
const lineItem: CustomLineItem = {
|
|
@@ -2,12 +2,12 @@ import type {
|
|
|
2
2
|
ShippingRate,
|
|
3
3
|
ShippingRateDraft,
|
|
4
4
|
} from "@commercetools/platform-sdk";
|
|
5
|
-
import {
|
|
5
|
+
import { createCentPrecisionMoney } from "../helpers.ts";
|
|
6
6
|
|
|
7
7
|
export const transformShippingRate = (
|
|
8
8
|
rate: ShippingRateDraft,
|
|
9
9
|
): ShippingRate => ({
|
|
10
|
-
price:
|
|
11
|
-
freeAbove: rate.freeAbove &&
|
|
10
|
+
price: createCentPrecisionMoney(rate.price),
|
|
11
|
+
freeAbove: rate.freeAbove && createCentPrecisionMoney(rate.freeAbove),
|
|
12
12
|
tiers: rate.tiers || [],
|
|
13
13
|
});
|