Browse Source

fix(core): Fix FK error when merging orders with an existing session

Fixes #1454
Michael Bromley 3 years ago
parent
commit
7cedf495f7

+ 31 - 2
packages/core/e2e/order-merge.e2e-spec.ts

@@ -17,14 +17,22 @@ import path from 'path';
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
 
-import { AttemptLogin, GetCustomerList } from './graphql/generated-e2e-admin-types';
+import {
+    AttemptLogin,
+    AttemptLoginMutation,
+    AttemptLoginMutationVariables,
+    GetCustomerList,
+} from './graphql/generated-e2e-admin-types';
 import {
     AddItemToOrder,
+    AddItemToOrderMutation,
+    GetActiveOrderPaymentsQuery,
+    GetNextOrderStatesQuery,
     TestOrderFragmentFragment,
     UpdatedOrderFragment,
 } from './graphql/generated-e2e-shop-types';
 import { ATTEMPT_LOGIN, GET_CUSTOMER_LIST } from './graphql/shared-definitions';
-import { TEST_ORDER_FRAGMENT } from './graphql/shop-definitions';
+import { GET_ACTIVE_ORDER_PAYMENTS, GET_NEXT_STATES, TEST_ORDER_FRAGMENT } from './graphql/shop-definitions';
 import { sortById } from './utils/test-order-utils';
 
 /**
@@ -225,6 +233,27 @@ describe('Order merging', () => {
             })),
         ).toEqual([{ productVariantId: 'T_8', quantity: 1 }]);
     });
+
+    // https://github.com/vendure-ecommerce/vendure/issues/1454
+    it('does not throw FK error when merging with a cart with an existing session', async () => {
+        await shopClient.asUserWithCredentials(customers[7].emailAddress, 'test');
+        // Create an Order linked with the current session
+        const { nextOrderStates } = await shopClient.query<GetNextOrderStatesQuery>(GET_NEXT_STATES);
+
+        // unset last auth token to simulate a guest user in a different browser
+        shopClient.setAuthToken('');
+        await shopClient.query<AddItemToOrderMutation, AddItemToOrderWithCustomFields>(
+            ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
+            { productVariantId: '1', quantity: 2 },
+        );
+
+        const { login } = await shopClient.query<AttemptLoginMutation, AttemptLoginMutationVariables>(
+            ATTEMPT_LOGIN,
+            { username: customers[7].emailAddress, password: 'test' },
+        );
+
+        expect(login.id).toBe(customers[7].user?.id);
+    });
 });
 
 export const ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS = gql`

+ 33 - 5
packages/core/src/service/services/order.service.ts

@@ -77,6 +77,7 @@ import { TransactionalConnection } from '../../connection/transactional-connecti
 import { Customer } from '../../entity/customer/customer.entity';
 import { Fulfillment } from '../../entity/fulfillment/fulfillment.entity';
 import { HistoryEntry } from '../../entity/history-entry/history-entry.entity';
+import { Session } from '../../entity/index';
 import { OrderItem } from '../../entity/order-item/order-item.entity';
 import { OrderLine } from '../../entity/order-line/order-line.entity';
 import { OrderModification } from '../../entity/order-modification/order-modification.entity';
@@ -1459,6 +1460,37 @@ export class OrderService {
         }
     }
 
+    /**
+     * @description
+     * Deletes an Order, ensuring that any Sessions that reference this Order are dereferenced before deletion.
+     *
+     * @since 1.5.0
+     */
+    async deleteOrder(ctx: RequestContext, orderOrId: ID | Order) {
+        const orderToDelete =
+            orderOrId instanceof Order
+                ? orderOrId
+                : await this.connection
+                      .getRepository(ctx, Order)
+                      .findOneOrFail(orderOrId, { relations: ['lines'] });
+        // If there is a Session referencing the Order to be deleted, we must first remove that
+        // reference in order to avoid a foreign key error. See https://github.com/vendure-ecommerce/vendure/issues/1454
+        const sessions = await this.connection
+            .getRepository(ctx, Session)
+            .find({ where: { activeOrderId: orderToDelete.id } });
+        if (sessions.length) {
+            await this.connection
+                .getRepository(ctx, Session)
+                .update(sessions.map(s => s.id) as string[], { activeOrder: null });
+        }
+
+        // TODO: v2 - Will not be needed after adding `{ onDelete: 'CASCADE' }` constraint to ShippingLine.order
+        for (const shippingLine of orderToDelete.shippingLines) {
+            await this.connection.getRepository(ctx, ShippingLine).delete(shippingLine.id);
+        }
+        await this.connection.getRepository(ctx, Order).delete(orderToDelete.id);
+    }
+
     /**
      * @description
      * When a guest user with an anonymous Order signs in and has an existing Order associated with that Customer,
@@ -1481,11 +1513,7 @@ export class OrderService {
         const { orderToDelete, linesToInsert, linesToDelete, linesToModify } = mergeResult;
         let { order } = mergeResult;
         if (orderToDelete) {
-            // TODO: v2 - Will not be needed after adding `{ onDelete: 'CASCADE' }` constraint to ShippingLine.order
-            for (const shippingLine of orderToDelete.shippingLines) {
-                await this.connection.getRepository(ctx, ShippingLine).delete(shippingLine.id);
-            }
-            await this.connection.getRepository(ctx, Order).delete(orderToDelete.id);
+            await this.deleteOrder(ctx, orderToDelete);
         }
         if (order && linesToInsert) {
             const orderId = order.id;