Hide menu Last updated: Oct 24 2016
Area: Episerver Commerce Applies to versions: 10 and higher
Other versions:

Custom promotions

This topic describes how to work with custom promotions in Episerver Commerce.

In this topic

How it works

To create a new promotion type, you need to create these:

  • PromotionData. The definition of a promotion, that is, the metadata needed to run the promotion.
  • processor. Evaluates if a promotion should be applied.

PromotionData

PromotionData should contain properties needed to evaluate an order and apply a reward. For example, a PromotionData contains entries to which the promotion applies and the promotion. Add to this type any property that a marketer should edit when setting up a promotion. The base class provides basic metadata properties, such as name and valid dates.

PromotionData comes in three types.

Note: When you create a promotion, inherit from one of the following types; never from PromotionData directly.

  • EntryPromotion. Applies reward to a specific entry.
  • OrderPromotion. Applies reward to full order.
  • ShippingPromotion. Applies reward to order's shipping cost.

Connecting a description to a property using colors

You can use a color to connect a property to a specific part of a promotion description. Colors make it easier to communicate what each property means in the UI. By adding the PromotionRegion attribute to a promotion property, it is marked for being included in the connection to the description. You also can use the PromotionRegion attribute on a block property, to connect properties in a block to the same part of the promotion description.

The attribute constructor takes a single string parameter that determines the property's color. Some pre-defined constants of the type EPiServer.Commerce.Marketing.PromotionRegionName are used by internal promotion types. You should also use those constants when creating custom promotion types.

Like all content types, it is possible to translate the name and description by adding an element in the XML file whose name matches the content type. You can use a new formdescription sub-element to connect properties with the description. By adding "{regionname}some text{/}" in the text, the part some text gets the color matching that region. Both {regionname} and {/} are removed before the UI displays the description.

Example

The following example shows a custom promotion type with two properties: one block and one number. ConditionBlock is colored in the UI with a css-class indicating it is a condition, while Percentage gets a css-class indicating it is a reward.

The description is specified in the formdescription element in the translation xml.

Promotion processors

After creating promotion data, you need to make a processor.

Note: If you want to inherit a promotion data only for the purpose of adding properties, not for evaluation, you can reuse the base promotion's processor.

The processor evaluates if a promotion should apply a reward to an order. You can implement the IPromotionProcessor interface directly, but the recommended way is to inherit from the abstract EntryPromotionProcessorBase<TEntryPromotion>, OrderPromotionProcessorBase<TOrderPromotion>, or  ShippingPromotionProcessorBase<TShippingPromotion> depending on the type of promotion being created.

PromotionProcessorBase has one abstract method to be implemented, Evaluate. The method is supplied with a PromotionData, and a PromotionProcessorContext object that contains information about the current order. It's responsible for evaluating if a reward should apply. The Evaluate method returns a RewardDescription.

RewardDescription

A reward description contains information about if and how a reward is applied. Its most important properties are:

  • A list of redemption descriptions, one for each of the maximum amount of redemptions that could be applied to the current order. The list can ignore redemption limits because the promotion engine handles them.
  • A reward type. Depending on the type, the promotion value is read from the properties UnitDiscount, Percentage or Quantity.
  • A status flag. Indicates if a promotion is not, partially, or fully fulfilled.
  • A saved amount. The amount by which this reward reduces order cost. Set by the promotion engine; should not be set in the promotion processor.

RedemptionDescription

A RedemptionDescription describes one redemption of a reward. Its primary goal is to identify the objects to which the redemption should apply. The RedemptionDescription also determines how much this redemption saves on the order, and has a status flag that is set if the promotion engine (for some reason) decides not to apply this redemption. Most commonly, that is because a redemption limit was reached.

Depending on which type of promotion the reward gives (entry, order or shipping), different types of affected objects are used. They can be found in either AffectedEntriesAffectedShipments or AffectedOrders. Use the CreateRedemptionDescription method on the promotion processor base classes to populate the redemption with the correct type of affected objects.

Affected entries

The promotion engine creates a price matrix for all items in an order form. Working with line item quantities while evaluating and applying discounts can get very complicated and creates a lot of tricky edge cases. The price matrix encapsulates this complexity and helps avoid pitfalls with multiple redemptions and multiple promotions affecting the same line items, gift items, and more.

The price matrix, OrderFormPriceMatrix, is accessible through the EntryPrices property of the PromotionProcessorContext object. PromotionProcessorContext is passed to the Evaluate method as one of the arguments.

The matrix remembers which codes, and quantity for the codes, have been received. The second ExtractEntries call starts to receive entries where the first call ended. This makes it easy to create several redemptions by calling ExtractEntries in a loop, and create one RedemptionDescription inside the loop.

Extract entries

The price matrix has one public method, ExtractEntries, with two overloads. Both overloads takes entry codes and quantity as parameters. One of them also contains an action for getting the entries in a specific sequence. If no sequence is specified, MostExpensiveFirst is used.

When you call ExtractEntries, the state change is analogous to popping an item from a queue.

Entry codes

The entry code is an IEnumerable<string>, where entry codes that should be received are defined. The items in the order, which have been defined in the condition part of the promotion data, are normally used as entry codes.

Quantity

Defines the number of items to receive. In the example "Buy 3, get the cheapest for free", the quantity is 3.

Sort method

An action which makes it possible to define a custom ordering of the affected entries. The order might be important, as only the top 3 items are received in the example "Buy 3, get the cheapest for free".

Two predefined sort orders can be passed in as the sortMethod argument: MostExpensiveFirst and CheapestFirst. Both sort methods are static methods on the OrderFormPriceMatrix class, which is accessible through the EntryPrices method on PromotionProcessorContext.

Set promotion range

SetDiscountRange defines which affected entries, received from the price matrix, should receive a discount. The method has two parameters: skip and take. The example "Buy 3, get the cheapest for free" should make the call SetDiscountRange(2, 1), which skips the first two items, and gives the promotion (free) to the third. If all items get the promotion, for example 20% off all items, do not call SetDiscountRange.

Promotion engine priority handling

The Discount Priorities view lets a merchandiser manage priority and exclusivity for promotions. Using this view, a merchant can indicate priority order (if an item is eligible for multiple promotions) and which promotions cannot be combined with the current one. The view uses the following PromotionData properties.

  • Priority. Higher priority promotions are evaluated before lower priority ones. 
  • ExcludedPromotions. List of excluded promotions; ignored when the promotion is evaluated.

Adding money collections to your promotion

A promotion may need a collection of currencies and amounts as part of the condition evaluation or reward logic. To achieve this, add an IList<Money> property to the custom promotion class. When used on a promotion type, such a Money collection is tightly coupled with the currencies available on the parent campaign.

Initially, currencies related the promotion's campaign market have an amount of zero. Changing a campaign's market also changes available currencies in the property. Consider this when developing promotion processors, because you must decide the desired behavior when one, or even all, amounts are set to zero.

The same applies to the MonetaryReward type, which uses an IList<Money> property to store some of its values.

Adding help text for custom groups

To include help text for custom groups on a campaign or promotion form, add the Display attribute with the GroupName property set to the name of a specific node within the <groups> section in the resource files. The groups sections are content-type specific, so should be placed under <contenttypes> node for your specific type.

See also Property attributes.

Get promotion items

GetPromotionItems exists to support scenarios where you want to display the items included in a promotion or campaign (using GetPromotionItemsForCampaign extension method for IPromotionEngine). You might want to create a landing page for the campaign or highlight campaign products in listings.

Unlike Evaluate, this method does not consider the cart. Instead, it returns all items that could potentially be discounted. Displaying this information on the website should encourage buyers to spend more.

Promotion processor example

Below is a more complete example of a percentage-based promotion processor.

Comments