Browse Source

feat(core): More flexible handling of shipping calculations

* feat(core): Accept empty as response in shipping calculator

* feat(core): Await all promises at same time in ShippingCalculator

* refactor(core): Update filter undefined shipping calculator

Closes #397, closes #398
Jonathan Célio 5 years ago
parent
commit
d166c08963

+ 2 - 2
packages/core/src/config/shipping-method/shipping-calculator.ts

@@ -59,7 +59,7 @@ export class ShippingCalculator<T extends ShippingCalculatorArgs = {}> extends C
     calculate(
         order: Order,
         args: ConfigArg[],
-    ): ShippingCalculationResult | Promise<ShippingCalculationResult> {
+    ): ShippingCalculationResult | Promise<ShippingCalculationResult> | undefined {
         return this.calculateFn(order, argsArrayToHash(args));
     }
 }
@@ -103,4 +103,4 @@ export interface ShippingCalculationResult {
 export type CalculateShippingFn<T extends ShippingCalculatorArgs> = (
     order: Order,
     args: ConfigArgValues<T>,
-) => ShippingCalculationResult | Promise<ShippingCalculationResult>;
+) => ShippingCalculationResult | Promise<ShippingCalculationResult> | undefined;

+ 9 - 6
packages/core/src/entity/shipping-method/shipping-method.entity.ts

@@ -59,12 +59,15 @@ export class ShippingMethod extends VendureEntity implements ChannelAware, SoftD
     async apply(order: Order): Promise<ShippingCalculationResult | undefined> {
         const calculator = this.allCalculators[this.calculator.code];
         if (calculator) {
-            const { price, priceWithTax, metadata } = await calculator.calculate(order, this.calculator.args);
-            return {
-                price: Math.round(price),
-                priceWithTax: Math.round(priceWithTax),
-                metadata,
-            };
+            const response = await calculator.calculate(order, this.calculator.args);
+            if (response) {
+                const { price, priceWithTax, metadata } = response;
+                return {
+                    price: Math.round(price),
+                    priceWithTax: Math.round(priceWithTax),
+                    metadata,
+                };
+            }
         }
     }
 

+ 25 - 13
packages/core/src/service/helpers/shipping-calculator/shipping-calculator.ts

@@ -1,4 +1,5 @@
 import { Injectable } from '@nestjs/common';
+import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
 
 import { RequestContext } from '../../../api/common/request-context';
 import { ShippingCalculationResult } from '../../../config/shipping-method/shipping-calculator';
@@ -6,6 +7,11 @@ import { Order } from '../../../entity/order/order.entity';
 import { ShippingMethod } from '../../../entity/shipping-method/shipping-method.entity';
 import { ShippingMethodService } from '../../services/shipping-method.service';
 
+type EligibleShippingMethod = {
+    method: ShippingMethod;
+    result: ShippingCalculationResult;
+};
+
 @Injectable()
 export class ShippingCalculator {
     constructor(private shippingMethodService: ShippingMethodService) {}
@@ -14,21 +20,27 @@ export class ShippingCalculator {
      * Returns an array of each eligible ShippingMethod for the given Order and sorts them by
      * price, with the cheapest first.
      */
-    async getEligibleShippingMethods(
-        ctx: RequestContext,
-        order: Order,
-    ): Promise<Array<{ method: ShippingMethod; result: ShippingCalculationResult }>> {
+    async getEligibleShippingMethods(ctx: RequestContext, order: Order): Promise<EligibleShippingMethod[]> {
         const shippingMethods = this.shippingMethodService.getActiveShippingMethods(ctx.channel);
-        const eligibleMethods: Array<{ method: ShippingMethod; result: ShippingCalculationResult }> = [];
-        for (const method of shippingMethods) {
-            const eligible = await method.test(order);
-            if (eligible) {
-                const result = await method.apply(order);
-                if (result) {
-                    eligibleMethods.push({ method, result });
-                }
+
+        const checkEligibilityPromises = shippingMethods.map((method) =>
+            this.checkEligibilityByShippingMethod(order, method),
+        );
+        const eligibleMethods = await Promise.all(checkEligibilityPromises);
+
+        return eligibleMethods.filter(notNullOrUndefined).sort((a, b) => a.result.price - b.result.price);
+    }
+
+    private async checkEligibilityByShippingMethod(
+        order: Order,
+        method: ShippingMethod,
+    ): Promise<EligibleShippingMethod | undefined> {
+        const eligible = await method.test(order);
+        if (eligible) {
+            const result = await method.apply(order);
+            if (result) {
+                return { method, result };
             }
         }
-        return eligibleMethods.sort((a, b) => a.result.price - b.result.price);
     }
 }