@unchainedshop/core-orders 2.9.3 → 2.10.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 (39) hide show
  1. package/lib/module/configureOrderDeliveriesModule.js +16 -23
  2. package/lib/module/configureOrderDeliveriesModule.js.map +1 -1
  3. package/lib/module/configureOrderPaymentsModule.js +15 -21
  4. package/lib/module/configureOrderPaymentsModule.js.map +1 -1
  5. package/lib/module/configureOrderPositionsModule.js +8 -8
  6. package/lib/module/configureOrderPositionsModule.js.map +1 -1
  7. package/lib/module/configureOrdersModule-mutations.js +10 -10
  8. package/lib/module/configureOrdersModule-mutations.js.map +1 -1
  9. package/lib/module/configureOrdersModule-processing.d.ts +3 -1
  10. package/lib/module/configureOrdersModule-processing.js +50 -49
  11. package/lib/module/configureOrdersModule-processing.js.map +1 -1
  12. package/lib/module/configureOrdersModule-transformations.js +3 -3
  13. package/lib/module/configureOrdersModule-transformations.js.map +1 -1
  14. package/lib/module/configureOrdersModule.js +13 -0
  15. package/lib/module/configureOrdersModule.js.map +1 -1
  16. package/lib/orders-settings.d.ts +2 -1
  17. package/lib/orders-settings.js +3 -1
  18. package/lib/orders-settings.js.map +1 -1
  19. package/lib/service/migrateOrderCartService.js +10 -4
  20. package/lib/service/migrateOrderCartService.js.map +1 -1
  21. package/lib/service/nextUserCartService.d.ts +2 -0
  22. package/lib/service/{createUserCartService.js → nextUserCartService.js} +13 -2
  23. package/lib/service/nextUserCartService.js.map +1 -0
  24. package/lib/service/orderServices.js +2 -2
  25. package/lib/service/orderServices.js.map +1 -1
  26. package/package.json +8 -7
  27. package/src/module/configureOrderDeliveriesModule.ts +31 -30
  28. package/src/module/configureOrderPaymentsModule.ts +30 -28
  29. package/src/module/configureOrderPositionsModule.ts +20 -15
  30. package/src/module/configureOrdersModule-mutations.ts +18 -12
  31. package/src/module/configureOrdersModule-processing.ts +70 -73
  32. package/src/module/configureOrdersModule-transformations.ts +3 -3
  33. package/src/module/configureOrdersModule.ts +15 -0
  34. package/src/orders-settings.ts +3 -0
  35. package/src/service/migrateOrderCartService.ts +10 -4
  36. package/src/service/{createUserCartService.ts → nextUserCartService.ts} +14 -3
  37. package/src/service/orderServices.ts +2 -2
  38. package/lib/service/createUserCartService.d.ts +0 -2
  39. package/lib/service/createUserCartService.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { mongodb, generateDbFilterById } from '@unchainedshop/mongodb';
2
2
  import { Order, OrderStatus, OrderProcessing, OrdersModule } from '@unchainedshop/types/orders.js';
3
+ import type { Locker } from '@kontsedal/locco';
3
4
  import { OrderDelivery } from '@unchainedshop/types/orders.deliveries.js';
4
5
  import { OrderPayment } from '@unchainedshop/types/orders.payments.js';
5
6
  import { OrderPosition } from '@unchainedshop/types/orders.positions.js';
@@ -21,11 +22,13 @@ export const configureOrderModuleProcessing = ({
21
22
  OrderPositions,
22
23
  OrderDeliveries,
23
24
  OrderPayments,
25
+ locker,
24
26
  }: {
25
27
  Orders: mongodb.Collection<Order>;
26
28
  OrderPositions: mongodb.Collection<OrderPosition>;
27
29
  OrderDeliveries: mongodb.Collection<OrderDelivery>;
28
30
  OrderPayments: mongodb.Collection<OrderPayment>;
31
+ locker: Locker;
29
32
  }): OrderProcessing => {
30
33
  registerEvents(ORDER_PROCESSING_EVENTS);
31
34
 
@@ -250,12 +253,10 @@ export const configureOrderModuleProcessing = ({
250
253
  };
251
254
 
252
255
  return {
253
- checkout: async (orderId, { orderContext, paymentContext, deliveryContext }, unchainedAPI) => {
254
- const { modules } = unchainedAPI;
255
-
256
- await modules.orders.updateContext(orderId, orderContext, unchainedAPI);
257
- let order = await modules.orders.findOrder({ orderId });
256
+ checkout: async (orderId, { paymentContext, deliveryContext }, unchainedAPI) => {
257
+ const { modules, services } = unchainedAPI;
258
258
 
259
+ const order = await Orders.findOne(generateDbFilterById(orderId), {});
259
260
  if (order.status !== null) return order;
260
261
 
261
262
  const errors = [
@@ -267,87 +268,83 @@ export const configureOrderModuleProcessing = ({
267
268
  throw new Error(errors[0]);
268
269
  }
269
270
 
270
- // Process order
271
- order = await modules.orders.processOrder(
272
- order,
273
- {
274
- paymentContext,
275
- deliveryContext,
276
- },
277
- unchainedAPI,
278
- );
279
-
280
- // After checkout, store last checkout information on user
281
- await modules.users.updateLastBillingAddress(order.userId, order.billingAddress);
282
- await modules.users.updateLastContact(order.userId, order.contact);
283
-
284
- // Then ensure new cart is created before we return from checkout
285
- const user = await modules.users.findUserById(order.userId);
286
- const locale = modules.users.userLocale(user);
287
- await modules.orders.ensureCartForUser(
288
- {
289
- user,
290
- countryCode: locale.country,
291
- },
292
- unchainedAPI,
293
- );
294
-
295
- return order;
296
- },
297
-
298
- confirm: async (orderId, { orderContext, paymentContext, deliveryContext }, unchainedAPI) => {
299
- const { modules } = unchainedAPI;
271
+ const lock = await locker.lock(`order:checkout:${order._id}`, 1500).acquire();
272
+ try {
273
+ const processedOrder = await modules.orders.processOrder(
274
+ order,
275
+ {
276
+ paymentContext,
277
+ deliveryContext,
278
+ },
279
+ unchainedAPI,
280
+ );
300
281
 
301
- await modules.orders.updateContext(orderId, orderContext, unchainedAPI);
302
- const order = await modules.orders.findOrder({ orderId });
282
+ await lock.extend(500);
303
283
 
304
- if (order.status !== OrderStatus.PENDING) return order;
284
+ // After checkout, store last checkout information on user
285
+ await modules.users.updateLastBillingAddress(
286
+ processedOrder.userId,
287
+ processedOrder.billingAddress,
288
+ );
289
+ await modules.users.updateLastContact(processedOrder.userId, processedOrder.contact);
290
+
291
+ // Then eventually build next cart
292
+ const user = await modules.users.findUserById(processedOrder.userId);
293
+ const locale = modules.users.userLocale(user);
294
+ await services.orders.nextUserCart(
295
+ {
296
+ user,
297
+ countryCode: locale.country,
298
+ },
299
+ unchainedAPI,
300
+ );
305
301
 
306
- return modules.orders.processOrder(
307
- order,
308
- {
309
- paymentContext,
310
- deliveryContext,
311
- nextStatus: OrderStatus.CONFIRMED,
312
- },
313
- unchainedAPI,
314
- );
302
+ return processedOrder;
303
+ } finally {
304
+ await lock.release();
305
+ }
315
306
  },
316
307
 
317
- reject: async (orderId, { orderContext, paymentContext, deliveryContext }, unchainedAPI) => {
308
+ confirm: async (order, { paymentContext, deliveryContext }, unchainedAPI) => {
318
309
  const { modules } = unchainedAPI;
319
310
 
320
- await modules.orders.updateContext(orderId, orderContext, unchainedAPI);
321
- const order = await modules.orders.findOrder({ orderId });
322
-
323
311
  if (order.status !== OrderStatus.PENDING) return order;
324
312
 
325
- return modules.orders.processOrder(
326
- order,
327
- {
328
- paymentContext,
329
- deliveryContext,
330
- nextStatus: OrderStatus.REJECTED,
331
- },
332
- unchainedAPI,
333
- );
313
+ const lock = await locker.lock(`order:confirm-reject:${order._id}`, 1500).acquire();
314
+ try {
315
+ return await modules.orders.processOrder(
316
+ order,
317
+ {
318
+ paymentContext,
319
+ deliveryContext,
320
+ nextStatus: OrderStatus.CONFIRMED,
321
+ },
322
+ unchainedAPI,
323
+ );
324
+ } finally {
325
+ await lock.release();
326
+ }
334
327
  },
335
328
 
336
- ensureCartForUser: async ({ user, countryCode }, unchainedAPI) => {
337
- const { modules, services } = unchainedAPI;
338
-
339
- if (!ordersSettings.ensureUserHasCart) return null;
329
+ reject: async (order, { paymentContext, deliveryContext }, unchainedAPI) => {
330
+ const { modules } = unchainedAPI;
340
331
 
341
- const cart = await modules.orders.cart({ countryContext: countryCode }, user);
342
- if (cart) return cart;
332
+ if (order.status !== OrderStatus.PENDING) return order;
343
333
 
344
- return services.orders.createUserCart(
345
- {
346
- user,
347
- countryCode,
348
- },
349
- unchainedAPI,
350
- );
334
+ const lock = await locker.lock(`order:confirm-reject:${order._id}`, 1500).acquire();
335
+ try {
336
+ return await modules.orders.processOrder(
337
+ order,
338
+ {
339
+ paymentContext,
340
+ deliveryContext,
341
+ nextStatus: OrderStatus.REJECTED,
342
+ },
343
+ unchainedAPI,
344
+ );
345
+ } finally {
346
+ await lock.release();
347
+ }
351
348
  },
352
349
 
353
350
  processOrder: async (initialOrder, params, unchainedAPI) => {
@@ -120,11 +120,11 @@ export const configureOrderModuleTransformations = ({
120
120
  isCart: (order) => {
121
121
  return order.status === null;
122
122
  },
123
- cart: async ({ orderNumber, countryContext }, user) => {
123
+ cart: async ({ orderNumber, countryContext, userId }) => {
124
124
  const selector: mongodb.Filter<Order> = {
125
- countryCode: countryContext || user.lastLogin.countryCode,
125
+ countryCode: countryContext,
126
126
  status: { $eq: null },
127
- userId: user._id,
127
+ userId,
128
128
  };
129
129
 
130
130
  if (orderNumber) {
@@ -4,6 +4,7 @@ import { OrderDelivery } from '@unchainedshop/types/orders.deliveries.js';
4
4
  import { OrderPayment } from '@unchainedshop/types/orders.payments.js';
5
5
  import { OrderPosition } from '@unchainedshop/types/orders.positions.js';
6
6
  import { generateDbFilterById } from '@unchainedshop/mongodb';
7
+ import { createRequire } from 'node:module';
7
8
  import { OrderDeliveriesCollection } from '../db/OrderDeliveriesCollection.js';
8
9
  import { OrderDiscountsCollection } from '../db/OrderDiscountsCollection.js';
9
10
  import { OrderDiscountTrigger } from '../db/OrderDiscountTrigger.js';
@@ -22,6 +23,9 @@ import { configureOrderModuleProcessing } from './configureOrdersModule-processi
22
23
  import { configureOrdersModuleQueries } from './configureOrdersModule-queries.js';
23
24
  import { configureOrderModuleTransformations } from './configureOrdersModule-transformations.js';
24
25
 
26
+ const require = createRequire(import.meta.url);
27
+ const { Locker, MongoAdapter } = require('@kontsedal/locco');
28
+
25
29
  export const configureOrdersModule = async ({
26
30
  db,
27
31
  options: orderOptions = {},
@@ -34,6 +38,16 @@ export const configureOrdersModule = async ({
34
38
  const OrderPayments = await OrderPaymentsCollection(db);
35
39
  const OrderPositions = await OrderPositionsCollection(db);
36
40
 
41
+ const mongoAdapter = new MongoAdapter({
42
+ client: {
43
+ db: () => db,
44
+ },
45
+ });
46
+ const locker = new Locker({
47
+ adapter: mongoAdapter,
48
+ retrySettings: { retryDelay: 200, retryTimes: 10 },
49
+ });
50
+
37
51
  const findOrderPositions = async (order: Order) =>
38
52
  OrderPositions.find(
39
53
  {
@@ -251,6 +265,7 @@ export const configureOrdersModule = async ({
251
265
  OrderDeliveries,
252
266
  OrderPayments,
253
267
  OrderPositions,
268
+ locker,
254
269
  });
255
270
  const orderMutations = configureOrderModuleMutations({
256
271
  Orders,
@@ -11,14 +11,17 @@ export const ordersSettings = {
11
11
  ensureUserHasCart: null,
12
12
  orderNumberHashFn: null,
13
13
  validateOrderPosition: null,
14
+ lockOrderDuringCheckout: false,
14
15
 
15
16
  configureSettings({
16
17
  ensureUserHasCart = false,
17
18
  orderNumberHashFn = generateRandomHash,
18
19
  validateOrderPosition = defaultValidateOrderPosition,
20
+ lockOrderDuringCheckout = false,
19
21
  }: OrdersSettingsOptions = {}) {
20
22
  this.ensureUserHasCart = ensureUserHasCart;
21
23
  this.orderNumberHashFn = orderNumberHashFn;
22
24
  this.validateOrderPosition = validateOrderPosition;
25
+ this.lockOrderDuringCheckout = lockOrderDuringCheckout;
23
26
  },
24
27
  };
@@ -1,11 +1,17 @@
1
1
  import { MigrateOrderCartsService } from '@unchainedshop/types/orders.js';
2
2
 
3
3
  export const migrateOrderCartsService: MigrateOrderCartsService = async (
4
- { fromUser, toUser, shouldMerge, countryContext },
4
+ { fromUserId, toUserId, shouldMerge, countryContext },
5
5
  unchainedAPI,
6
6
  ) => {
7
- const fromCart = await unchainedAPI.modules.orders.cart({ countryContext }, fromUser);
8
- const toCart = await unchainedAPI.modules.orders.cart({ countryContext }, toUser);
7
+ const fromCart = await unchainedAPI.modules.orders.cart({
8
+ countryContext,
9
+ userId: fromUserId,
10
+ });
11
+ const toCart = await unchainedAPI.modules.orders.cart({
12
+ countryContext,
13
+ userId: toUserId,
14
+ });
9
15
 
10
16
  if (!fromCart) {
11
17
  // No cart, don't copy
@@ -14,7 +20,7 @@ export const migrateOrderCartsService: MigrateOrderCartsService = async (
14
20
 
15
21
  if (!toCart || !shouldMerge) {
16
22
  // No destination cart, move whole cart
17
- unchainedAPI.modules.orders.setCartOwner({ orderId: fromCart._id, userId: toUser._id });
23
+ unchainedAPI.modules.orders.setCartOwner({ orderId: fromCart._id, userId: toUserId });
18
24
  return unchainedAPI.modules.orders.updateCalculation(fromCart._id, unchainedAPI);
19
25
  }
20
26
 
@@ -1,11 +1,22 @@
1
- import { CreateUserCartService } from '@unchainedshop/types/orders.js';
1
+ import { NextUserCartService } from '@unchainedshop/types/orders.js';
2
+ import { ordersSettings } from '../orders-settings.js';
2
3
 
3
- export const createUserCartService: CreateUserCartService = async (
4
- { user, orderNumber, countryCode },
4
+ export const nextUserCartService: NextUserCartService = async (
5
+ { user, orderNumber, countryCode, forceCartCreation },
5
6
  unchainedAPI,
6
7
  ) => {
7
8
  const { modules, services } = unchainedAPI;
8
9
 
10
+ const cart = await modules.orders.cart({
11
+ countryContext: countryCode || user.lastLogin?.countryCode,
12
+ orderNumber,
13
+ userId: user._id,
14
+ });
15
+ if (cart) return cart;
16
+
17
+ const shouldCreateNewCart = forceCartCreation || !ordersSettings.ensureUserHasCart;
18
+ if (!shouldCreateNewCart) return null;
19
+
9
20
  const currency = await services.countries.resolveDefaultCurrencyCode(
10
21
  {
11
22
  isoCode: countryCode,
@@ -1,8 +1,8 @@
1
1
  import { OrderServices } from '@unchainedshop/types/orders.js';
2
2
  import { migrateOrderCartsService } from './migrateOrderCartService.js';
3
- import { createUserCartService } from './createUserCartService.js';
3
+ import { nextUserCartService } from './nextUserCartService.js';
4
4
 
5
5
  export const orderServices: OrderServices = {
6
6
  migrateOrderCarts: migrateOrderCartsService,
7
- createUserCart: createUserCartService,
7
+ nextUserCart: nextUserCartService,
8
8
  };
@@ -1,2 +0,0 @@
1
- import { CreateUserCartService } from '@unchainedshop/types/orders.js';
2
- export declare const createUserCartService: CreateUserCartService;
@@ -1 +0,0 @@
1
- {"version":3,"file":"createUserCartService.js","sourceRoot":"","sources":["../../src/service/createUserCartService.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,qBAAqB,GAA0B,KAAK,EAC/D,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,EAClC,YAAY,EACZ,EAAE;IACF,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC;IAE3C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,0BAA0B,CAClE;QACE,OAAO,EAAE,WAAW;KACrB,EACD,YAAY,CACb,CAAC;IAEF,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;QACxC,MAAM,EAAE,IAAI,CAAC,GAAG;QAChB,WAAW;QACX,QAAQ;QACR,WAAW;QACX,cAAc,EAAE,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,OAAO,EAAE,OAAO;QAChE,OAAO,EACL,IAAI,CAAC,WAAW;YAChB,CAAC,CAAC,IAAI,CAAC,KAAK;gBACV,CAAC,CAAC;oBACE,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW;oBACpC,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO;iBACxD;gBACH,CAAC,CAAC,EAAE,CAAC;KACV,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;AAC3D,CAAC,CAAC"}