Sitecore Experience Commerce: Promotion Evaluation and Application Logic in XC 10

In this article, we will review the breaking changes to promotions that have been introduced in XC 10, considerations for upgrading from a XC 9.X solution, and the updated business logic that the Commerce Engine utilises to evaluate and apply promotions.

Note: References to date will indicate both date and time throughout this article.

Introduction

Previously in Sitecore Experience Commerce: Promotion Evaluation and Application Logic, we had identified that promotions calculations were broken down into cart line level and cart level promotions, of which cart line level promotions were calculated and applied prior to cart level promotions.

In Sitecore Commerce 10, there are 3 key changes that are important to understand, especially when upgrading solutions to XC 10, as the changes to rules for promotion application means that cart line and cart subtotal and total calculations may differ.

  1. Exclusive promotions are now exclusive across the cart line level and cart level promotions.
  2. Promotion Priority indexes have been introduced, allowing business users to specify a priority level that will take precedence in the initial ordering of promotions during evaluation. The lower the promotion priority value, the higher the promotion is evaluated (let it sink in for a minute).
    1. For exclusive promotion application, automatic promotions are addressed prior to coupon promotions, then Promotion Priority is the decider for determining the exclusive promotion within the group regardless of whether it's a cart line level or cart level promotion.
    2. For applying multiple non-exclusive promotions, cart line level promotions, still take precedence over cart level promotions, while the Promotion Priority is a secondary factor.
 
Sitecore.Commerce.Plugin.Carts
ICalculateCartPipeline (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Carts.ClearCartBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Carts.CalculateCartSubTotalsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Fulfillment.CalculateCartLinesFulfillmentBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Fulfillment.CalculateCartFulfillmentBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Promotions.CalculateCartPromotionsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Promotions.RemoveUnwatedFreeGiftsFromCartBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Tax.CalculateCartLinesTaxBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Tax.CalculateCartTaxBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Carts.CalculateCartTotalsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Payments.CalculateCartPaymentsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
     ------------------------------------------------------------
     Plugin.Carts.WriteCartTotalsToContextBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)

Promotion Evaluating Logic

The following diagram shows the pipelines and pipeline blocks that are called during the process of evaluating the applicable promotions and additional filtering for exclusive promotion evaluation.

Figure 1. Promotion qualification evaluation pipeline logic

The following steps make up the evaluation process:

  1. Pre-Evaluate Promotions: Removes any previous selected free gift cart lines from the cart and adds them as a unique object by type to the commerce context. This is too ensure that these cart lines are not utilised when evaluating applicable promotions.
  2. Search For Promotions: Performs a search on the promotions index and retrieves the promotion entities from the commerce database:
    • Index Search Filters:
      1. By Promotion Entity Type: Filters down to the promotion entity type, i.e. excludes promotion book indexes.
      2. By Valid Date: Filters out promotions that do not fall within the Valid From/To dates based on effective date.
      3. By Catalogs: Filters out promotions where the associated catalog does not match any of the catalogs that are associated to any of the cart line items.
    • Post-Search Filters on Entity List:
      1. Filter Not Approved Promotions: Removes promotions that are not approved and, if the promotion is disabled, where the effective date is prior to the updated date (the date the promotion was disabled). The latter rule is to allow the promotion to be active when reviewed the storefront at a previous point in time.
  3. Filter Promotions By Items: Removes promotions where the cart contains no sellable items marked as included in the ItemsCollection qualification or where the cart contains any of the sellable items marked as excluded in the ItemsCollection qualification.
  4. Filter Promotions By Coupon: Removes promotions that require a coupon that has not been applied to the cart.
  5. Evaluate Promotions: Filters out promotions where promotion qualification and benefit rules are not applicable to the current cart.
  6. Filter Promotions With Coupons By Exclusivity: If exclusive coupon promotions are present in the promotions list, the list will be filtered down to a single exclusive coupon promotion. The exclusive promotion will be determined with the lowest Promotion Priority value, followed by the Added date that their corresponding coupons were applied to the cart, in the event of a tie breaker.
  7. Filter Promotions By Exclusivity: If exclusive automatic promotions are present in the promotions list, the list will be filtered down to a single exclusive automatic promotion. The promotion will be determined by the lowest Promotion Priority value, followed by the earliest (oldest) Valid From date, and in the event of multiple promotions sharing the same lowest Promotion Priority value and earliest Valid From date the promotion that was created earliest will take win.
  8. Post-Evaluate Promotions: Adds the previously selected free gift cart lines back to the cart; retrieved from the commerce context.

Promotion Priorisation Rules

While the previous section covered how promotions are evaluated, and also provided some insight into promotion priorisation, we will now cover the prioritisation rules.

The following diagram shows the logic used to determine which promotion(s) to apply to the cart.

Figure 2. Flow chart of promotion application logic

The blue boxes represent logic for maintaining free gift selections and don't impact the application of the promotions, however this shows where this logic is handled.

There are essentially 3 steps that make up the promotion application process:

  1. Apply a single exclusive automatic promotion.
    • The promotion will be determined by the lowest Promotion Priority value, which will fallback to the earliest (oldest) Valid From date, and then the earliest Created date the promotion that was created, in the result of tied values.
    • If a promotion is applied here, no further promotions are applied.
  2. Apply a single exclusive coupon promotion.
    • The promotion will be determined by the lowest Promotion Priority value, with the Added date that their corresponding coupons were applied to the cart as a fallback to manage tied values.
    • If a promotion is applied here, no further promotions are applied.
  3. Apply all non-exclusive promotions.
    • The promotion order will be determined by cart line level promotions being applied first and cart level promotions applied second, both ordered by ascending Promotion Priority value. Where Promotion Priority values are equal, promotions are furthered ordered as follows:
      1. Automatic promotions, ordered by earliest (oldest) Valid From date, and in the event of multiple promotions sharing the same earliest Valid From date the promotion that was created earliest will win.
      2. Coupon promotions, ordered by earliest Added date that their corresponding coupons were applied to the cart.

Can Promotions Be Configured to XC 9.X Logic?

If Promotion Priorities are not utilised, the promotion logic remains largely the same as cart line level promotions are still applied before cart level promotions. The key difference is the promotion logic now ensures a sole exclusive promotion, which cannot be reverted by configuration and will require customisation.

References

Sitecore Experience Commerce: Promotion Evaluation and Application Logic

In this article, we will review the default business logic that the Commerce Engine utilises to evaluate and apply promotions.

Note: References to date will indicate both date and time throughout this article.

Introduction

Before we get into the details around promotions, there are a few things we need to understand.

    • Promotions are separated into cart line level and cart level promotions, determined by the promotion benefits configured to each promotion. While multiple benefits can be added to promotions, additional benefits after the first can only be of the same benefit type.
    • Cart line calculations (subtotals, fulfillment fees, promotion discounts, taxes, totals) are evaluated and applied prior to the and cart calculations.
      Sitecore.Commerce.Plugin.Carts
      ICalculateCartLinesPipeline (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Carts.ClearCartLinesBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Carts.ClearCartBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Carts.CalculateCartLinesSubTotalsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Fulfillment.CalculateCartLinesFulfillmentBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Promotions.CalculateCartLinesPromotionsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Tax.CalculateCartLinesTaxBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Carts.CalculateCartLinesTotalsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
      -----------------------------------------------------------------
      Sitecore.Commerce.Plugin.Carts
      ICalculateCartPipeline (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Carts.CalculateCartSubTotalsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Fulfillment.CalculateCartFulfillmentBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Promotions.CalculateCartPromotionsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Tax.CalculateCartTaxBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Carts.CalculateCartTotalsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Payments.CalculateCartPaymentsBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
           ------------------------------------------------------------
           Plugin.Carts.WriteCartTotalsToContextBlock (Sitecore.Commerce.Plugin.Carts.Cart => Sitecore.Commerce.Plugin.Carts.Cart)
      
    • Exclusive promotions apply against the benefit type only, therefore it is possible to apply a cart line level exclusive promotion and a cart level exclusive promotion at the same time.

Evaluating Promotions

The following diagram shows the pipelines and pipeline blocks that are called during the process of evaluating the applicable promotions and additional filtering for exclusive promotion evaluation.

There are essentially 10 steps that make up the evaluation process:

  1. Search For Promotions: Retrieves all promotions.
  2. Filter Promotions By Valid Date: Removes promotions that do not fall within the Valid From/To dates based on effective date.
  3. Filter Not Approved Promotions: Removes promotions that are not approved and, if the promotion is disabled, where the effective date is prior to the updated date (the date the promotion was disabled). The latter rule is to allow the promotion to be active when reviewed the storefront at a previous point in time.
    Note: From 9.0.1, the GlobalPromotionsPolicy was introduced to allow promotions to be previewed in the storefront prior to submitting a promotion for approval.
  4. Filter Promotions By Items: Removes promotions where the cart contains no sellable items marked as included in the ItemsCollection qualification or where the cart contains any of the sellable items marked as excluded in the ItemsCollection qualification.
  5. Filter Promotions By Book Associated Catalogs: Removes promotions where the catalog, associated to its promotion book, does not match any of the catalogs associated to the sellable items of the cart lines.
  6. Filter Promotions By Benefit Type: Removes promotions where the type of benefits configured to the promotion does not match the benefit type being evaluated. i.e. Cart line level benefits (CartLineActions) and cart level benefits (CartActions) for CalculateCartLinesPipeline and CalculateCartLinesPipeline respectively.
  7. Filter Promotions By Coupon: Removes promotions that require a coupon that has not been applied to the cart.
  8. Evaluate Promotions: Filters out promotions where promotion qualifications and benefit rules do are not applicable to the current cart.
  9. Filter Promotions With Coupons By Exclusivity: If exclusive coupon promotions are present in the promotions list, the list will be filtered down to a single exclusive coupon promotion. The promotion will be determined by the Added date that their corresponding coupons were applied to the cart.
  10. Filter Promotions By Exclusivity: If exclusive automatic promotions are present in the promotions list, the list will be filtered down to a single exclusive automatic promotion. The promotion will be determined by the earliest (oldest) Valid From date, and in the event of multiple promotions sharing the same earliest Valid From date the promotion that was created earliest will take win.

Promotion Priorisation Rules

While the previous section covered how promotions are evaluated, and also provided some insight into promotion priorisation, we will now cover the prioritisation rules.

The following diagram shows the logic used to determine which promotion(s) to apply to the cart.

There are essentially 3 steps that make up the application process:

  1. Apply a single exclusive automatic promotion.
    • The promotion will be determined by the earliest (oldest) Valid From date, and in the event of multiple promotions sharing the same earliest Valid From date the promotion that was created earliest will take win.
    • If a promotion is applied here no further promotions are applied.
  2. Apply a single exclusive coupon promotion.
    • The promotion will be determined by the Added date that their corresponding coupons were applied to the cart.
    • If a promotion is applied here no further promotions are applied.
  3. Apply all non-exclusive promotions.
    • The promotion order will be determined by:
      1. Automatic promotions ordered by earliest (oldest) Valid From date, and in the event of multiple promotions sharing the same earliest Valid From date the promotion that was created earliest will take win.
      2. Coupon Promotions ordered by earliest Added date that their corresponding coupons were applied to the cart.

References