1
0

mv-order-process.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { OrderType } from '@vendure/common/lib/generated-types';
  2. import { ID } from '@vendure/common/lib/shared-types';
  3. import {
  4. ChannelService,
  5. CustomOrderProcess,
  6. idsAreEqual,
  7. Order,
  8. orderItemsAreDelivered,
  9. orderItemsArePartiallyDelivered,
  10. orderItemsArePartiallyShipped,
  11. orderItemsAreShipped,
  12. OrderService,
  13. RequestContext,
  14. RequestContextService,
  15. TransactionalConnection,
  16. User,
  17. } from '@vendure/core';
  18. let connection: TransactionalConnection;
  19. let orderService: OrderService;
  20. let channelService: ChannelService;
  21. let requestContextService: RequestContextService;
  22. export const multivendorOrderProcess: CustomOrderProcess<any> = {
  23. init(injector) {
  24. connection = injector.get(TransactionalConnection);
  25. orderService = injector.get(OrderService);
  26. channelService = injector.get(ChannelService);
  27. requestContextService = injector.get(RequestContextService);
  28. },
  29. async onTransitionStart(fromState, toState, data) {
  30. const { ctx, order } = data;
  31. if (fromState === 'AddingItems' && toState === 'ArrangingPayment') {
  32. for (const line of data.order.lines) {
  33. if (!line.shippingLineId) {
  34. return 'not all lines have shipping';
  35. }
  36. }
  37. }
  38. // Aggregate orders are allowed to transition to these states without validating
  39. // fulfillments, since aggregate orders do not have fulfillments, but will get
  40. // transitioned based on the status of the sellerOrders' fulfillments.
  41. if (order.type !== OrderType.Aggregate) {
  42. if (toState === 'PartiallyShipped') {
  43. const orderWithFulfillments = await findOrderWithFulfillments(ctx, order.id);
  44. if (!orderItemsArePartiallyShipped(orderWithFulfillments)) {
  45. return `message.cannot-transition-unless-some-order-items-shipped`;
  46. }
  47. }
  48. if (toState === 'Shipped') {
  49. const orderWithFulfillments = await findOrderWithFulfillments(ctx, order.id);
  50. if (!orderItemsAreShipped(orderWithFulfillments)) {
  51. return `message.cannot-transition-unless-all-order-items-shipped`;
  52. }
  53. }
  54. if (toState === 'PartiallyDelivered') {
  55. const orderWithFulfillments = await findOrderWithFulfillments(ctx, order.id);
  56. if (!orderItemsArePartiallyDelivered(orderWithFulfillments)) {
  57. return `message.cannot-transition-unless-some-order-items-delivered`;
  58. }
  59. }
  60. if (toState === 'Delivered') {
  61. const orderWithFulfillments = await findOrderWithFulfillments(ctx, order.id);
  62. if (!orderItemsAreDelivered(orderWithFulfillments)) {
  63. return `message.cannot-transition-unless-all-order-items-delivered`;
  64. }
  65. }
  66. }
  67. },
  68. async onTransitionEnd(fromState, toState, data) {
  69. const { ctx, order } = data;
  70. if (order.type === OrderType.Seller) {
  71. const aggregateOrder = await orderService.getAggregateOrder(ctx, order);
  72. if (aggregateOrder) {
  73. // Create a new RequestContext on the default Channel, since the current
  74. // RequestContext may be scoped to the Seller channel, and will not be able to
  75. // update the AggregateOrder.
  76. const defaultChannel = await channelService.getDefaultChannel();
  77. const defaultChannelCtx = await requestContextService.create({
  78. apiType: 'admin',
  79. channelOrToken: defaultChannel,
  80. req: ctx.req,
  81. languageCode: ctx.languageCode,
  82. user: ctx.activeUserId ? new User({ id: ctx.activeUserId }) : undefined,
  83. });
  84. // This part is responsible for automatically updating the state of the aggregate Order
  85. // based on the fulfillment state of all the associated seller Orders.
  86. const otherSellerOrders = (await orderService.getSellerOrders(ctx, aggregateOrder)).filter(
  87. so => !idsAreEqual(so.id, order.id),
  88. );
  89. const sellerOrderStates = [...otherSellerOrders.map(so => so.state), toState];
  90. if (sellerOrderStates.every(state => state === 'Shipped')) {
  91. await orderService.transitionToState(defaultChannelCtx, aggregateOrder.id, 'Shipped');
  92. } else if (sellerOrderStates.every(state => state === 'Delivered')) {
  93. await orderService.transitionToState(defaultChannelCtx, aggregateOrder.id, 'Delivered');
  94. } else if (sellerOrderStates.some(state => state === 'Delivered')) {
  95. await orderService.transitionToState(
  96. defaultChannelCtx,
  97. aggregateOrder.id,
  98. 'PartiallyDelivered',
  99. );
  100. } else if (sellerOrderStates.some(state => state === 'Shipped')) {
  101. await orderService.transitionToState(
  102. defaultChannelCtx,
  103. aggregateOrder.id,
  104. 'PartiallyShipped',
  105. );
  106. }
  107. }
  108. }
  109. },
  110. };
  111. async function findOrderWithFulfillments(ctx: RequestContext, id: ID): Promise<Order> {
  112. return await connection.getEntityOrThrow(ctx, Order, id, {
  113. relations: ['lines', 'fulfillments', 'fulfillments.lines', 'fulfillments.lines.fulfillment'],
  114. });
  115. }