Try our conversational search powered by Generative AI!

Promotions - Dynamic discount on items within the same Promotion


Hi there! 

We are dealing with a business scenario for which we are trying to find a solution.

Let's work with the following assumptions:

We have 3 Items

  • Item 100101
    • Weight 2 lb
    • Price  $ 10
  • Item 100102
    • Weight 3 lb
    • Price  $ 30
  • Item 100103
    • Weight 8 lb
    • Price  $ 20 

We have one Promotion at item level, with those 3 items.
And from the Business side, we want to apply a Discount for each item, based on the weight. Meaning we can have a $1 OFF per pound

  • Item 100101
    • 2 lb x $ 1 = $2 OFF
    • Discounted price = $10 - $2 = $8
  • Item 100102
    • 3 lb x $ 1 = $3 OFF
    • Discounted price = $30 - $3 = $17
  • Item 100103
    • 8 lb x $ 1 = $8 OFF
    • Discounted price = $20 - $8 = $12

I have been trying to override or create a new processor with the logic to accomplish this, but I am not finding an easy way to do it. 
One of the main issues is that the object RedemptionDescription has a list of AffectedEntries, but is has only a decimal SavedAmount, which is just one amount that will be applied to all affected entries (besides is read-only).

public class RedemptionDescription
        public AffectedEntries AffectedEntries { get; }
        public decimal SavedAmount { get; }

Another thing I have been looking into is overriding the GetRedemptions, to return multiples RedemptionDescription, one for each item, but is getting quite complex.

Workarounds I can think of:

  1. Having one item per promotion
  2. Having one promotion at order level 

Any ideas or anyone that has gone trough something similar? 

Feb 22, 2021 21:41

Hi Mauro,

I think you can try to add manual line item discount to fit your requirement like this:

var promotionInformation = new PromotionInformation { 
Name = discountName, 
Description = discountName, 
DiscountType = EPiServer.Commerce.Marketing.DiscountType.Manual, 
Entries = new List { 
new PromotionInformationEntry { EntryCode = "100101", SavedAmount = 2 },
new PromotionInformationEntry { EntryCode = "100102", SavedAmount = 3 },
new PromotionInformationEntry { EntryCode = "100103", SavedAmount = 8 },
OrderForm = new PromotionInformationOrderForm { SavedAmount = 0 } }; 

Reference link:

Feb 23, 2021 8:15

I have been through this rabbit hole many a times and have yet to find a good enough solution for it, myself.

Mauro, am I correct in assuming the requirement you'd want can be shortened to:

"For a promotion, in a promotion processor, I want to be able to have different monetary discounts on different items on the cart."

My workarounds have previously been to redirect the customer to somehow work with prices instead. But I think for your scenario, it is even more obvious that promotions would be the correct place to do this.

I don't know any other workarounds then the two you listed.

It feels like quite rooted in the promotion system that it is not the processors concern to actually reduce the price of a specific item, but rather to return instructions how to do it, so when all processors have been run, you can apply the correct prices, with the complexity of combining and everything else.

@Quan, do you have any thoughts here?

Feb 23, 2021 16:15

@Binh: Thanks for the option, but we would like to run it with a promotion processor.

@Joel: Exactly. And thanks for summarizing it perfectly. 

Will wait to see if anyone else has some thoughts.

Feb 23, 2021 16:38

For reference and further discussion, the use case I latest worked with this was:

The customer had a list of prices of variations, that were a part of a discount. They wanted us to build a promotion were they would simply just input the variation codes with their respective price and have the promotion see if any cart items matched the items in the promotion. If they matched, the promotion would reduce the price of that item to the price configured in the promotion.

This specific requirement is probably, in the whole perspective, better to do via custom pricing and connect content to it. But there are others (such as the case OP described) that are more legit. And even so, it would be nice to solve my case in the epi promotion system, if the customer cannot achieve the same thing with prices/in their ERP, and their business have a good case for working with price lists.

... Another workaround that came to my head while writing this:

  1. You create a promotion that looks the same in the admin UI as you would imagine if it worked.
  2. In the processor, you do three things:
  3. Calculate the prices per item as you want them to be and save this data to a custom metafield on the orderform
  4. Return a fulfilled reward, but with no price change. See code snippet below how that can be done
  5. Add a flag to the HttpContext somewhere, to indicate that you've already calculated the prices for the items in this request. This because you cannot access the PromotionEngineSettings within the processor, which we'll use later. (Quan: can we get this? 😁 More than once I'd like to access the promotionenginesettings within the processor)
  6. After you ran the promotion engine for the first time, you automatically create manual promotions based off the data you stored in the meta field (the prices per item) and then you run promotions again, but with the `ReevaluatePromotions` flag set to true. You can use the httpcontext flag to make sure the promotion won't change the value in the meta field again (even though the result should be the same)

It might work... Is it water proof to all possible crazy ways the admins can configure proomtions? Probably not. Will you have to deal with combinations and multiple promotions of this type yourself? Yes, prepare your meta field for it! 

It is very hacky though :)

Edited, Feb 23, 2021 17:09

Woops, forgot to add code snippet to get a reward without price changes. This is how we have done it for one of our custom promotions. The promotionprocessor inherits from `EntryPromotionProcessorBase<EmptyRewardPromotion>` and the GetRedemptions is not overridden from the base class.

protected override RewardDescription Evaluate(EmptyRewardPromotion promotionData, PromotionProcessorContext context)
            var lineItemsInOrder = context.OrderForm.GetAllLineItems().ToList();
            var codesInOrder = lineItemsInOrder.Select(s => s.Code);

            var applicableCodes = promotionData.VariationCodes.Where(s => codesInOrder.Contains(s)).ToList();
            if (!applicableCodes.Any())
                return NotFulfilledRewardDescription(promotionData, context, FulfillmentStatus.NotFulfilled);

            var redemptions = GetRedemptions(promotionData, context, applicableCodes);

            return new RewardDescription(FulfillmentStatus.Fulfilled, redemptions, promotionData, 0, 0M, RewardType.Money, "❤");
Feb 23, 2021 17:18

I generally agree with @Joel's solution. 

Just for the sake of other options...

1 - create a visitor group with the criteria "Product in Cart or Wish list" - Add your products SKU in VG

2 - Now create custom item level promotion (I think your processor inherit with EntryPromotionProcessorBase). Above VG group should be one of the qualifying criteria of your promotion.

3 - In RedemptionDescription Give a reward based on the item's weight

Feb 23, 2021 21:00

Hi Mauro,

I think there is another option for your business logic. You can inject a new implementation for EntryRewardApplicator, you can calculate discount amount as your expectation based on discount amount/discount percentage and weight by changing logic in ApplyDiscount/ApplyPercentage methods. By this way, you do not need to modify promotion proccessor and another customization

Feb 25, 2021 1:35
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.