Просмотр исходного кода

fix(core): Fix bug in applying OrderItem promotions with postgres

Resolves an incorrect discount application bug, caused by the order of
OrderLine.items being non-deterministic in postgres.
Michael Bromley 4 лет назад
Родитель
Сommit
aaa83937b4

+ 8 - 0
packages/core/src/entity/order-line/order-line.entity.ts

@@ -196,6 +196,14 @@ export class OrderLine extends VendureEntity implements HasCustomFields {
         return (this.items || []).filter(i => !i.cancelled);
     }
 
+    /**
+     * Returns the first OrderItems of the line (i.e. the one with the earliest
+     * `createdAt` property).
+     */
+    get firstItem(): OrderItem | undefined {
+        return (this.items ?? []).sort((a, b) => +a.createdAt - +b.createdAt)[0];
+    }
+
     /**
      * Clears Adjustments from all OrderItems of the given type. If no type
      * is specified, then all adjustments are removed.

+ 1 - 1
packages/core/src/service/helpers/order-calculator/order-calculator.spec.ts

@@ -1573,7 +1573,7 @@ function assertOrderTotalsAddUp(order: Order) {
         const itemUnitPriceWithTaxSum = summate(line.items, 'unitPriceWithTax');
         expect(line.linePriceWithTax).toBe(itemUnitPriceWithTaxSum);
 
-        const pricesIncludeTax = line.items[0].listPriceIncludesTax;
+        const pricesIncludeTax = line.firstItem?.listPriceIncludesTax;
 
         if (pricesIncludeTax) {
             const lineDiscountsAmountWithTaxSum = summate(line.discounts, 'amountWithTax');

+ 18 - 10
packages/core/src/service/helpers/order-calculator/order-calculator.ts

@@ -185,7 +185,7 @@ export class OrderCalculator {
             // which affected the order price.
             const applicablePromotions = await filterAsync(promotions, p => p.test(ctx, order).then(Boolean));
 
-            const lineHasExistingPromotions = !!line.items[0]?.adjustments?.find(
+            const lineHasExistingPromotions = !!line.firstItem?.adjustments?.find(
                 a => a.type === AdjustmentType.PROMOTION,
             );
             const forceUpdateItems = this.orderLineHasInapplicablePromotions(applicablePromotions, line);
@@ -209,10 +209,14 @@ export class OrderCalculator {
                 if (applicableOrState) {
                     const state = typeof applicableOrState === 'object' ? applicableOrState : undefined;
                     for (const item of line.items) {
-                        const adjustment = await promotion.apply(ctx, {
-                            orderItem: item,
-                            orderLine: line,
-                        }, state);
+                        const adjustment = await promotion.apply(
+                            ctx,
+                            {
+                                orderItem: item,
+                                orderLine: line,
+                            },
+                            state,
+                        );
                         if (adjustment) {
                             item.addAdjustment(adjustment);
                             priceAdjusted = true;
@@ -225,9 +229,9 @@ export class OrderCalculator {
                     }
                 }
             }
-            const lineNoLongerHasPromotions =
-                !line.items[0]?.adjustments ||
-                !line.items[0]?.adjustments.find(a => a.type === AdjustmentType.PROMOTION);
+            const lineNoLongerHasPromotions = !line.firstItem?.adjustments?.find(
+                a => a.type === AdjustmentType.PROMOTION,
+            );
             if (lineHasExistingPromotions && lineNoLongerHasPromotions) {
                 line.items.forEach(i => updatedOrderItems.add(i));
             }
@@ -285,7 +289,9 @@ export class OrderCalculator {
         }
 
         this.calculateOrderTotals(order);
-        const applicableOrderPromotions = await filterAsync(promotions, p => p.test(ctx, order).then(Boolean));
+        const applicableOrderPromotions = await filterAsync(promotions, p =>
+            p.test(ctx, order).then(Boolean),
+        );
         if (applicableOrderPromotions.length) {
             for (const promotion of applicableOrderPromotions) {
                 // re-test the promotion on each iteration, since the order total
@@ -334,7 +340,9 @@ export class OrderCalculator {
     }
 
     private async applyShippingPromotions(ctx: RequestContext, order: Order, promotions: Promotion[]) {
-        const applicableOrderPromotions = await filterAsync(promotions, p => p.test(ctx, order).then(Boolean));
+        const applicableOrderPromotions = await filterAsync(promotions, p =>
+            p.test(ctx, order).then(Boolean),
+        );
         if (applicableOrderPromotions.length) {
             order.shippingLines.forEach(line => (line.adjustments = []));
             for (const promotion of applicableOrderPromotions) {