فهرست منبع

fix(core): Fix error when pro-rating order with 0 price variant

Fixes #1492
Michael Bromley 3 سال پیش
والد
کامیت
44cc46de14

+ 1 - 0
packages/core/e2e/fixtures/e2e-products-promotions.csv

@@ -4,3 +4,4 @@ item-1000     ,item-1000     ,           ,      ,          ,            ,
 item-5000     ,item-5000     ,           ,      ,          ,            ,            ,I60 ,50.00,standard   ,100        ,false         ,             ,
 item-sale-100 ,item-sale-100 ,           ,      ,promo:sale,            ,            ,I10 ,1.00 ,standard   ,100        ,false         ,             ,
 item-sale-1000,item-sale-1000,           ,      ,promo:sale,            ,            ,I12S,10.00,standard   ,100        ,false         ,             ,
+item-0        ,item-0        ,           ,      ,          ,            ,            ,I0  ,0.00 ,standard   ,100        ,false         ,             ,

+ 38 - 1
packages/core/e2e/order-promotion.e2e-spec.ts

@@ -4,10 +4,12 @@ import { pick } from '@vendure/common/lib/pick';
 import {
     containsProducts,
     customerGroup,
+    DefaultLogger,
     defaultShippingCalculator,
     defaultShippingEligibilityChecker,
     discountOnItemWithFacets,
     hasFacetValues,
+    LogLevel,
     manualFulfillmentHandler,
     minimumOrderAmount,
     orderPercentageDiscount,
@@ -50,6 +52,8 @@ import {
     AdjustItemQuantity,
     AdjustmentType,
     ApplyCouponCode,
+    ApplyCouponCodeMutation,
+    ApplyCouponCodeMutationVariables,
     ErrorCode,
     GetActiveOrder,
     GetOrderPromotionsByCode,
@@ -88,6 +92,7 @@ import { addPaymentToOrder, proceedToArrangingPayment } from './utils/test-order
 describe('Promotions applied to Orders', () => {
     const { server, adminClient, shopClient } = createTestEnvironment({
         ...testConfig(),
+        logger: new DefaultLogger({ level: LogLevel.Info }),
         paymentOptions: {
             paymentMethodHandlers: [testSuccessfulPaymentMethod],
         },
@@ -1503,6 +1508,38 @@ describe('Promotions applied to Orders', () => {
         expect(check2!.discounts.length).toBe(0);
     });
 
+    // https://github.com/vendure-ecommerce/vendure/issues/1492
+    it('correctly handles pro-ration of variants with 0 price', async () => {
+        const couponCode = '20%_off_order';
+        const promotion = await createPromotion({
+            enabled: true,
+            name: '20% discount on order',
+            couponCode,
+            conditions: [],
+            actions: [
+                {
+                    code: orderPercentageDiscount.code,
+                    arguments: [{ name: 'discount', value: '20' }],
+                },
+            ],
+        });
+        await shopClient.asAnonymousUser();
+        await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
+            productVariantId: getVariantBySlug('item-100').id,
+            quantity: 1,
+        });
+        await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
+            productVariantId: getVariantBySlug('item-0').id,
+            quantity: 1,
+        });
+        const { applyCouponCode } = await shopClient.query<
+            ApplyCouponCodeMutation,
+            ApplyCouponCodeMutationVariables
+        >(APPLY_COUPON_CODE, { couponCode });
+        orderResultGuard.assertSuccess(applyCouponCode);
+        expect(applyCouponCode.totalWithTax).toBe(96);
+    });
+
     async function getProducts() {
         const result = await adminClient.query<GetProductsWithVariantPrices.Query>(
             GET_PRODUCTS_WITH_VARIANT_PRICES,
@@ -1546,7 +1583,7 @@ describe('Promotions applied to Orders', () => {
     }
 
     function getVariantBySlug(
-        slug: 'item-100' | 'item-1000' | 'item-5000' | 'item-sale-100' | 'item-sale-1000',
+        slug: 'item-100' | 'item-1000' | 'item-5000' | 'item-sale-100' | 'item-sale-1000' | 'item-0',
     ): GetProductsWithVariantPrices.Variants {
         return products.find(p => p.slug === slug)!.variants[0];
     }

+ 12 - 0
packages/core/src/service/helpers/order-calculator/prorate.spec.ts

@@ -27,4 +27,16 @@ describe('prorate()', () => {
     it('many weights non-neatly divisible', () => {
         testProrate([10, 20, 10, 30, 50, 20, 10, 40], 93, [5, 10, 5, 15, 24, 10, 5, 19]);
     });
+    it('weights include zero', () => {
+        testProrate([10, 0], 40, [40, 0]);
+    });
+    it('all weights are zero', () => {
+        testProrate([0, 0], 10, [5, 5]);
+    });
+    it('all weights are zero with zero total', () => {
+        testProrate([0, 0], 0, [0, 0]);
+    });
+    it('amount is negative', () => {
+        testProrate([100, 100], -20, [-10, -10]);
+    });
 });

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

@@ -20,7 +20,7 @@ export function prorate(weights: number[], amount: number): number[] {
 
     let i = 0;
     for (const w of weights) {
-        actual[i] = amount * (w / totalWeight);
+        actual[i] = totalWeight === 0 ? amount / weights.length : amount * (w / totalWeight);
         rounded[i] = Math.floor(actual[i]);
         error[i] = actual[i] - rounded[i];
         added += rounded[i];