Browse Source

feat(core): Introduce OrderPlacedStrategy for better control of process

This strategy allows fine control over exactly when an Order is considered to have been "placed".
Michael Bromley 5 years ago
parent
commit
b9b7767889

+ 2 - 0
packages/core/src/config/default-config.ts

@@ -19,6 +19,7 @@ import { manualFulfillmentHandler } from './fulfillment/manual-fulfillment-handl
 import { DefaultLogger } from './logger/default-logger';
 import { DefaultChangedPriceHandlingStrategy } from './order/default-changed-price-handling-strategy';
 import { DefaultOrderItemPriceCalculationStrategy } from './order/default-order-item-price-calculation-strategy';
+import { DefaultOrderPlacedStrategy } from './order/default-order-placed-strategy';
 import { DefaultStockAllocationStrategy } from './order/default-stock-allocation-strategy';
 import { MergeOrdersStrategy } from './order/merge-orders-strategy';
 import { DefaultOrderCodeStrategy } from './order/order-code-strategy';
@@ -115,6 +116,7 @@ export const defaultConfig: RuntimeVendureConfig = {
         stockAllocationStrategy: new DefaultStockAllocationStrategy(),
         orderCodeStrategy: new DefaultOrderCodeStrategy(),
         changedPriceHandlingStrategy: new DefaultChangedPriceHandlingStrategy(),
+        orderPlacedStrategy: new DefaultOrderPlacedStrategy(),
     },
     paymentOptions: {
         paymentMethodEligibilityCheckers: [],

+ 3 - 0
packages/core/src/config/index.ts

@@ -22,10 +22,13 @@ export * from './logger/vendure-logger';
 export * from './merge-config';
 export * from './order/custom-order-process';
 export * from './order/changed-price-handling-strategy';
+export * from './order/default-changed-price-handling-strategy';
+export * from './order/default-order-placed-strategy';
 export * from './order/default-stock-allocation-strategy';
 export * from './order/merge-orders-strategy';
 export * from './order/order-code-strategy';
 export * from './order/order-merge-strategy';
+export * from './order/order-placed-strategy';
 export * from './order/use-existing-strategy';
 export * from './order/use-guest-strategy';
 export * from './order/use-guest-if-existing-empty-strategy';

+ 26 - 0
packages/core/src/config/order/default-order-placed-strategy.ts

@@ -0,0 +1,26 @@
+import { RequestContext } from '../../api/common/request-context';
+import { Order } from '../../entity/order/order.entity';
+import { OrderState } from '../../service/helpers/order-state-machine/order-state';
+
+import { OrderPlacedStrategy } from './order-placed-strategy';
+
+/**
+ * @description
+ * The default {@link OrderPlacedStrategy}. The order is set as "placed" when it transitions from
+ * 'ArrangingPayment' to either 'PaymentAuthorized' or 'PaymentSettled'.
+ *
+ * @docsCategory orders
+ */
+export class DefaultOrderPlacedStrategy implements OrderPlacedStrategy {
+    shouldSetAsPlaced(
+        ctx: RequestContext,
+        fromState: OrderState,
+        toState: OrderState,
+        order: Order,
+    ): boolean {
+        return fromState === 'ArrangingPayment' &&
+            (toState === 'PaymentAuthorized' || toState === 'PaymentSettled')
+            ? true
+            : false;
+    }
+}

+ 33 - 0
packages/core/src/config/order/order-placed-strategy.ts

@@ -0,0 +1,33 @@
+import { RequestContext } from '../../api/common/request-context';
+import { InjectableStrategy } from '../../common/types/injectable-strategy';
+import { Order } from '../../entity/order/order.entity';
+import { OrderState } from '../../service/helpers/order-state-machine/order-state';
+
+/**
+ * @description
+ * This strategy is responsible for deciding at which stage in the order process
+ * the Order will be set as "placed" (i.e. the Customer has checked out, and
+ * next it must be processed by an Administrator).
+ *
+ * By default, the order is set as "placed" when it transitions from
+ * 'ArrangingPayment' to either 'PaymentAuthorized' or 'PaymentSettled'.
+ *
+ * @docsCategory orders
+ */
+export interface OrderPlacedStrategy extends InjectableStrategy {
+    /**
+     * @description
+     * This method is called whenever an _active_ Order transitions from one state to another.
+     * If it resolves to `true`, then the Order will be set as "placed", which means:
+     *
+     * * Order.active = false
+     * * Order.placedAt = new Date()
+     * * Any active Promotions are linked to the Order
+     */
+    shouldSetAsPlaced(
+        ctx: RequestContext,
+        fromState: OrderState,
+        toState: OrderState,
+        order: Order,
+    ): boolean | Promise<boolean>;
+}

+ 1 - 1
packages/core/src/config/order/stock-allocation-strategy.ts

@@ -13,7 +13,7 @@ import { OrderState } from '../../service/helpers/order-state-machine/order-stat
 export interface StockAllocationStrategy extends InjectableStrategy {
     /**
      * @description
-     * This method is called whenever and Order transitions from one state to another.
+     * This method is called whenever an Order transitions from one state to another.
      * If it resolves to `true`, then stock will be allocated for this order.
      */
     shouldAllocateStock(

+ 8 - 0
packages/core/src/config/vendure-config.ts

@@ -25,6 +25,7 @@ import { CustomOrderProcess } from './order/custom-order-process';
 import { OrderCodeStrategy } from './order/order-code-strategy';
 import { OrderItemPriceCalculationStrategy } from './order/order-item-price-calculation-strategy';
 import { OrderMergeStrategy } from './order/order-merge-strategy';
+import { OrderPlacedStrategy } from './order/order-placed-strategy';
 import { StockAllocationStrategy } from './order/stock-allocation-strategy';
 import { CustomPaymentProcess } from './payment/custom-payment-process';
 import { PaymentMethodEligibilityChecker } from './payment/payment-method-eligibility-checker';
@@ -446,6 +447,13 @@ export interface OrderOptions {
      * @default DefaultChangedPriceHandlingStrategy
      */
     changedPriceHandlingStrategy?: ChangedPriceHandlingStrategy;
+    /**
+     * @description
+     * Defines the point of the order process at which the Order is set as "placed".
+     *
+     * @default DefaultOrderPlacedStrategy
+     */
+    orderPlacedStrategy?: OrderPlacedStrategy;
 }
 
 /**

+ 8 - 8
packages/core/src/service/helpers/order-state-machine/order-state-machine.ts

@@ -153,14 +153,14 @@ export class OrderStateMachine {
      */
     private async onTransitionEnd(fromState: OrderState, toState: OrderState, data: OrderTransitionData) {
         const { ctx, order } = data;
-        const { stockAllocationStrategy } = this.configService.orderOptions;
-        if (
-            fromState === 'ArrangingPayment' &&
-            (toState === 'PaymentAuthorized' || toState === 'PaymentSettled')
-        ) {
-            order.active = false;
-            order.orderPlacedAt = new Date();
-            await this.promotionService.addPromotionsToOrder(ctx, order);
+        const { stockAllocationStrategy, orderPlacedStrategy } = this.configService.orderOptions;
+        if (order.active) {
+            const shouldSetAsPlaced = orderPlacedStrategy.shouldSetAsPlaced(ctx, fromState, toState, order);
+            if (shouldSetAsPlaced) {
+                order.active = false;
+                order.orderPlacedAt = new Date();
+                await this.promotionService.addPromotionsToOrder(ctx, order);
+            }
         }
         const shouldAllocateStock = await stockAllocationStrategy.shouldAllocateStock(
             ctx,