From 2396cc317aad7d497a25e8801e5139dd745dc761 Mon Sep 17 00:00:00 2001 From: Michael Bromley Date: Wed, 17 Feb 2021 14:09:56 +0100 Subject: [PATCH] fix(core): Remove inapplicable order-level discounts Fixes #710 --- packages/core/e2e/order-promotion.e2e-spec.ts | 50 +++++++++++++++++++ .../order-calculator/order-calculator.ts | 15 +++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/packages/core/e2e/order-promotion.e2e-spec.ts b/packages/core/e2e/order-promotion.e2e-spec.ts index fa83cfe297..bbee7c2524 100644 --- a/packages/core/e2e/order-promotion.e2e-spec.ts +++ b/packages/core/e2e/order-promotion.e2e-spec.ts @@ -53,6 +53,7 @@ import { GetActiveOrder, GetOrderPromotionsByCode, RemoveCouponCode, + RemoveItemFromOrder, SetCustomerForOrder, SetShippingMethod, TestOrderFragmentFragment, @@ -76,6 +77,7 @@ import { GET_ACTIVE_ORDER, GET_ORDER_PROMOTIONS_BY_CODE, REMOVE_COUPON_CODE, + REMOVE_ITEM_FROM_ORDER, SET_CUSTOMER, SET_SHIPPING_METHOD, } from './graphql/shop-definitions'; @@ -1387,6 +1389,54 @@ describe('Promotions applied to Orders', () => { }); }); + // https://github.com/vendure-ecommerce/vendure/issues/710 + it('removes order-level discount made invalid by removing OrderLine', async () => { + const promotion = await createPromotion({ + enabled: true, + name: 'Test Promo', + conditions: [minOrderAmountCondition(10000)], + actions: [ + { + code: orderFixedDiscount.code, + arguments: [{ name: 'discount', value: '1000' }], + }, + ], + }); + + await shopClient.asAnonymousUser(); + await shopClient.query(ADD_ITEM_TO_ORDER, { + productVariantId: getVariantBySlug('item-1000').id, + quantity: 8, + }); + const { addItemToOrder } = await shopClient.query( + ADD_ITEM_TO_ORDER, + { + productVariantId: getVariantBySlug('item-5000').id, + quantity: 1, + }, + ); + orderResultGuard.assertSuccess(addItemToOrder); + expect(addItemToOrder!.discounts.length).toBe(1); + expect(addItemToOrder!.discounts[0].description).toBe('Test Promo'); + + const { activeOrder: check1 } = await shopClient.query(GET_ACTIVE_ORDER); + expect(check1!.discounts.length).toBe(1); + expect(check1!.discounts[0].description).toBe('Test Promo'); + + const { removeOrderLine } = await shopClient.query< + RemoveItemFromOrder.Mutation, + RemoveItemFromOrder.Variables + >(REMOVE_ITEM_FROM_ORDER, { + orderLineId: addItemToOrder.lines[1].id, + }); + + orderResultGuard.assertSuccess(removeOrderLine); + expect(removeOrderLine.discounts.length).toBe(0); + + const { activeOrder: check2 } = await shopClient.query(GET_ACTIVE_ORDER); + expect(check2!.discounts.length).toBe(0); + }); + async function getProducts() { const result = await adminClient.query( GET_PRODUCTS_WITH_VARIANT_PRICES, diff --git a/packages/core/src/service/helpers/order-calculator/order-calculator.ts b/packages/core/src/service/helpers/order-calculator/order-calculator.ts index 220d497c02..cf5370194c 100644 --- a/packages/core/src/service/helpers/order-calculator/order-calculator.ts +++ b/packages/core/src/service/helpers/order-calculator/order-calculator.ts @@ -265,7 +265,20 @@ export class OrderCalculator { promotions: Promotion[], ): Promise { const updatedItems = new Set(); - order.lines.forEach(line => line.clearAdjustments(AdjustmentType.DISTRIBUTED_ORDER_PROMOTION)); + const orderHasDistributedPromotions = !!order.discounts.find( + adjustment => adjustment.type === AdjustmentType.DISTRIBUTED_ORDER_PROMOTION, + ); + if (orderHasDistributedPromotions) { + // If the Order currently has any Order-level discounts applied, we need to + // mark all OrderItems in the Order as "updated", since one or more of those + // Order-level discounts may become invalid, which will require _all_ OrderItems + // to be saved. + order.lines.forEach(line => { + line.clearAdjustments(AdjustmentType.DISTRIBUTED_ORDER_PROMOTION); + line.items.forEach(item => updatedItems.add(item)); + }); + } + this.calculateOrderTotals(order); const applicableOrderPromotions = await filterAsync(promotions, p => p.test(ctx, order)); if (applicableOrderPromotions.length) {