@labdigital/commercetools-mock 2.66.0 → 3.0.0-beta.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.
Files changed (281) hide show
  1. package/README.md +31 -8
  2. package/dist/abstract-BKFcva6S.mjs +1044 -0
  3. package/dist/abstract-BKFcva6S.mjs.map +1 -0
  4. package/dist/config-BcNSzPZz.d.mts +1718 -0
  5. package/dist/index.d.mts +50 -1633
  6. package/dist/index.mjs +3769 -2653
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/storage/sqlite.d.mts +59 -0
  9. package/dist/storage/sqlite.mjs +234 -0
  10. package/dist/storage/sqlite.mjs.map +1 -0
  11. package/package.json +26 -29
  12. package/src/ctMock.ts +125 -136
  13. package/src/helpers.ts +14 -6
  14. package/src/index.ts +5 -0
  15. package/src/lib/masking.ts +4 -5
  16. package/src/lib/product-review-statistics.test.ts +257 -294
  17. package/src/lib/review-statistics.ts +17 -4
  18. package/src/oauth/helpers.ts +7 -4
  19. package/src/oauth/server.test.ts +102 -62
  20. package/src/oauth/server.ts +215 -213
  21. package/src/oauth/store.ts +20 -6
  22. package/src/orderSearch.ts +3 -3
  23. package/src/product-projection-search.ts +38 -20
  24. package/src/product-search-availability.test.ts +31 -52
  25. package/src/product-search.ts +19 -10
  26. package/src/projectAPI.ts +6 -22
  27. package/src/repositories/abstract.ts +182 -48
  28. package/src/repositories/as-associate.test.ts +19 -19
  29. package/src/repositories/associate-role.ts +12 -23
  30. package/src/repositories/attribute-group.test.ts +23 -23
  31. package/src/repositories/attribute-group.ts +6 -4
  32. package/src/repositories/business-unit.test.ts +63 -57
  33. package/src/repositories/business-unit.ts +107 -55
  34. package/src/repositories/cart/actions.ts +96 -65
  35. package/src/repositories/cart/helpers.ts +15 -11
  36. package/src/repositories/cart/index.test.ts +136 -30
  37. package/src/repositories/cart/index.ts +76 -59
  38. package/src/repositories/cart-discount/actions.ts +12 -44
  39. package/src/repositories/cart-discount/index.ts +20 -8
  40. package/src/repositories/category/actions.ts +27 -27
  41. package/src/repositories/category/index.test.ts +13 -9
  42. package/src/repositories/category/index.ts +40 -23
  43. package/src/repositories/channel.test.ts +53 -51
  44. package/src/repositories/channel.ts +12 -22
  45. package/src/repositories/custom-object.ts +34 -25
  46. package/src/repositories/customer/actions.ts +47 -25
  47. package/src/repositories/customer/index.test.ts +11 -11
  48. package/src/repositories/customer/index.ts +65 -35
  49. package/src/repositories/customer-group.test.ts +44 -42
  50. package/src/repositories/customer-group.ts +12 -22
  51. package/src/repositories/discount-code/actions.ts +3 -19
  52. package/src/repositories/discount-code/index.ts +9 -4
  53. package/src/repositories/discount-group/index.ts +8 -3
  54. package/src/repositories/extension.test.ts +27 -27
  55. package/src/repositories/extension.ts +10 -5
  56. package/src/repositories/helpers.ts +126 -47
  57. package/src/repositories/inventory-entry/actions.ts +3 -24
  58. package/src/repositories/inventory-entry/index.ts +19 -11
  59. package/src/repositories/my-customer.ts +13 -12
  60. package/src/repositories/my-order.ts +5 -2
  61. package/src/repositories/order/actions.ts +84 -56
  62. package/src/repositories/order/index.test.ts +36 -31
  63. package/src/repositories/order/index.ts +83 -49
  64. package/src/repositories/order-edit.ts +8 -3
  65. package/src/repositories/payment/actions.ts +64 -44
  66. package/src/repositories/payment/helpers.ts +3 -3
  67. package/src/repositories/payment/index.ts +28 -12
  68. package/src/repositories/product/actions.ts +133 -98
  69. package/src/repositories/product/helpers.ts +29 -16
  70. package/src/repositories/product/index.ts +42 -25
  71. package/src/repositories/product-discount.ts +6 -4
  72. package/src/repositories/product-projection.ts +41 -21
  73. package/src/repositories/product-selection.ts +8 -15
  74. package/src/repositories/product-tailoring.ts +22 -3
  75. package/src/repositories/product-type.ts +45 -4
  76. package/src/repositories/project.ts +57 -13
  77. package/src/repositories/quote/actions.ts +5 -28
  78. package/src/repositories/quote/index.ts +29 -6
  79. package/src/repositories/quote-request/actions.ts +5 -28
  80. package/src/repositories/quote-request/index.test.ts +3 -3
  81. package/src/repositories/quote-request/index.ts +31 -11
  82. package/src/repositories/quote-staged/actions.ts +5 -28
  83. package/src/repositories/quote-staged/index.ts +22 -8
  84. package/src/repositories/recurrence-policy/index.ts +6 -4
  85. package/src/repositories/recurring-order/actions.ts +7 -32
  86. package/src/repositories/recurring-order/index.ts +8 -6
  87. package/src/repositories/review.test.ts +147 -142
  88. package/src/repositories/review.ts +31 -37
  89. package/src/repositories/shipping-method/actions.ts +11 -28
  90. package/src/repositories/shipping-method/index.ts +26 -15
  91. package/src/repositories/shopping-list/actions.ts +21 -31
  92. package/src/repositories/shopping-list/index.ts +44 -22
  93. package/src/repositories/standalone-price.ts +6 -4
  94. package/src/repositories/state.ts +15 -9
  95. package/src/repositories/store.ts +21 -32
  96. package/src/repositories/subscription.test.ts +22 -22
  97. package/src/repositories/subscription.ts +8 -3
  98. package/src/repositories/tax-category/index.ts +8 -3
  99. package/src/repositories/type/actions.ts +21 -3
  100. package/src/repositories/type/index.ts +5 -3
  101. package/src/repositories/zone.test.ts +112 -77
  102. package/src/repositories/zone.ts +5 -3
  103. package/src/schemas/generated/associate-role.ts +13 -0
  104. package/src/schemas/generated/attribute-group.ts +12 -0
  105. package/src/schemas/generated/business-unit.ts +38 -0
  106. package/src/schemas/generated/cart-discount.ts +33 -0
  107. package/src/schemas/generated/cart.ts +61 -0
  108. package/src/schemas/generated/category.ts +25 -0
  109. package/src/schemas/generated/channel.ts +21 -0
  110. package/src/schemas/generated/common.ts +1372 -0
  111. package/src/schemas/generated/custom-object.ts +11 -0
  112. package/src/schemas/generated/customer-group.ts +11 -0
  113. package/src/schemas/generated/customer.ts +47 -0
  114. package/src/schemas/generated/discount-code.ts +25 -0
  115. package/src/schemas/generated/discount-group.ts +13 -0
  116. package/src/schemas/generated/extension.ts +15 -0
  117. package/src/schemas/generated/index.ts +42 -0
  118. package/src/schemas/generated/inventory-entry.ts +20 -0
  119. package/src/schemas/generated/my-quote-request.ts +10 -0
  120. package/src/schemas/generated/order-edit.ts +18 -0
  121. package/src/schemas/generated/order-from-cart.ts +25 -0
  122. package/src/schemas/generated/payment.ts +30 -0
  123. package/src/schemas/generated/product-discount.ts +20 -0
  124. package/src/schemas/generated/product-selection.ts +18 -0
  125. package/src/schemas/generated/product-tailoring.ts +26 -0
  126. package/src/schemas/generated/product-type.ts +12 -0
  127. package/src/schemas/generated/product.ts +37 -0
  128. package/src/schemas/generated/quote-request.ts +19 -0
  129. package/src/schemas/generated/quote.ts +18 -0
  130. package/src/schemas/generated/recurrence-policy.ts +15 -0
  131. package/src/schemas/generated/recurring-order.ts +19 -0
  132. package/src/schemas/generated/review.ts +24 -0
  133. package/src/schemas/generated/shipping-method.ts +24 -0
  134. package/src/schemas/generated/shopping-list.ts +28 -0
  135. package/src/schemas/generated/staged-quote.ts +18 -0
  136. package/src/schemas/generated/standalone-price.ts +32 -0
  137. package/src/schemas/generated/state.ts +20 -0
  138. package/src/schemas/generated/store.ts +23 -0
  139. package/src/schemas/generated/subscription.ts +20 -0
  140. package/src/schemas/generated/tax-category.ts +12 -0
  141. package/src/schemas/generated/type.ts +17 -0
  142. package/src/schemas/generated/zone.ts +12 -0
  143. package/src/schemas/update-request.ts +3 -5
  144. package/src/server.ts +32 -4
  145. package/src/services/abstract.ts +207 -101
  146. package/src/services/as-associate-cart.test.ts +28 -36
  147. package/src/services/as-associate-cart.ts +15 -12
  148. package/src/services/as-associate-order.test.ts +33 -40
  149. package/src/services/as-associate-order.ts +15 -12
  150. package/src/services/as-associate-quote-request.ts +15 -12
  151. package/src/services/as-associate-shopping-list.test.ts +25 -35
  152. package/src/services/as-associate-shopping-list.ts +15 -12
  153. package/src/services/as-associate.test.ts +21 -15
  154. package/src/services/as-associate.ts +23 -22
  155. package/src/services/associate-roles.test.ts +16 -22
  156. package/src/services/associate-roles.ts +2 -2
  157. package/src/services/attribute-group.test.ts +40 -44
  158. package/src/services/attribute-group.ts +2 -2
  159. package/src/services/business-units.test.ts +227 -163
  160. package/src/services/business-units.ts +2 -2
  161. package/src/services/cart-discount.test.ts +253 -187
  162. package/src/services/cart-discount.ts +2 -2
  163. package/src/services/cart.test.ts +833 -832
  164. package/src/services/cart.ts +31 -12
  165. package/src/services/category.test.ts +208 -130
  166. package/src/services/category.ts +2 -2
  167. package/src/services/channel.test.ts +39 -44
  168. package/src/services/channel.ts +2 -2
  169. package/src/services/custom-object.test.ts +103 -79
  170. package/src/services/custom-object.ts +106 -38
  171. package/src/services/customer-group.test.ts +39 -44
  172. package/src/services/customer-group.ts +2 -2
  173. package/src/services/customer.test.ts +357 -292
  174. package/src/services/customer.ts +70 -23
  175. package/src/services/discount-code.test.ts +57 -68
  176. package/src/services/discount-code.ts +2 -2
  177. package/src/services/discount-group.test.ts +111 -134
  178. package/src/services/discount-group.ts +2 -2
  179. package/src/services/draft-validation.test.ts +255 -0
  180. package/src/services/extension.test.ts +39 -44
  181. package/src/services/extension.ts +2 -2
  182. package/src/services/inventory-entry.test.ts +106 -87
  183. package/src/services/inventory-entry.ts +2 -2
  184. package/src/services/my-business-unit.test.ts +82 -112
  185. package/src/services/my-business-unit.ts +25 -19
  186. package/src/services/my-cart.test.ts +46 -41
  187. package/src/services/my-cart.ts +32 -28
  188. package/src/services/my-customer.test.ts +153 -88
  189. package/src/services/my-customer.ts +130 -61
  190. package/src/services/my-order.ts +15 -12
  191. package/src/services/my-payment.test.ts +30 -24
  192. package/src/services/my-payment.ts +2 -2
  193. package/src/services/my-shopping-list.ts +2 -2
  194. package/src/services/order.test.ts +332 -276
  195. package/src/services/order.ts +45 -27
  196. package/src/services/payment.test.ts +31 -29
  197. package/src/services/payment.ts +2 -2
  198. package/src/services/product-discount.test.ts +39 -46
  199. package/src/services/product-discount.ts +2 -2
  200. package/src/services/product-projection.test.ts +176 -166
  201. package/src/services/product-projection.ts +31 -15
  202. package/src/services/product-selection.test.ts +17 -9
  203. package/src/services/product-selection.ts +2 -2
  204. package/src/services/product-type.test.ts +80 -21
  205. package/src/services/product-type.ts +2 -2
  206. package/src/services/product.test.ts +569 -534
  207. package/src/services/product.ts +14 -7
  208. package/src/services/project.test.ts +22 -12
  209. package/src/services/project.ts +28 -13
  210. package/src/services/quote-request.test.ts +36 -39
  211. package/src/services/quote-request.ts +2 -2
  212. package/src/services/quote-staged.ts +2 -2
  213. package/src/services/quote.ts +2 -2
  214. package/src/services/recurrence-policy.test.ts +114 -139
  215. package/src/services/recurrence-policy.ts +2 -2
  216. package/src/services/recurring-order.test.ts +149 -194
  217. package/src/services/recurring-order.ts +2 -2
  218. package/src/services/reviews.test.ts +127 -106
  219. package/src/services/reviews.ts +2 -2
  220. package/src/services/shipping-method.test.ts +96 -125
  221. package/src/services/shipping-method.ts +24 -12
  222. package/src/services/shopping-list.test.ts +183 -141
  223. package/src/services/shopping-list.ts +2 -2
  224. package/src/services/standalone-price.test.ts +60 -46
  225. package/src/services/standalone-price.ts +2 -2
  226. package/src/services/state.test.ts +20 -25
  227. package/src/services/state.ts +2 -2
  228. package/src/services/store.test.ts +26 -45
  229. package/src/services/store.ts +2 -2
  230. package/src/services/subscription.test.ts +39 -44
  231. package/src/services/subscription.ts +2 -2
  232. package/src/services/tax-category.test.ts +33 -36
  233. package/src/services/tax-category.ts +2 -2
  234. package/src/services/type.test.ts +45 -44
  235. package/src/services/type.ts +2 -2
  236. package/src/services/zone.test.ts +40 -44
  237. package/src/services/zone.ts +2 -2
  238. package/src/shipping.ts +41 -11
  239. package/src/storage/abstract.ts +248 -17
  240. package/src/storage/in-memory.ts +147 -290
  241. package/src/storage/sqlite.ts +429 -0
  242. package/src/storage/storage-map.ts +75 -0
  243. package/src/storage/storage.test-helpers.ts +97 -0
  244. package/src/storage/storage.test.ts +802 -0
  245. package/src/testing/associate-role.ts +28 -0
  246. package/src/testing/attribute-group.ts +27 -0
  247. package/src/testing/business-unit.ts +9 -8
  248. package/src/testing/cart-discount.ts +34 -0
  249. package/src/testing/cart.ts +20 -0
  250. package/src/testing/category.ts +25 -0
  251. package/src/testing/channel.ts +23 -0
  252. package/src/testing/custom-object.ts +27 -0
  253. package/src/testing/customer-group.ts +26 -0
  254. package/src/testing/customer.ts +36 -33
  255. package/src/testing/discount-code.ts +29 -0
  256. package/src/testing/discount-group.ts +27 -0
  257. package/src/testing/extension.ts +32 -0
  258. package/src/testing/index.ts +33 -0
  259. package/src/testing/inventory-entry.ts +26 -0
  260. package/src/testing/order.ts +27 -0
  261. package/src/testing/payment.ts +23 -0
  262. package/src/testing/product-discount.ts +33 -0
  263. package/src/testing/product-selection.ts +28 -0
  264. package/src/testing/product-type.ts +27 -0
  265. package/src/testing/product.ts +38 -0
  266. package/src/testing/quote-request.ts +29 -0
  267. package/src/testing/recurrence-policy.ts +33 -0
  268. package/src/testing/recurring-order.ts +32 -0
  269. package/src/testing/review.ts +24 -0
  270. package/src/testing/shipping-method.ts +31 -0
  271. package/src/testing/shopping-list.ts +25 -0
  272. package/src/testing/standalone-price.ts +31 -0
  273. package/src/testing/state.ts +21 -0
  274. package/src/testing/store.ts +26 -0
  275. package/src/testing/subscription.ts +38 -0
  276. package/src/testing/tax-category.ts +27 -0
  277. package/src/testing/type.ts +9 -6
  278. package/src/testing/zone.ts +22 -0
  279. package/src/validate.test.ts +122 -0
  280. package/src/validate.ts +78 -7
  281. package/src/.env +0 -0
@@ -1,4 +1,5 @@
1
1
  import type { CartDraft, LineItem } from "@commercetools/platform-sdk";
2
+ import type { CartAddDiscountCodeAction } from "@commercetools/platform-sdk/dist/declarations/src/generated/models/cart";
2
3
  import { beforeEach, describe, expect, test } from "vitest";
3
4
  import type { Config } from "#src/config.ts";
4
5
  import { getBaseResourceProperties } from "#src/helpers.ts";
@@ -20,7 +21,7 @@ describe("Cart repository", () => {
20
21
  });
21
22
 
22
23
  test("create cart in store", async () => {
23
- storage.add("dummy", "product", {
24
+ await storage.add("dummy", "product", {
24
25
  createdAt: "",
25
26
  lastModifiedAt: "",
26
27
  productType: {
@@ -83,7 +84,7 @@ describe("Cart repository", () => {
83
84
  },
84
85
  });
85
86
 
86
- storage.add("dummy", "tax-category", {
87
+ await storage.add("dummy", "tax-category", {
87
88
  ...getBaseResourceProperties(),
88
89
  id: "tax-category-id",
89
90
  key: "standard-tax",
@@ -99,7 +100,7 @@ describe("Cart repository", () => {
99
100
  ],
100
101
  });
101
102
 
102
- storage.add("dummy", "zone", {
103
+ await storage.add("dummy", "zone", {
103
104
  ...getBaseResourceProperties(),
104
105
  id: "nl-zone-id",
105
106
  key: "nl-zone",
@@ -111,7 +112,7 @@ describe("Cart repository", () => {
111
112
  ],
112
113
  });
113
114
 
114
- storage.add("dummy", "shipping-method", {
115
+ await storage.add("dummy", "shipping-method", {
115
116
  ...getBaseResourceProperties(),
116
117
  id: "shipping-method-id",
117
118
  key: "standard-shipping",
@@ -239,10 +240,10 @@ describe("Cart repository", () => {
239
240
 
240
241
  const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
241
242
 
242
- const result = repository.create(ctx, cart);
243
+ const result = await repository.create(ctx, cart);
243
244
  expect(result.id).toBeDefined();
244
245
 
245
- const items = repository.query(ctx);
246
+ const items = await repository.query(ctx);
246
247
  expect(items.count).toBe(1);
247
248
 
248
249
  expect(result.anonymousId).toEqual(cart.anonymousId);
@@ -281,18 +282,18 @@ describe("Cart repository", () => {
281
282
  expect(result.shippingInfo?.taxRate?.name).toBe("Standard VAT");
282
283
  });
283
284
 
284
- test("create start with store from draft", () => {
285
+ test("create start with store from draft", async () => {
285
286
  const ctx = { projectKey: "dummy" };
286
287
  const draft: CartDraft = {
287
288
  currency: "USD",
288
289
  store: { key: "draftStore", typeId: "store" },
289
290
  };
290
- const result = repository.create(ctx, draft);
291
+ const result = await repository.create(ctx, draft);
291
292
  expect(result.store).toEqual({ typeId: "store", key: "draftStore" });
292
293
  });
293
294
 
294
295
  test("create cart with business unit", async () => {
295
- storage.add("dummy", "business-unit", {
296
+ await storage.add("dummy", "business-unit", {
296
297
  ...getBaseResourceProperties(),
297
298
  unitType: "Company",
298
299
  key: "business-unit-key",
@@ -307,6 +308,8 @@ describe("Cart repository", () => {
307
308
  key: "business-unit-key",
308
309
  },
309
310
  approvalRuleMode: "Explicit",
311
+ shippingAddressIds: [],
312
+ billingAddressIds: [],
310
313
  });
311
314
 
312
315
  const cart: CartDraft = {
@@ -320,7 +323,7 @@ describe("Cart repository", () => {
320
323
 
321
324
  const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
322
325
 
323
- const result = repository.create(ctx, cart);
326
+ const result = await repository.create(ctx, cart);
324
327
  expect(result.id).toBeDefined();
325
328
 
326
329
  expect(result.businessUnit).toEqual({
@@ -329,8 +332,8 @@ describe("Cart repository", () => {
329
332
  });
330
333
  });
331
334
 
332
- test("should calculate taxed price for custom line items with tax category", () => {
333
- storage.add("dummy", "tax-category", {
335
+ test("should calculate taxed price for custom line items with tax category", async () => {
336
+ await storage.add("dummy", "tax-category", {
334
337
  ...getBaseResourceProperties(),
335
338
  id: "tax-category-with-rate",
336
339
  key: "vat-tax",
@@ -367,7 +370,7 @@ describe("Cart repository", () => {
367
370
  };
368
371
 
369
372
  const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
370
- const result = repository.create(ctx, cart);
373
+ const result = await repository.create(ctx, cart);
371
374
 
372
375
  expect(result.customLineItems).toHaveLength(1);
373
376
  const customLineItem = result.customLineItems[0];
@@ -385,8 +388,8 @@ describe("Cart repository", () => {
385
388
  expect(customLineItem.taxRate?.country).toBe("NL");
386
389
  });
387
390
 
388
- test("should calculate taxed price for the cart", () => {
389
- storage.add("dummy", "tax-category", {
391
+ test("should calculate taxed price for the cart", async () => {
392
+ await storage.add("dummy", "tax-category", {
390
393
  ...getBaseResourceProperties(),
391
394
  id: "cart-tax-category",
392
395
  key: "cart-vat-tax",
@@ -423,7 +426,7 @@ describe("Cart repository", () => {
423
426
  };
424
427
 
425
428
  const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
426
- const result = repository.create(ctx, cart);
429
+ const result = await repository.create(ctx, cart);
427
430
 
428
431
  expect(result.taxedPrice).toBeDefined();
429
432
  expect(result.taxedPrice?.totalNet.centAmount).toBe(1000);
@@ -439,8 +442,8 @@ describe("createShippingInfo", () => {
439
442
  const config: Config = { storage, strict: false };
440
443
  const repository = new CartRepository(config);
441
444
 
442
- beforeEach(() => {
443
- storage.add("dummy", "tax-category", {
445
+ beforeEach(async () => {
446
+ await storage.add("dummy", "tax-category", {
444
447
  ...getBaseResourceProperties(),
445
448
  id: "shipping-tax-category-id",
446
449
  key: "shipping-tax",
@@ -456,7 +459,7 @@ describe("createShippingInfo", () => {
456
459
  ],
457
460
  });
458
461
 
459
- storage.add("dummy", "zone", {
462
+ await storage.add("dummy", "zone", {
460
463
  ...getBaseResourceProperties(),
461
464
  id: "test-zone-id",
462
465
  name: "Test Zone",
@@ -468,8 +471,8 @@ describe("createShippingInfo", () => {
468
471
  });
469
472
  });
470
473
 
471
- test("should calculate shipping info", () => {
472
- storage.add("dummy", "shipping-method", {
474
+ test("should calculate shipping info", async () => {
475
+ await storage.add("dummy", "shipping-method", {
473
476
  ...getBaseResourceProperties(),
474
477
  id: "basic-shipping-id",
475
478
  name: "Standard Shipping",
@@ -533,7 +536,7 @@ describe("createShippingInfo", () => {
533
536
  id: "basic-shipping-id",
534
537
  };
535
538
 
536
- const result = repository.createShippingInfo(
539
+ const result = await repository.createShippingInfo(
537
540
  context,
538
541
  cart,
539
542
  shippingMethodRef,
@@ -547,8 +550,8 @@ describe("createShippingInfo", () => {
547
550
  expect(result.taxedPrice!.totalGross.centAmount).toBe(720);
548
551
  });
549
552
 
550
- test("should apply free shipping when cart total is above freeAbove threshold", () => {
551
- storage.add("dummy", "shipping-method", {
553
+ test("should apply free shipping when cart total is above freeAbove threshold", async () => {
554
+ await storage.add("dummy", "shipping-method", {
552
555
  ...getBaseResourceProperties(),
553
556
  id: "free-above-shipping-id",
554
557
  key: "free-above-shipping",
@@ -620,7 +623,7 @@ describe("createShippingInfo", () => {
620
623
  id: "free-above-shipping-id",
621
624
  };
622
625
 
623
- const result = repository.createShippingInfo(
626
+ const result = await repository.createShippingInfo(
624
627
  context,
625
628
  cart,
626
629
  shippingMethodRef,
@@ -632,8 +635,8 @@ describe("createShippingInfo", () => {
632
635
  expect(result.taxedPrice!.totalNet.centAmount).toBe(0);
633
636
  });
634
637
 
635
- test("should charge normal shipping when cart total is below freeAbove threshold", () => {
636
- storage.add("dummy", "shipping-method", {
638
+ test("should charge normal shipping when cart total is below freeAbove threshold", async () => {
639
+ await storage.add("dummy", "shipping-method", {
637
640
  ...getBaseResourceProperties(),
638
641
  id: "free-above-shipping-id-2",
639
642
  key: "free-above-shipping-2",
@@ -705,7 +708,7 @@ describe("createShippingInfo", () => {
705
708
  id: "free-above-shipping-id-2",
706
709
  };
707
710
 
708
- const result = repository.createShippingInfo(
711
+ const result = await repository.createShippingInfo(
709
712
  context,
710
713
  cart,
711
714
  shippingMethodRef,
@@ -718,7 +721,7 @@ describe("createShippingInfo", () => {
718
721
  });
719
722
 
720
723
  test("create cart with discount code", async () => {
721
- const code = storage.add("dummy", "discount-code", {
724
+ const code = await storage.add("dummy", "discount-code", {
722
725
  ...getBaseResourceProperties(),
723
726
  code: "test-1234",
724
727
  cartDiscounts: [],
@@ -735,7 +738,7 @@ describe("createShippingInfo", () => {
735
738
 
736
739
  const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
737
740
 
738
- const result = repository.create(ctx, cart);
741
+ const result = await repository.create(ctx, cart);
739
742
  expect(result.id).toBeDefined();
740
743
 
741
744
  expect(result.discountCodes).toEqual([
@@ -748,4 +751,107 @@ describe("createShippingInfo", () => {
748
751
  },
749
752
  ]);
750
753
  });
754
+
755
+ test("create cart with non-existent discount code throws error", async () => {
756
+ const cart: CartDraft = {
757
+ country: "NL",
758
+ currency: "EUR",
759
+ discountCodes: ["non-existent-code"],
760
+ };
761
+
762
+ const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
763
+
764
+ await expect(repository.create(ctx, cart)).rejects.toThrow(
765
+ "The discount code 'non-existent-code' was not found.",
766
+ );
767
+ });
768
+
769
+ test("create cart with duplicate discount codes deduplicates them", async () => {
770
+ const code = await storage.add("dummy", "discount-code", {
771
+ ...getBaseResourceProperties(),
772
+ code: "duplicate-test",
773
+ cartDiscounts: [],
774
+ isActive: true,
775
+ references: [],
776
+ groups: [],
777
+ });
778
+
779
+ const cart: CartDraft = {
780
+ country: "NL",
781
+ currency: "EUR",
782
+ discountCodes: ["duplicate-test", "duplicate-test"],
783
+ };
784
+
785
+ const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
786
+
787
+ const result = await repository.create(ctx, cart);
788
+ expect(result.discountCodes).toHaveLength(1);
789
+ expect(result.discountCodes).toEqual([
790
+ {
791
+ discountCode: {
792
+ typeId: "discount-code",
793
+ id: code.id,
794
+ },
795
+ state: "MatchesCart",
796
+ },
797
+ ]);
798
+ });
799
+
800
+ test("addDiscountCode action adds discount code to cart", async () => {
801
+ const code = await storage.add("dummy", "discount-code", {
802
+ ...getBaseResourceProperties(),
803
+ code: "action-test-code",
804
+ cartDiscounts: [],
805
+ isActive: true,
806
+ references: [],
807
+ groups: [],
808
+ });
809
+
810
+ const cart: CartDraft = {
811
+ country: "NL",
812
+ currency: "EUR",
813
+ };
814
+
815
+ const ctx = { projectKey: "dummy", storeKey: "dummyStore" };
816
+
817
+ const createdCart = await repository.create(ctx, cart);
818
+ expect(createdCart.discountCodes).toHaveLength(0);
819
+
820
+ const updatedCart = await repository.processUpdateActions(
821
+ ctx,
822
+ createdCart,
823
+ createdCart.version,
824
+ [
825
+ {
826
+ action: "addDiscountCode",
827
+ code: "action-test-code",
828
+ } as CartAddDiscountCodeAction,
829
+ ],
830
+ );
831
+
832
+ expect(updatedCart.discountCodes).toEqual([
833
+ {
834
+ discountCode: {
835
+ typeId: "discount-code",
836
+ id: code.id,
837
+ },
838
+ state: "MatchesCart",
839
+ },
840
+ ]);
841
+
842
+ // Adding the same discount code again should not create a duplicate
843
+ const updatedCart2 = await repository.processUpdateActions(
844
+ ctx,
845
+ updatedCart,
846
+ updatedCart.version,
847
+ [
848
+ {
849
+ action: "addDiscountCode",
850
+ code: "action-test-code",
851
+ } as CartAddDiscountCodeAction,
852
+ ],
853
+ );
854
+
855
+ expect(updatedCart2.discountCodes).toHaveLength(1);
856
+ });
751
857
  });
@@ -8,14 +8,15 @@ import type {
8
8
  LineItem,
9
9
  LineItemDraft,
10
10
  Product,
11
- ProductPagedQueryResponse,
12
11
  ShippingMethodDoesNotMatchCartError,
13
12
  } from "@commercetools/platform-sdk";
13
+
14
14
  import { v4 as uuidv4 } from "uuid";
15
15
  import type { Config } from "#src/config.ts";
16
16
  import { CommercetoolsError } from "#src/exceptions.ts";
17
17
  import { getBaseResourceProperties } from "#src/helpers.ts";
18
18
  import { calculateTaxTotals } from "#src/lib/tax.ts";
19
+ import { CartDraftSchema } from "#src/schemas/generated/cart.ts";
19
20
  import {
20
21
  createShippingInfoFromMethod,
21
22
  getShippingMethodsMatchingCart,
@@ -43,9 +44,10 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
43
44
  constructor(config: Config) {
44
45
  super("cart", config);
45
46
  this.actions = new CartUpdateHandler(this._storage, this);
47
+ this.draftSchema = CartDraftSchema;
46
48
  }
47
49
 
48
- create(context: RepositoryContext, draft: CartDraft): Cart {
50
+ async create(context: RepositoryContext, draft: CartDraft): Promise<Cart> {
49
51
  if (draft.anonymousId && draft.customerId) {
50
52
  throw new CommercetoolsError<InvalidOperationError>({
51
53
  code: "InvalidOperation",
@@ -55,7 +57,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
55
57
 
56
58
  // Validate that the customer exists
57
59
  if (draft.customerId) {
58
- this._storage.getByResourceIdentifier(context.projectKey, {
60
+ await this._storage.getByResourceIdentifier(context.projectKey, {
59
61
  typeId: "customer",
60
62
  id: draft.customerId,
61
63
  });
@@ -64,7 +66,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
64
66
  let storedBusinessUnit: BusinessUnit | undefined;
65
67
  if (draft.businessUnit?.id || draft.businessUnit?.key) {
66
68
  storedBusinessUnit =
67
- this._storage.getByResourceIdentifier<"business-unit">(
69
+ await this._storage.getByResourceIdentifier<"business-unit">(
68
70
  context.projectKey,
69
71
  {
70
72
  typeId: "business-unit",
@@ -74,42 +76,51 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
74
76
  );
75
77
  }
76
78
 
77
- const lineItems =
78
- draft.lineItems?.map((draftLineItem) =>
79
- this.draftLineItemtoLineItem(
80
- context.projectKey,
81
- draftLineItem,
82
- draft.currency,
83
- draft.country,
84
- ),
85
- ) ?? [];
79
+ const lineItems = draft.lineItems
80
+ ? await Promise.all(
81
+ draft.lineItems.map((draftLineItem) =>
82
+ this.draftLineItemtoLineItem(
83
+ context.projectKey,
84
+ draftLineItem,
85
+ draft.currency,
86
+ draft.country,
87
+ ),
88
+ ),
89
+ )
90
+ : [];
86
91
 
87
- const customLineItems =
88
- draft.customLineItems?.map((draftCustomLineItem) =>
89
- createCustomLineItemFromDraft(
90
- context.projectKey,
91
- draftCustomLineItem,
92
- this._storage,
93
- draft.shippingAddress?.country ?? draft.country,
94
- ),
95
- ) ?? [];
92
+ const customLineItems = draft.customLineItems
93
+ ? await Promise.all(
94
+ draft.customLineItems.map((draftCustomLineItem) =>
95
+ createCustomLineItemFromDraft(
96
+ context.projectKey,
97
+ draftCustomLineItem,
98
+ this._storage,
99
+ draft.shippingAddress?.country ?? draft.country,
100
+ ),
101
+ ),
102
+ )
103
+ : [];
96
104
 
97
- // Validate that discount codes exist
105
+ // Validate that discount codes exist and deduplicate
98
106
  const discountCodeInfo: DiscountCodeInfo[] = [];
99
107
  if (draft.discountCodes?.length) {
100
- draft.discountCodes.forEach((code) => {
101
- discountCodeInfo.push(
102
- createDiscountCodeInfoFromCode(
103
- context.projectKey,
104
- this._storage,
105
- code,
106
- ),
108
+ const seen = new Set<string>();
109
+ for (const code of draft.discountCodes) {
110
+ const info = await createDiscountCodeInfoFromCode(
111
+ context.projectKey,
112
+ this._storage,
113
+ code,
107
114
  );
108
- });
115
+ if (!seen.has(info.discountCode.id)) {
116
+ seen.add(info.discountCode.id);
117
+ discountCodeInfo.push(info);
118
+ }
119
+ }
109
120
  }
110
121
 
111
122
  const resource: Writable<Cart> = {
112
- ...getBaseResourceProperties(),
123
+ ...getBaseResourceProperties(context.clientId),
113
124
  anonymousId: draft.anonymousId,
114
125
  businessUnit:
115
126
  storedBusinessUnit && draft.businessUnit
@@ -154,7 +165,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
154
165
  shippingInfo: undefined,
155
166
  origin: draft.origin ?? "Customer",
156
167
  refusedGifts: [],
157
- custom: createCustomFields(
168
+ custom: await createCustomFields(
158
169
  draft.custom,
159
170
  context.projectKey,
160
171
  this._storage,
@@ -169,7 +180,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
169
180
 
170
181
  // Set shipping info after resource is created
171
182
  if (draft.shippingMethod) {
172
- resource.shippingInfo = this.createShippingInfo(
183
+ resource.shippingInfo = await this.createShippingInfo(
173
184
  context,
174
185
  resource,
175
186
  draft.shippingMethod,
@@ -180,12 +191,12 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
180
191
  resource.taxedPrice = taxedPrice;
181
192
  resource.taxedShippingPrice = taxedShippingPrice;
182
193
 
183
- return this.saveNew(context, resource);
194
+ return await this.saveNew(context, resource);
184
195
  }
185
196
 
186
- getActiveCart(projectKey: string): Cart | undefined {
197
+ async getActiveCart(projectKey: string): Promise<Cart | undefined> {
187
198
  // Get first active cart
188
- const results = this._storage.query(projectKey, this.getTypeId(), {
199
+ const results = await this._storage.query(projectKey, this.getTypeId(), {
189
200
  where: [`cartState="Active"`],
190
201
  });
191
202
  if (results.count > 0) {
@@ -195,26 +206,26 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
195
206
  return;
196
207
  }
197
208
 
198
- draftLineItemtoLineItem = (
209
+ draftLineItemtoLineItem = async (
199
210
  projectKey: string,
200
211
  draftLineItem: LineItemDraft,
201
212
  currency: string,
202
213
  country: string | undefined,
203
- ): LineItem => {
214
+ ): Promise<LineItem> => {
204
215
  const { productId, quantity, variantId, sku } = draftLineItem;
205
216
 
206
217
  let product: Product | null = null;
207
218
 
208
219
  if (productId && variantId) {
209
220
  // Fetch product and variant by ID
210
- product = this._storage.get(projectKey, "product", productId, {});
221
+ product = await this._storage.get(projectKey, "product", productId, {});
211
222
  } else if (sku) {
212
223
  // Fetch product and variant by SKU
213
- const items = this._storage.query(projectKey, "product", {
224
+ const items = await this._storage.query(projectKey, "product", {
214
225
  where: [
215
226
  `masterData(current(masterVariant(sku="${sku}"))) or masterData(current(variants(sku="${sku}")))`,
216
227
  ],
217
- }) as ProductPagedQueryResponse;
228
+ });
218
229
 
219
230
  if (items.count === 1) {
220
231
  product = items.results[0];
@@ -243,20 +254,22 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
243
254
 
244
255
  if (!variant) {
245
256
  // Check if variant is found
246
- throw new Error(
247
- sku
257
+ throw new CommercetoolsError<InvalidOperationError>({
258
+ code: "InvalidOperation",
259
+ message: sku
248
260
  ? `A variant with SKU '${sku}' for product '${product.id}' not found.`
249
261
  : `A variant with ID '${variantId}' for product '${product.id}' not found.`,
250
- );
262
+ });
251
263
  }
252
264
 
253
265
  const quant = quantity ?? 1;
254
266
 
255
267
  const price = selectPrice({ prices: variant.prices, currency, country });
256
268
  if (!price) {
257
- throw new Error(
258
- `No valid price found for ${productId} for country ${country} and currency ${currency}`,
259
- );
269
+ throw new CommercetoolsError<InvalidOperationError>({
270
+ code: "InvalidOperation",
271
+ message: `No valid price found for ${productId} for country ${country} and currency ${currency}`,
272
+ });
260
273
  }
261
274
 
262
275
  const totalPrice = createCentPrecisionMoney({
@@ -281,7 +294,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
281
294
  lineItemMode: "Standard",
282
295
  priceMode: "Platform",
283
296
  state: [],
284
- custom: createCustomFields(
297
+ custom: await createCustomFields(
285
298
  draftLineItem.custom,
286
299
  projectKey,
287
300
  this._storage,
@@ -289,25 +302,28 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
289
302
  };
290
303
  };
291
304
 
292
- createShippingInfo(
305
+ async createShippingInfo(
293
306
  context: RepositoryContext,
294
307
  resource: Writable<Cart>,
295
308
  shippingMethodRef: NonNullable<CartDraft["shippingMethod"]>,
296
- ): NonNullable<Cart["shippingInfo"]> {
309
+ ): Promise<NonNullable<Cart["shippingInfo"]>> {
297
310
  if (resource.taxMode === "External") {
298
- throw new Error("External tax rate is not supported");
311
+ throw new CommercetoolsError<InvalidOperationError>({
312
+ code: "InvalidOperation",
313
+ message: "External tax rate is not supported",
314
+ });
299
315
  }
300
316
 
301
317
  // Bit of a hack: calling this checks that the resource identifier is
302
318
  // valid (i.e. id xor key) and that the shipping method exists.
303
- this._storage.getByResourceIdentifier<"shipping-method">(
319
+ await this._storage.getByResourceIdentifier<"shipping-method">(
304
320
  context.projectKey,
305
321
  shippingMethodRef,
306
322
  );
307
323
 
308
324
  // getShippingMethodsMatchingCart does the work of determining whether the
309
325
  // shipping method is allowed for the cart, and which shipping rate to use
310
- const shippingMethods = getShippingMethodsMatchingCart(
326
+ const shippingMethods = await getShippingMethodsMatchingCart(
311
327
  context,
312
328
  this._storage,
313
329
  resource,
@@ -316,10 +332,11 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
316
332
  },
317
333
  );
318
334
 
319
- const method = shippingMethods.results.find((candidate) =>
320
- shippingMethodRef.id
321
- ? candidate.id === shippingMethodRef.id
322
- : candidate.key === shippingMethodRef.key,
335
+ const method = shippingMethods.results.find(
336
+ (candidate: { id: string; key?: string }) =>
337
+ shippingMethodRef.id
338
+ ? candidate.id === shippingMethodRef.id
339
+ : candidate.key === shippingMethodRef.key,
323
340
  );
324
341
 
325
342
  // Not finding the method in the results means it's not allowed, since
@@ -333,7 +350,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
333
350
  }
334
351
 
335
352
  // Use the shared shipping info creation logic
336
- return createShippingInfoFromMethod(
353
+ return await createShippingInfoFromMethod(
337
354
  context,
338
355
  this._storage,
339
356
  resource,