Try our conversational search powered by Generative AI!

Multiple reward descriptions from one promotion processor

Vote:
 

Hello, 
I need to have different discounts applied to lineitems based on various rules. However, it seems that promotion processor allows only one reward description to be created. Is there any provisions to have multiple?


For example, I want lineItem1 to get 10% discount and lineitem2 to get 20% percent discount. 
I cannot use multiple promotion processors, because these percentages are dynamic. 

What is the best way to do this?


Regards,

#184691
Nov 02, 2017 11:03
Vote:
 

I'm looking for a ways to do this, and fouhnd that you can add manual fromotion as described in this thread:

https://world.episerver.com/forum/developer-forum/Episerver-Commerce/Thread-Container/2017/4/how-to-add-manual-order-promotion/

private static void AddDiscount(
    IOrderForm orderForm,
    string lineItemCode,
    decimal discountValue,
    string discountName)
{
    var promotionInformation = new PromotionInformation
    {
        Name = discountName,
        Description = discountName,
        DiscountType = EPiServer.Commerce.Marketing.DiscountType.Manual,
        Entries = new List<PromotionInformationEntry>
        {
            new PromotionInformationEntry
            {
                EntryCode = lineItemCode,
                SavedAmount = discountValue
            }
        },
        OrderForm = new PromotionInformationOrderForm
        {
            SavedAmount = 0
        }
    };
    orderForm.Promotions.Add(promotionInformation);
}

However, this doesn't seem to do anything in version I use (9.24). What could be wrong?

#184759
Nov 03, 2017 7:39
Vote:
 

Are you running _promotionEngine.Run after you've added your manual promotion to the orderform?

#184760
Nov 03, 2017 8:38
Vote:
 

Currenly, I'm adding this inside PromotionProcessor as a workaround to have multiple discounts from one promotion. But it doesn't seem to work.

#184761
Nov 03, 2017 8:44
Vote:
 

Or actually it seems that you need to run cart.ApplyDiscounts(). From what I can see, _promotionEngine.Run doesn't care about manual discounts in the orderForm.Promotions, but if you run cart.ApplyDiscounts() it will apply them as well.

cart.ApplyDiscounts() will in itself run _promotionEngine.Run and you can provide the PromotionEngineSettings to ApplyDiscounts as well.

#184762
Nov 03, 2017 8:44
Vote:
 

Ah, no, then I see why it wouldn't work. PromotionInformation is just a describing class of a seemingly applied promotion. But if you want to add a manual promotion by first creating the PromotionInformation, you still need to actually do something to apply them. By themselves PromotionInformations do nothing.

Therefore I'm not sure you can actually set your manual PromotionInformation from inside a processor. Since the promotion engine runs the processor, you have gone beyond the point where it would take care of manual promotions. So it would never be applied price wise (to the discountedPrice on any lineitem, where the actual discount values are stored after being calculated by the whole promotion engine schbang).

The flow as I see it (haven't tested it, just theorizing):

  1. Create PromotionInformation with type Manual
  2. Add them to the orderform by form.Promotions.Add
  3. Run IOrderGroupExtensions.ApplyDiscounts, that in turn will
    1. Store away the manual discounts existing on all the orderforms
    2. Runs promotionEngine.Run, which itself will remove all the Promotions from the orderform (because they are to be re-evaluated)
    3. Take the stored manual discounts and apply them

So creating PromotionInformations outside of this flow won't impact the actual prices.

#184764
Nov 03, 2017 8:54
Vote:
 

That seems wrong, because we wanted to use new promotion engine specifically to avoid such mess. It looked very promising until we stumbled upon this inability to apply multiple discounts.


Also, I looked at Episrever code and your solution also look as if it wont work. ApplyDiscounts look like this:

    public static IEnumerable<RewardDescription> ApplyDiscounts(this IOrderGroup orderGroup, IPromotionEngine promotionEngine, PromotionEngineSettings settings)
    {
      return promotionEngine.Run(orderGroup, settings);
    }


Regards,

#184765
Nov 03, 2017 9:07
Vote:
 

Ah, perhaps I'm actually talking about something that was implemented after 9.24 :( 

ApplyDiscounts in 11.2 looks like this:

So I guess somewhere along the line of 9.24 to 11.2 manual discounts were implemented in the new promotion system. Or there is some other way of enabling/using it.

#184766
Nov 03, 2017 9:12
Vote:
 

Yes, it seems to be different in the version I use. However,  this still would be no help to me because I need this discount to be proceesed before some others. 

#184770
Nov 03, 2017 10:38
Vote:
 

Hi Joel

I tried the above logic by adding AddDiscount method to my Custom Promotion Processor, and then in the processor, i'm trying not to calculate discount, so that its only handled by ApplyDiscounts method. However, my items are getting double discounts. Plus, each refresh of the cart applies the double discounts again, until the total price on line item becomes 0.

Also, i've added logic in CanBefulfilled method to not apply discounts when condition product isn't in cart and that part works. But once i have it in cart and my discount applies, if i remove the condition item, the discount still stays. It shouldn't. How and where can i handle this if I'm using above ApplyDiscounts logic?

#185648
Nov 28, 2017 16:44
Vote:
 

Hi, 

Which ApplyDiscounts logic are you talking about? ApplyDiscounts is just an extension method to IOrderGroup that will run _promotionEngine.Run().

What I'm saying in the above answer is that the use case Giedrius is talking about cannot be solved with Manual Promotions. And in order to run manual promotions you need to run cart.ApplyDiscounts instead of _promotionEngine.Run because it's in that method it will take into consideration the manual promotions existing on the orderform :)

I'm not sure where the problems you're having are originating, it would help if you pasted your processor code here so I can take a look at what you are doing.

#185654
Nov 29, 2017 2:37
Vote:
 

Hi Joel

What I implemented is the AddDiscount method posted by Giedrius, in my processor logic, so i can set different promotion for different line item on the OrderForm.Promotions, as manual DiscountType. Once i've done this for all my reward scenarios, i still have to return a RewardDescription object on the Evaluate method. There, i'm not passing any redemptions or discount amount to calculate discounts in the processor. The only useful thing i'm returning in RewardDescription is the FulfillmentStatus.Fulfilled. After doing so, the extension method ApplyDiscounts automatically pulls those promotions and applies them to cart, which works for my requirement. But, i have some issues with the way its working:

  1. When I pass saved amount after calculating a percentage discount, it gets applied twice, and keeps getting applied on each refresh of cart. This is not OK.
  2. When I pass saved amount as a fixed amount discount, it gets applied once and doesn't duplicate on each refresh. This is OK.
  3. I have logic under CanBeFulfilled method to look for my condition items in order, before applying discount. So if my condition item isn't there, no reward will be applied. This is OK.
  4. But, if i have reward applied and then i take the condition item out of cart, the rewards should go away too, but they stay. This is not OK. Where should I add logic so it re-evaluates the promotion when an item is removed from cart?

This si my processor logic :

protected override RewardDescription Evaluate(MultipleDiscountPromotion promotionData, PromotionProcessorContext context)
        {    
            //first reward
            var condition1 = promotionData.Reward1.PromoItems;
            SetupPerReward(condition1, "Reward1", promotionData, context, promotionData.Reward1);

            //second reward
            var condition2 = promotionData.Reward2.PromoItems;
            SetupPerReward(condition2, "Reward2", promotionData, context, promotionData.Reward2);

            //third reward
            var condition3 = promotionData.Reward3.PromoItems;
            SetupPerReward(condition3, "Reward3", promotionData, context, promotionData.Reward3);

            var reward = this.CreateRewardDescription(promotionData, null, FulfillmentStatus.Fulfilled);

            return reward;
            
        }

        private void SetupPerReward(IList<ContentReference> targetItems, string rewardName, MultipleDiscountPromotion promotionData, PromotionProcessorContext context, CustomRewardBlock reward)
        {
            IOrderForm orderForm = context.OrderForm;
            var lineItems = GetLineItems(orderForm);
            var condition = targetItems;   
            var applicableCodes = _targetEvaluator.GetApplicableCodes(lineItems, condition, true);
            var affectedEntries = context.EntryPrices.ExtractEntries(applicableCodes, lineItems.Sum(l => l.Quantity) + 1);
            IEnumerable<RedemptionDescription> redemptions = this.GetRedemptions(applicableCodes, promotionData, context);
            foreach (var redemption in redemptions)
            {
                foreach (var item in redemption.AffectedEntries.PriceEntries)
                {
                    string lineItemCode = item.ParentItem.Code;
                    decimal price = item.Price;
                    decimal discount = item.Price * (reward.Discount / 100);
                    AddDiscount(orderForm, lineItemCode, discount, rewardName);

                }
            }
        }
private void AddDiscount(IOrderForm orderForm, string lineItemCode, decimal discountValue, string discountName)
        {
            bool IsNewPromo = true;
            foreach(var promo in orderForm.Promotions)
            {
                if (promo.Name.Equals(discountName))
                {
                    IsNewPromo = false;
                    break;
                }
            }

            if (IsNewPromo)
            {
                var promotionInformation = new PromotionInformation
                {
                    Name = discountName,
                    Description = discountName,
                    DiscountType = DiscountType.Manual,
                    Entries = new List<PromotionInformationEntry>
                    {
                        new PromotionInformationEntry
                        {
                            EntryCode = lineItemCode,
                            SavedAmount = discountValue
                        }
                    },
                    OrderForm = new PromotionInformationOrderForm
                    {
                        SavedAmount = 0
                    },
                    IsRedeemed = true
                };
                orderForm.Promotions.Add(promotionInformation);
            }
        }
protected override bool CanBeFulfilled(MultipleDiscountPromotion promotionData, PromotionProcessorContext context)
        {
            if (promotionData.Reward1.Discount <= 0 && promotionData.Reward2.Discount <= 0 && promotionData.Reward3.Discount <= 0)
            {
                return false;
            }

            IOrderForm orderForm = context.OrderForm;
            var lineItems = GetLineItems(orderForm);
            var conditionItems = promotionData.ConditionItems;
            var applicableCodes = _targetEvaluator.GetApplicableCodes(lineItems, conditionItems, true);
            if(applicableCodes.Count <= 0)
            {
                return false;
            }
           
            if (!lineItems.Any())
            {
                return false;
            }
            return true;
        }



#185673
Edited, Nov 29, 2017 15:21
Vote:
 

Hey, 

So my response would be quite long, I recorded it instead :D 

I fucked up the sound a bit, so it's very low. So up the volume and enable subtitles!

https://www.youtube.com/watch?v=bBXowqDZrGA

#185677
Edited, Nov 29, 2017 18:00
Vote:
 

Hi Joel

I understand what you tried to explain, but the code that I shared actually does apply discounts in the cart. It shows saved amount next to each line item. the only issue i have is it doubles the saved amount than what it actually should be, and every postback keeps adding that double discount to each line item. My understanding is this happens because of the no. of time the Evaluate method is called by the PromotionEngine.. and it seems like multiple times, because when i put a breakpoint on the Evaluate method, it gets hit atleast 3 times on one postback. If this is true to how this functions, why is that?

And maybe i might have to jump back to my original post, because ultimately my goal is to be able to return multiple reward descriptions, pertaining to different line items and i don't see such an option on PromotionProcessor. There is however a bunch of overloaded Evaluate methods on IPromotionEngine interface, that return an IEnumerable<RewardDescription>. Is there a way to override these, so we can add the custom logic for multiple rewards in single promotion inside it? If so, how and where can we do that?

#185678
Nov 29, 2017 18:33
Vote:
 

Interesting. I would be interested to see why it actually works... But regardless, Evaluate will be called once per promotion that can be applied. Do you maybe have several of that custom promotion set up?

If no, could you print screen the stack trace for each of the 3 times you hit Evaluate in that custom promotion?

#185695
Nov 30, 2017 2:49
* 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.