@labdigital/commercetools-mock 2.62.0 → 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 +92 -42
- 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 +111 -23
- 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
|
});
|
|
@@ -10,7 +10,6 @@ import type {
|
|
|
10
10
|
AssociateRoleResourceIdentifier,
|
|
11
11
|
BaseAddress,
|
|
12
12
|
BusinessUnitKeyReference,
|
|
13
|
-
BusinessUnitReference,
|
|
14
13
|
BusinessUnitResourceIdentifier,
|
|
15
14
|
CentPrecisionMoney,
|
|
16
15
|
CustomFields,
|
|
@@ -29,6 +28,7 @@ import type {
|
|
|
29
28
|
StoreReference,
|
|
30
29
|
StoreResourceIdentifier,
|
|
31
30
|
Type,
|
|
31
|
+
TypedMoney,
|
|
32
32
|
} from "@commercetools/platform-sdk";
|
|
33
33
|
import { Decimal } from "decimal.js/decimal";
|
|
34
34
|
import type { Request } from "express";
|
|
@@ -110,10 +110,9 @@ export const roundDecimal = (decimal: Decimal, roundingMode: RoundingMode) => {
|
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
-
export const
|
|
113
|
+
export const getCurrencyFractionDigits = (currencyCode: string): number => {
|
|
114
114
|
// Taken from https://docs.adyen.com/development-resources/currency-codes
|
|
115
|
-
|
|
116
|
-
switch (value.currencyCode.toUpperCase()) {
|
|
115
|
+
switch (currencyCode.toUpperCase()) {
|
|
117
116
|
case "BHD":
|
|
118
117
|
case "IQD":
|
|
119
118
|
case "JOD":
|
|
@@ -121,8 +120,7 @@ export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => {
|
|
|
121
120
|
case "LYD":
|
|
122
121
|
case "OMR":
|
|
123
122
|
case "TND":
|
|
124
|
-
|
|
125
|
-
break;
|
|
123
|
+
return 3;
|
|
126
124
|
case "CVE":
|
|
127
125
|
case "DJF":
|
|
128
126
|
case "GNF":
|
|
@@ -138,29 +136,120 @@ export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => {
|
|
|
138
136
|
case "XAF":
|
|
139
137
|
case "XOF":
|
|
140
138
|
case "XPF":
|
|
141
|
-
|
|
142
|
-
break;
|
|
139
|
+
return 0;
|
|
143
140
|
default:
|
|
144
|
-
|
|
141
|
+
return 2;
|
|
145
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
|
+
};
|
|
146
161
|
|
|
147
|
-
|
|
148
|
-
|
|
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
|
+
);
|
|
149
179
|
}
|
|
150
180
|
|
|
151
181
|
return {
|
|
152
182
|
type: "centPrecision",
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
centAmount,
|
|
184
|
+
currencyCode: value.currencyCode,
|
|
185
|
+
fractionDigits,
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
|
|
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,
|
|
156
212
|
currencyCode: value.currencyCode,
|
|
157
|
-
fractionDigits: fractionDigits,
|
|
213
|
+
fractionDigits: value.fractionDigits,
|
|
214
|
+
preciseAmount: value.preciseAmount,
|
|
158
215
|
};
|
|
159
216
|
};
|
|
160
217
|
|
|
161
|
-
export const createTypedMoney = (value: _Money):
|
|
162
|
-
const
|
|
163
|
-
|
|
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
|
+
);
|
|
164
253
|
};
|
|
165
254
|
|
|
166
255
|
export const resolveStoreReference = (
|
|
@@ -323,18 +412,17 @@ export const getBusinessUnitKeyReference = (
|
|
|
323
412
|
};
|
|
324
413
|
}
|
|
325
414
|
|
|
326
|
-
const
|
|
327
|
-
id,
|
|
415
|
+
const resource = storage.getByResourceIdentifier<"business-unit">(
|
|
328
416
|
projectKey,
|
|
329
|
-
|
|
417
|
+
id,
|
|
330
418
|
);
|
|
331
419
|
|
|
332
|
-
if (!
|
|
420
|
+
if (!resource?.key) {
|
|
333
421
|
throw new Error("No business-unit found for reference");
|
|
334
422
|
}
|
|
335
423
|
|
|
336
424
|
return {
|
|
337
425
|
typeId: "business-unit",
|
|
338
|
-
key:
|
|
426
|
+
key: resource.key,
|
|
339
427
|
};
|
|
340
428
|
};
|
|
@@ -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
|
});
|