Sunday, June 22, 2014

Behind the scenes of "Release shipment" in EPiServer Commerce

Working with EPiServer Commerce, one thing you quickly realize is that you spend more time debugging than you do developing. And some of the bugs can be a real pain to figure out.

I ran into a bug last week where nothing happened when the customer clicked on "Release Shipment" on the order. They clicked the button, and not a thing happened.


The only error i found was a javascript error "Workflow generated an exception, please look at the previous error for more details". However, there was no "previous error", so I had no way of knowing what was failing and there was nothing in the logs...

Decompiling the Mediachase.Commerce assemblies, I found that the method called when you click "Release Shipment" is DoCommand(...) in Mediachase.Commerce.Manager.Order.CommandHandlers.PurchaseOrderHandlers.ReleaseShipmentHandler. This method validates a couple of things such as the authorized payment amount, that the item is in stock etc. The most important part, however, is that the method changes the status of the order to be "Released" by calling the ReleaseOrderShipment(...) method in Mediachase.Commerce.Orders.Managers. After the status is changed, the RecalculatePurchaseOrderWorkflow is called (Note: I've also seen this workflow being called by the name PurchaseOrderRecalculateWorkflow, just to make the confusion complete).

So what does the RecalculatePurchaseOrderWorkflow / PurchaseOrderRecalculateWorkflow do? If you're using the standard workflows, it contains the following activities:
  • ValidateLineItemsActivity
  • GetFulfillmentWarehouseActivity
  • CheckInventoryActivity
  • ProcessShipmentsActivity
  • RemoveDiscountsActivity
  • CalculateTotalsActivity
  • CalculateDiscountsActivity
  • CalculateTaxActivity
  • CalculateTotalsActivity
  • CalculatePurchaseOrderStatusActivity
In my case, the CalculateDiscountsActivity was missing a null check, and that was what caused all the trouble. How did I find out which activity was the troublemaker? I added logging to the catch block of the try/catch of all the Execute methods in the activites, and retrieved the error message from the log.

I cannot believe the amount of exceptions being swallowed by the Commerce workflows. Any developer working with Commerce projects should make sure they go through all the activities and add some logging. It will make your life a lot easier in the future!

Update: 
I've received notice that the CalculateDiscountActivity should only run when the order is a cart and not a purchase order. This of course makes sense as the payment has already begun processing for orders. The Execute method of your CalculateDiscountActivity should then look somewhat like this:
   protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            try
            {
                ValidateRuntime();

                if (OrderGroup is Orders.Cart) // Do not calculate discounts for Purchase Orders as the payment has already been done
                {
                    InitMarketingContext();
                    CalculateDiscounts();
                }
                return ActivityExecutionStatus.Closed;
            }
            catch (Exception e)
            {
                _logger.Error(e.Message, e);
                // An unhandled exception occured.  Throw it back to the WorkflowRuntime.
                throw;
            }
        }

No comments:

Post a Comment