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

docs(core): Add docs on promotion side effect API for free gift action

Relates to #1798
Michael Bromley 3 лет назад
Родитель
Сommit
5c4c26b48a
1 измененных файлов с 94 добавлено и 1 удалено
  1. 94 1
      docs/content/developer-guide/promotions.md

+ 94 - 1
docs/content/developer-guide/promotions.md

@@ -101,10 +101,11 @@ export const config: VendureConfig = {
 
 ## Creating custom actions
 
-There are two kinds of PromotionAction:
+There are three kinds of PromotionAction:
 
 -   [`PromotionItemAction`]({{< relref "promotion-action" >}}#promotionitemaction) applies a discount on the OrderItem level, i.e. it would be used for a promotion like "50% off USB cables".
 -   [`PromotionOrderAction`]({{< relref "promotion-action" >}}#promotionorderaction) applies a discount on the Order level, i.e. it would be used for a promotion like "5% off the order total".
+-   [`PromotionShippingAction`]({{< relref "promotion-action" >}}#promotionshippingaction) applies a discount on the shipping, i.e. it would be used for a promotion like "free shipping".
 
 Their implementations are similar, with the difference being the arguments passed to the `execute()` function of each.
 
@@ -157,6 +158,98 @@ export const config: VendureConfig = {
 }
 ```
 
+## Free gift promotions
+
+Vendure v1.8 introduces a new **side effect API** to PromotionActions, which allow you to define some additional action to be performed when a Promotion becomes active or inactive.
+
+A primary use-case of this API is to add a free gift to the Order. Here's an example of a plugin which implements a "free gift" action:
+
+```TypeScript
+import {
+  ID, idsAreEqual, isGraphQlErrorResult, LanguageCode,
+  Logger, OrderLine, OrderService, PromotionItemAction, VendurePlugin,
+} from '@vendure/core';
+import { createHash } from 'crypto';
+
+let orderService: OrderService;
+export const freeGiftAction = new PromotionItemAction({
+  code: 'free_gift',
+  description: [{ languageCode: LanguageCode.en, value: 'Add free gifts to the order' }],
+  args: {
+    productVariantIds: {
+      type: 'ID',
+      list: true,
+      ui: { component: 'product-selector-form-input' },
+      label: [{ languageCode: LanguageCode.en, value: 'Gift product variants' }],
+    },
+  },
+  init(injector) {
+    orderService = injector.get(OrderService);
+  },
+  execute(ctx, orderItem, orderLine, args) {
+    // This part is responsible for ensuring the variants marked as 
+    // "free gifts" have their price reduced to zero.  
+    if (lineContainsIds(args.productVariantIds, orderLine)) {
+      const unitPrice = orderLine.unitPrice;
+      return -unitPrice;
+    }
+    return 0;
+  },
+  // The onActivate function is part of the side effect API, and
+  // allows us to perform some action whenever a Promotion becomes active
+  // due to it's conditions & constraints being satisfied.  
+  async onActivate(ctx, order, args, promotion) {
+    for (const id of args.productVariantIds) {
+      if (
+        !order.lines.find(
+          (line) =>
+            idsAreEqual(line.productVariant.id, id) &&
+            line.customFields.freeGiftDescription == null,
+        )
+      ) {
+        // The order does not yet contain this free gift, so add it
+        const result = await orderService.addItemToOrder(ctx, order.id, id, 1, {
+          freeGiftPromotionId: promotion.id.toString(),
+        });
+        if (isGraphQlErrorResult(result)) {
+          Logger.error(`Free gift action error for variantId "${id}": ${result.message}`);
+        }
+      }
+    }
+  },
+  // The onDeactivate function is the other part of the side effect API and is called 
+  // when an active Promotion becomes no longer active. It should reverse any 
+  // side effect performed by the onActivate function.
+  async onDeactivate(ctx, order, args, promotion) {
+    const linesWithFreeGift = order.lines.filter(
+      (line) => line.customFields.freeGiftPromotionId === promotion.id.toString(),
+    );
+    for (const line of linesWithFreeGift) {
+      await orderService.removeItemFromOrder(ctx, order.id, line.id);
+    }
+  },
+});
+
+function lineContainsIds(ids: ID[], line: OrderLine): boolean {
+  return !!ids.find((id) => idsAreEqual(id, line.productVariant.id));
+}
+
+@VendurePlugin({
+  configuration: config => {
+    config.promotionOptions.promotionActions.push(freeGiftAction);
+    config.customFields.OrderItem.push(
+    {
+      name: 'freeGiftPromotionId',
+      type: 'string',
+      public: true,
+      readonly: true,
+      nullable: true,
+    })
+  }
+})
+export class FreeGiftPromotionPlugin {}
+```
+
 ## Dependency relationships
 
 It is possible to establish dependency relationships between a PromotionAction and one or more PromotionConditions.