I have found a couple of questions in the forum with similar challenges, for instance:
But none of them give (not to me at least) a straightforward solution to the problem. I'm also wondering if this can be handled by creating our own version of ILineItemCalculator where labor cost is excluded from promotion calculations somehow but included in other calculations of line item prices?
/Martin
Hi Martin
It's very common to implement your own LineItemCalculator in commerce solution. I don't know much of your commerce modelling, at this stage I suggest you do a quick POC by creating your own LineItemCalculator by inherting from DefaultLineItemCalculator, and override "CalculateDiscountedPrice" to set labor cost lineitem discount to zero to see if it works as you expected.
Hi Martin,
If you want to follow the approach that create a custom promotion for this one then I suggest that you can apply customization when returning reward description in Evaluate method of promotion processor like this
...
//Your business
IEnumerable<RedemptionDescription> redemptions = GetRedemptions(promotionData, context, applicableCodes);
//Add your business here to calculate discount
foreach (var redempt in redemptions)
{
var manualSavedAmount = 0;
foreach (var priceEntry in redempt.AffectedEntries.PriceEntries)
{
var discountAmount = 0;
//TODO: Add your business here to calculate discountAmount of each price entry based on line item that you can get from priceEntry.ParentItem
...
priceEntry.AdjustActualTotal(-discountAmount);
manualSavedAmount += discountAmount;
}
redempt.SavedAmount = manualSavedAmount;
}
return new RewardDescription(fulfillmentStatus, redemptions, promotionData, 0m, 0m, RewardType.Money, fulfillmentStatus.GetRewardDescriptionText(_localizationService));
Hope that it works with you
Hi Binh,
Thank you so much for your response! Unfortunately the suggested solution does not seem to work, at least I can't see any discount being applied. Also there is no public setter available for SavedAmount so this line does not work:
redempt.SavedAmount = manualSavedAmount;
Do you have a solution similar to this in any of your projects that works?
/Martin
Hi Martin,
Actually I did something like this when I worked with EpiServer Commerce 13 and it worked. But SavedAmount is private property so I must use Reflection to update it value. I thought this prop is public in new version 14 but not. Just try to use Reflection for it. There is only 1 way that I can say if we want to do it in promotion processor.
var savedAmountPropertyInfo = redempt.GetType().GetProperty("SavedAmount", BindingFlags.NonPublic | BindingFlags.Instance);
if (savedAmountPropertyInfo != null)
{
savedAmountPropertyInfo .SetValue(redempt, manualSavedAmount);
}
Don't think that will work since this is what SavedAmount looks like in the RedemptionDescription class:
/// <summary>Gets the amount saved by the redeemed promotion.</summary>
public Decimal SavedAmount
{
get
{
return this.AffectedObjects.Sum<IAffectedObject>((Func<IAffectedObject, Decimal>) (x => x.SavedAmount));
}
}
So even if it was possible to set the property using reflection I guess this code will still run when fetching the value for SavedAmount.
Seems to be possible to set SavedAmount on AffectedEntries like this:
((IAffectedObject)redempt.AffectedEntries).SavedAmount = manualSavedAmount;
but still I can't see any discount in the order management gui for instance.
Hi,
I may forgot some code that I did before.
Just try this one
...
//Your business
IEnumerable<RedemptionDescription> redemptions = GetRedemptions(promotionData, context, applicableCodes);
//Add your business here to calculate discount
foreach (var redempt in redemptions)
{
var manualSavedAmount = 0;
foreach (var priceEntry in redempt.AffectedEntries.PriceEntries)
{
var discountAmount = 0;
//TODO: Add your business here to calculate discountAmount of each price entry based on line item that you can get from priceEntry.ParentItem
...
priceEntry.Price = priceEntry.Price - discountAmount/priceEntry.Quantity;
manualSavedAmount += discountAmount;
}
redempt.AffectedEntries.SavedAmount = manualSavedAmount;
}
return new RewardDescription(fulfillmentStatus, redemptions, promotionData, 0m, 0m, RewardType.Money, fulfillmentStatus.GetRewardDescriptionText(_localizationService));
Change to set price for price entry instead
Hi Martin,
Does it work with you?
I corrected code a bit in my side and did testing in Foundation project and it works.
Here is the correct code
...
var i = 1;
foreach (var redempt in redemptions)
{
var manualSavedAmount = 0m;
foreach (var priceEntry in redempt.AffectedEntries.PriceEntries)
{
//Change by your business to calculate final price after discount
priceEntry.Price = priceEntry.Price * i * 0.1m;
manualSavedAmount += priceEntry.OriginalTotal - priceEntry.ActualTotal;
i++;
}
((IAffectedObject)redempt.AffectedEntries).SavedAmount = manualSavedAmount;
}
return new RewardDescription(fulfillmentStatus, redemptions, promotionData, 0m, 0m, RewardType.Money, fulfillmentStatus.GetRewardDescriptionText(_localizationService));
You can see result here
One item get discount 90% and one get discount 80% as my above example
Sorry about late reply, friday was a holiday (midsummers eve) here in Sweden so didn't work that day.
Yes that code actually seems to work quite ok, but I notice that for instance this list in Order management view for one of my carts show incorrect information under the header Discounts, do you see a similar thing in your Foundation-solution? Do you think there is a way to solve that?
Also the list of carts in Order management show a different total for the order than what shows when I open the cart. I'm just a bit worried that this might cause other side effects if the solution is not 100% working:
Hi Martin,
I saw same issue when testing in my side. Actually, there are some different logics between new version of commerce and old one version 13.x.
But I found a way to do custom for new version.
return new RewardDescription(reward.Status, reward.Redemptions, promotionData, 0m, 0m, RewardType.None, reward.Description);
public class CustomEntryRewardApplicator : EntryRewardApplicator
{
protected override decimal ApplyNotSpecified(AffectedEntries item, RewardDescription reward, PromotionProcessorContext processorContext)
{
processorContext.EntryPrices.CommitPrices(item);
return ((IAffectedObject)item).SavedAmount;
}
}
_services.AddSingleton<EntryRewardApplicator, CustomEntryRewardApplicator>();
Let's try to use it and do more test in your site to make sure it works completely.
At a first glance that seems to work just fine, hopefully further testing will not show any more issues.
Thanks a lot for your time and help!!
I'm trying to understand if it's possible to create a custom promotion (https://docs.developers.optimizely.com/customized-commerce/docs/custom-promotions) in Optimizely Commerce (version 14.22.0) where certain line items get a percentage discount (15%) and certain other line items get a calculated discount based on the price of the line item?
The problem comes from having packages which contain a variant and the price of the package also includes labor cost for installing the variant, which should not be touched by the percentage discount. However, if the variant itself is added to the cart it should get a 15% discount straight up.
Is this possible to handle in a way that some line items get 15% discount while for others we calculate the discount? Or is it possible to have a promotion where we calculate different discounts line item by line item?
I have to say that I find the documentation regarding custom promotions quite hard to follow so any help would be appreciated.