Browse Source

fix(core): Ensure all Orders have a ShippingMethod before payment

Michael Bromley 4 years ago
parent
commit
9b9e547696

+ 26 - 1
packages/core/e2e/shop-order.e2e-spec.ts

@@ -807,7 +807,32 @@ describe('Shop orders', () => {
             expect(customer!.addresses).toEqual([]);
         });
 
-        it('can transition to ArrangingPayment once Customer has been set', async () => {
+        it('attempting to transition to ArrangingPayment returns error result when Order has no ShippingMethod', async () => {
+            const { transitionOrderToState } = await shopClient.query<
+                TransitionToState.Mutation,
+                TransitionToState.Variables
+            >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
+            orderResultGuard.assertErrorResult(transitionOrderToState);
+
+            expect(transitionOrderToState!.transitionError).toBe(
+                `Cannot transition Order to the "ArrangingPayment" state without a ShippingMethod`,
+            );
+            expect(transitionOrderToState!.errorCode).toBe(ErrorCode.ORDER_STATE_TRANSITION_ERROR);
+        });
+
+        it('can transition to ArrangingPayment once Customer and ShippingMethod has been set', async () => {
+            const { eligibleShippingMethods } = await shopClient.query<GetShippingMethods.Query>(
+                GET_ELIGIBLE_SHIPPING_METHODS,
+            );
+
+            const { setOrderShippingMethod } = await shopClient.query<
+                SetShippingMethod.Mutation,
+                SetShippingMethod.Variables
+            >(SET_SHIPPING_METHOD, {
+                id: eligibleShippingMethods[0].id,
+            });
+            orderResultGuard.assertSuccess(setOrderShippingMethod);
+
             const { transitionOrderToState } = await shopClient.query<
                 TransitionToState.Mutation,
                 TransitionToState.Variables

+ 16 - 0
packages/core/e2e/stock-control.e2e-spec.ts

@@ -33,8 +33,10 @@ import {
     AddPaymentToOrder,
     ErrorCode,
     GetProductStockLevel,
+    GetShippingMethods,
     PaymentInput,
     SetShippingAddress,
+    SetShippingMethod,
     TestOrderFragmentFragment,
     TestOrderWithPaymentsFragment,
     TransitionToState,
@@ -52,8 +54,10 @@ import {
 import {
     ADD_ITEM_TO_ORDER,
     ADD_PAYMENT,
+    GET_ELIGIBLE_SHIPPING_METHODS,
     GET_PRODUCT_WITH_STOCK_LEVEL,
     SET_SHIPPING_ADDRESS,
+    SET_SHIPPING_METHOD,
     TRANSITION_TO_STATE,
 } from './graphql/shop-definitions';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -86,6 +90,15 @@ describe('Stock control', () => {
         return product;
     }
 
+    async function setFirstEligibleShippingMethod() {
+        const { eligibleShippingMethods } = await shopClient.query<GetShippingMethods.Query>(
+            GET_ELIGIBLE_SHIPPING_METHODS,
+        );
+        await shopClient.query<SetShippingMethod.Mutation, SetShippingMethod.Variables>(SET_SHIPPING_METHOD, {
+            id: eligibleShippingMethods[0].id,
+        });
+    }
+
     beforeAll(async () => {
         await server.init({
             initialData: {
@@ -259,6 +272,7 @@ describe('Stock control', () => {
                     } as CreateAddressInput,
                 },
             );
+            await setFirstEligibleShippingMethod();
             await shopClient.query<TransitionToState.Mutation, TransitionToState.Variables>(
                 TRANSITION_TO_STATE,
                 { state: 'ArrangingPayment' as OrderState },
@@ -440,6 +454,7 @@ describe('Stock control', () => {
                     } as CreateAddressInput,
                 },
             );
+            await setFirstEligibleShippingMethod();
             await shopClient.query<TransitionToState.Mutation, TransitionToState.Variables>(
                 TRANSITION_TO_STATE,
                 { state: 'ArrangingPayment' as OrderState },
@@ -1091,6 +1106,7 @@ describe('Stock control', () => {
                     } as CreateAddressInput,
                 },
             );
+            await setFirstEligibleShippingMethod();
             await shopClient.query<TransitionToState.Mutation, TransitionToState.Variables>(
                 TRANSITION_TO_STATE,
                 {

+ 1 - 0
packages/core/src/i18n/messages/en.json

@@ -98,6 +98,7 @@
     "cannot-transition-no-additional-payments-needed": "Cannot transition Order to the \"ArrangingAdditionalPayment\" state as no additional payments are needed",
     "cannot-transition-to-shipping-when-order-is-empty": "Cannot transition Order to the \"ArrangingShipping\" state when it is empty",
     "cannot-transition-to-payment-without-customer": "Cannot transition Order to the \"ArrangingPayment\" state without Customer details",
+    "cannot-transition-to-payment-without-shipping-method": "Cannot transition Order to the \"ArrangingPayment\" state without a ShippingMethod",
     "cannot-transition-unless-all-cancelled": "Cannot transition Order to the \"Cancelled\" state unless all OrderItems are cancelled",
     "cannot-transition-unless-all-order-items-delivered": "Cannot transition Order to the \"Delivered\" state unless all OrderItems are delivered",
     "cannot-transition-unless-some-order-items-delivered": "Cannot transition Order to the \"PartiallyDelivered\" state unless some OrderItems are delivered",

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

@@ -129,6 +129,9 @@ export class OrderStateMachine {
             if (!data.order.customer) {
                 return `message.cannot-transition-to-payment-without-customer`;
             }
+            if (!data.order.shippingLines || data.order.shippingLines.length === 0) {
+                return `message.cannot-transition-to-payment-without-shipping-method`;
+            }
         }
         if (toState === 'PaymentAuthorized') {
             const hasAnAuthorizedPayment = !!data.order.payments.find(p => p.state === 'Authorized');