Proper use of Promotions BETA

Member since: 2012

I am trying to use some of the new BETA features and have some questions how to properly use it. I am trying new Promotions and created my own promotion implementation like in this article: http://www.david-tec.com/2015/07/creating-a-custom-promotion-with-the-new-episerver-commerce-9-promotion-engine-beta---part-1/

There is a small difference in IPromotionResult implementation's ApplyReward method I made - I am not modifying anything, but returning reward I want to be applied to whole order (in the sample I skipped discount calculation and use constant):

public IEnumerable<PromotionInformation> ApplyReward()
{
	if (Status != FulfillmentStatus.Fulfilled || OrderGroup == null)
	{
		return new[] {NoReward()};
	}

	return new []
	{
		new PromotionInformation
		{
			Description = Description,
			IsActive = true,
			SavedAmount = 10
		}, 
	};
}

I assumed that consumer of this method will apply SavedAmount to OrderGroup (Cart), but I am stuck to achieve it.

In the controller I tried to call IPromotionEngine's method Run, I tried to call Cart validate workflow, tried to save Cart with IOrderRepository, tried to save Cart just with Cart.AcceptChanges(), but my promotion is not applied.

This works partially:

_promotionEngine.Run(cart);
_orderRepository.Save(cart);

It runs my ApplyReward method, but result is not used. I also see that after _promotionEngine.Run(cart) new promotion is added to ((IOrderGroup)cart).Promotions collection, but _orderRepository.Save(cart) seems not saving items in this collection. After loading cart again ((IOrderGroup)cart).Promotions collection is empty.

So the questions:

1. How to apply discount returned by ApplyReward or should ApplyReward update order?

2. How to save ((IOrderGroup)cart).Promotions that those doesn't disappear?

#143293 Jan 19, 2016 15:50
  • Member since: 2011

    We are currently reworking the processor so it will work as you expect when returning the saved amount the engine will modify the dicscount of the ILineItem for you.  This should be released in a couple weeks time.

    1. In the version you are wroking with you will need to set the ILineItem.LineItemDiscountAmount or IlineItem.OrderLevelDiscountAmount manually.

    2. You need to enable VNextWorkflows to save PromotionInformation right now.  http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Commerce/9/Marketing/promotions-and-workflow-activities-beta/

    #143308 Jan 20, 2016 1:17
  • Member since: 2012

    Mark, thanks for the answer, but I have more questions :)

    1. Why order level discount should be applied to LineItem? And which line item should I use - first or it doesn't matter?

    2. I have VNextWorkflows enabled (using code in initializable module). Should I call Cart Validate workflow or there are some new workflows to run?

    #143312 Jan 20, 2016 6:44
  • Member since: 2011

    1.  Depends on the type of promotion.  Order level promotions are stored with the distributed amount per item in OrderLevelDiscountAmount in order to be able process returns.  For example $100 of your order would be would be $50 OrderLevelDiscount if you had two items that cost $100 each.  Again we are changing this in the interface for promotion processors so you wont need to know these details just the amount you want to save.  Also in Evaluate you should determine which lineitems matched in the promotion.  Apply reward is supposed to use the items that were matched in evaluate.  You also want to pass in the ContentLink to PromotionInformation so you know which sku was discounted for historical puprposes.

    2.  In your initalization module have a [ModuleDependency(typeof(Mediachase.Commerce.Initialization.CommerceInitialization))]  Then just Addfeautre and EnableFeature no need to initalize.  I will update the docuementation.

    #143316 Jan 20, 2016 10:07
  • Member since: 2012

    1. Thanks, this helps to understand the reason behind line item's OrderLevelDiscountAmount. But when I use IOrderFormCalculator's GetSubTotal() method, it calculates SubTotal taking OrderLevelDiscountAmount into account which is not right. SubTotal should be sum before applying OrderLevelDiscountAmount and Total should include OrderLevelDiscountAmount.

    About ContentLink in the PromotionInformation - if I skip it I get Null reference exception when workflow runs, so I have to provide it always.

    2. Now I use config file to enable new features and it works well.

    Now I changed ApplyReward to this and it works:

    public IEnumerable<PromotionInformation> ApplyReward()
    {
    	if (Status != FulfillmentStatus.Fulfilled 
    		|| OrderGroup == null
    		|| !HasLineItems())
    	{
    		return new[] {NoReward()};
    	}
    
    	var totalDiscount = CalculateDiscountAmount();
    
    	var lineItems = OrderGroup.Forms.First().Shipments.First().LineItems;
    	var lineItemDiscount = totalDiscount/lineItems.Count;
    
    	lineItems.ToList().ForEach(x => x.OrderLevelDiscountAmount = lineItemDiscount);
    
    	return lineItems.Select(x => new PromotionInformation
    	{
    		Description = Description,
    		IsActive = true,
    		SavedAmount = lineItemDiscount,
    		ContentLink = _referenceConverter.GetContentLink(x.Code)
    	});
    }

    In the CalculateDiscountAmount() method I have to manually calculate line item total before discounts:

    private decimal CalculateDiscountAmount()
    {
    	var orderForm = OrderGroup.Forms.First();
    	if (!orderForm.Shipments.Any())
    	{
    		return 0;
    	}
    	var itemTotal = orderForm.Shipments.First().LineItems.Sum(x => x.PlacedPrice*x.Quantity);
    	var totalDiscount = itemTotal*(DiscountPercent/100M);
    	return totalDiscount;
    }

    It would be nice if IOrderFormCalculator would contain method which would calculate line item sum before any discounts and also fix GetSubTotal() as mentioned in 1..

    While everything seems to work, there is one issue I found. When there is only one line item and I am trying to remove it I get this exception:

    The provided content link does not have a value.
    Parameter name: contentLink
    
    [ArgumentNullException: The provided content link does not have a value.
    Parameter name: contentLink]
       EPiServer.Core.DefaultContentLoader.Get(ContentReference contentLink, LoaderOptions loaderOptions) +502
       Mediachase.Commerce.Workflow.Activities.CalculateDiscountsVNextActivity.Execute(ActivityExecutionContext executionContext) +314
       Mediachase.Commerce.WorkflowCompatibility.Activity.Execute() +40
       Mediachase.Commerce.Engine.<>c__DisplayClass1`1.<Do>b__0() +314
       Mediachase.Commerce.Engine.ActivityFlowRunner.Execute() +124
       Mediachase.Commerce.Engine.ExecutionManager.ExecuteActivityFlow(String name, ActivityFlowContext context) +275

    It seems that one Promotion in the cart has empty content link now. I am removing line item from cart like this:

    var lineItem = cart.GetLineItem(code);
    PurchaseOrderManager.RemoveLineItemFromOrder(cart, lineItem.LineItemId);

    Is it the right way to remove line item so that promotions will work?

    #143322 Edited, Jan 20, 2016 11:13
  • Member since: 2011

    We will look into IOrderFormCalculator.GetSubTotal it should be total before discounts, tax, handling etc.

    Yes that is the correct way to remove the lineItem.  It may be you need to save the cart for now to get past the error.  We will look into this as well.

    Thanks for the feedback 

    #143324 Jan 20, 2016 11:41
  • Member since: 2012

    Just tried to save cart before running workflow and it didn't help. Still same error. Tried to save Cart with OrderRepository and also with cart.AcceptChanges() and no difference.

    #143325 Jan 20, 2016 11:49
  • Member since: 2011

    Can you check the PromotionInfromation table I think there might be some orphaned records in there that has no contentLink

    #143326 Jan 20, 2016 11:53
  • Member since: 2012

    I deleted the cart in Commerce Manager and started again. PromotionInformation table was empty. Added new item to the cart and applied discount. One record appeared in PromotionInformation table and it has valid ContentReference. Now I am trying to remove item and exception still appears. PromotionInformation table still have same record with valid ContentReference.

    #143327 Jan 20, 2016 11:59
  • Member since: 2011

    Okay, I will file a bug.  PurchaseOrderManager.RemoveLineItemFromOrder should remove the promotionInformation for the remove lineitem.  To work around I suggest you remove the PromotionInformation manually for removed LineItem

    #143328 Jan 20, 2016 12:03
  • Member since: 2012

    I am trying to remove PromotionInformation, but it seems to not work at least as I am doing it.

    So I tried to remove it like this:

    PurchaseOrderManager.RemoveLineItemFromOrder(cart, lineItem.LineItemId);
    var lineItemLink = _referenceConverter.GetContentLink(code);
    var orderGroup = (IOrderGroup) cart;
    var promotion = orderGroup.Promotions.FirstOrDefault(x => x.ContentLink.Equals(lineItemLink, true));
    if (promotion != null)
    {
    	orderGroup.Promotions.Remove(promotion);
    	cart.PromotionInformationRepository.Service.Save(orderGroup.Promotions, cart.OrderGroupId);
    }

    It finds promotion and removes from Promotions collection, but after save it doesn't remove promotions from database. I also tried to save Cart itself, but it didn't help.

    #143329 Edited, Jan 20, 2016 12:23
First   1 2   Last