Compare between prices for an item with applicable promotions

Vote:
 

Hi,

I am using Commerce 12.15, I have a requirment where I need to compare between the sale price and the promotion price (All lineitem promotions only). The logic is as follows:

An item 001 has a Regular price $10.00, the sale price is $6.00. Now they want me to apply the discount on the regular price and see if it is even lower than the current sale price.

I am able to check if the line item has an applicable promotion lower than the sale price, but I am not sure where I need to add the logic to find the lowest price should be inserted. 

My implementation so far is as follows:

I am overriding UpdatePlacedPrice(ILineItem lineItem, CustomerContact customerContact, MarketId market, Currency currency, Action<ILineItem, ValidationIssue> onValidationError) from DefaultPlacedPriceProcessor class.

There I get the current shopping cart and use the lineitems in the cart to create a InMemomryOrderGroup and apply the coupon. This gives me the collection of rewards, which I can use to compare against the sale price. I have created 3 metafields on the line item level as well, Original Price ex: $10.00, SalePrice: $6.00 and $PromoPrice $xx.xx. Now I want to check the lowest price between SalePrice and PromoPrice. But the promotions engine is always discounting on the placed price, so this leads to a double discount $6.00 being further discounted. I know this is the default behaviour of EPi, but I am looking for ideas or suggestions to overcome this. 

Below is my sample code :

                        if (cart.GetFirstForm().Promotions.Any())
                        {
                            PromotionInformation promo = cart.GetFirstForm().Promotions.Where(x => x.DiscountType == DiscountType.LineItem).FirstOrDefault();
                            if (promo != null)
                            {
                                var inMemoryOrderGroup = new InMemoryOrderGroup(currentMarket, ServiceLocator.Current.GetInstance<CurrencyService>().GetCurrentCurrency().CurrencyCode);
                                inMemoryOrderGroup.AddLineItem(lineItem);

                                if (!string.IsNullOrEmpty(promo.CouponCode))
                                {
                                    inMemoryOrderGroup.GetFirstForm().CouponCodes.Add(promo.CouponCode);      
                                }

                                var rewards = ServiceLocator.Current.GetInstance<IPromotionEngine>().Run(inMemoryOrderGroup);

                                if (rewards.Any())
                                {
                                    RewardDescription reward = rewards.OrderByDescending(x => x.SavedAmount).FirstOrDefault();
                                    decimal promoFinalPrice = reward.Redemptions.FirstOrDefault().AffectedEntries.PriceEntries.FirstOrDefault().Price;
                                    lineItem.Properties[Constants.Metadata.LineItem.PromoPrice] = promoFinalPrice;
                                    lineItem.Properties[Constants.Metadata.LineItem.SalePrice] = placedPrice.Value.Amount;
                                    //If promo price is greater than sale price, pass the regular price to be used by the promotions engine
                                    if (promoFinalPrice < highestDiscount)
                                    {
                                        lineItem.PlacedPrice = regPrice.UnitPrice.Amount;
                                    }
                                    else
                                    {
                                        lineItem.PlacedPrice = placedPrice.Value.Amount;
                                    }
                                  }
                               }
                           }
                    
#203120
Edited, Apr 09, 2019 18:06
Vote:
 

I think we have 2 ways to do that:

1. Update line item price: By this way, we need to do some below steps when validating cart:

  • Run placed price processor to update latest price in case of changing price
  • Calling Apply discounts for whole order
  • Update placed price again based on comparing applied discount price to sale price. We only need to get discount amounts to compare, no need to run promotion anymore. However, we also need to clear applied discounts if we choose sale price instead of applying discount price  

2. Update discount amount: By this way, we won't change placed price in every line item. We will only re-update line item discount amount if discount amount > Max discount amount (Regular Price - Sale Price). Here is steps that we need to do when validating cart:

  • Run placed price processor to update latest price in case of changing price
  • Calling Apply discounts for whole order
  • Comparing applied discount price to sale price. If Sale price is lower than discount price then update line item discount amount = Max discount amount (Regular Price - Sale Price). 

That is my ideas, the option 2 is more simple I think.

#203135
Apr 10, 2019 5:38
Vote:
 

@Binh Nguyen: Thank you so much for the idea, I ended up creating a cart extension method to do the check and update the placed price. But it only works fine if I have a single SKU in the cart. Upon having more than 1 SKU it double discounts all line items other than the last one which is very strange.

            if (cart.GetFirstForm().Promotions.Any())
            {
                var currentMarket = ServiceLocator.Current.GetInstance<ICurrentMarket>().GetCurrentMarket();
                //Look for a line item promo
                PromotionInformation promo = cart.GetFirstForm().Promotions.Where(x => x.DiscountType == DiscountType.LineItem).FirstOrDefault();
                if (promo != null)
                {
                    //Create an in memory order group and see the discount
                    var inMemoryOrderGroup = new InMemoryOrderGroup(currentMarket, ServiceLocator.Current.GetInstance<CurrencyService>().GetCurrentCurrency().CurrencyCode);

                    foreach (ILineItem lineItem in cart.GetFirstForm().GetAllLineItems())
                    {
                        EntryContentBase entry = lineItem.GetEntryContent(ServiceLocator.Current.GetInstance<ReferenceConverter>(), ServiceLocator.Current.GetInstance<IContentLoader>());

                        decimal currentPlacedPrice = placedPriceUpdater.GetPlacedPrice(entry, lineItem.Quantity, ServiceLocator.Current.GetInstance<CurrentCustomerProfile>().GetCurrentCustomer(), currentMarket.MarketId, ServiceLocator.Current.GetInstance<CurrencyService>().GetCurrentCurrency()).Value.Amount;
                        //Passing regular price into the promotion engine
                        lineItem.PlacedPrice = Convert.ToDecimal(lineItem.Properties[Constants.Metadata.LineItem.ListPrice]);
                        inMemoryOrderGroup.AddLineItem(lineItem);

                        if (!string.IsNullOrEmpty(promo.CouponCode))
                        {
                            inMemoryOrderGroup.GetFirstForm().CouponCodes.Add(promo.CouponCode);
                        }

                        var rewards = ServiceLocator.Current.GetInstance<IPromotionEngine>().Run(inMemoryOrderGroup);

                        if (rewards.Any())
                        {
                            RewardDescription reward = rewards.OrderByDescending(x => x.SavedAmount).FirstOrDefault();
                            decimal promoFinalPrice = reward.Redemptions.FirstOrDefault().AffectedEntries.PriceEntries.Where(x => x.OriginalTotal == Convert.ToDecimal(lineItem.Properties[Constants.Metadata.LineItem.ListPrice])).FirstOrDefault().Price;


                            if (promoFinalPrice < currentPlacedPrice)
                            {
                                lineItem.PlacedPrice = Convert.ToDecimal(lineItem.Properties[Constants.Metadata.LineItem.ListPrice]);
                            }
                            else
                            {
                                lineItem.PlacedPrice = currentPlacedPrice;
                                lineItem.TrySetDiscountValue(x => x.EntryAmount, 0.0M);
                                var entryPromo = promo.Entries.Where(x => x.EntryCode == lineItem.Code).FirstOrDefault();
                                entryPromo.SavedAmount = 0.0M;
                            }
                        }
                        else
                        {
                            lineItem.PlacedPrice = currentPlacedPrice;
                        }
                    }
                }
                
            }

Here is an sample: https://www.screencast.com/t/ZMaw4YOJSj

The customer disocunt was 20% and the Epi coupon was 15%. In the image, you can see that all line items are being double discounted other than the last line item. I also had a look at the LineItem table and I see that LineItemDiscountAmount is populated with $6.66 for Item 1 and $4.32 for Item 2.

#203192
Edited, Apr 11, 2019 2:08
Vote:
 

The problem here is you ran promotion in the loop. For every iterator, you added more 1 line item and get all discounts for all items in the memory order. So  the discounts retrieved may be not proper if you don't have all line items in the order. 

#203193
Apr 11, 2019 3:46
Vote:
 

Binh Nguyen: Thank you so much! :)

#203194
Apr 11, 2019 4:18