You are here: Order System > Customizing Order Processing Workflow

Customizing Order Processing Workflow

Introduction

Checkout activities like calculating cart totals, updating inventory, and processing payments are handled outside of the core API using Windows Workflow Foundation workflows. Each workflow contains a number of activities, each with a distinct purpose such as calculating discounts; the activities may be re-used among workflows. This division allows for greater flexibility and easier business rules management. The Windows Workflow Foundation technology (WF) is shipped with the .NET Framework. If you want to change the business rules associated with checkout, EPiServer Commerce allows you to incorporate your own workflows.

Key Files and Controls

Workflows

CartValidateWorkflow - This workflow is executed every time the cart view page is loaded. It is called in the CartViewModule.ascx control, which renders the cart. It uses the following activities, in order:

CartPrepareWorkflow - This workflow is executed just prior to completing the order. It performs all of the calculations necessary to ensure the cart includes all relevent discounts and ancillary costs (e.g. taxes, shipping charges). It uses the following activities, in order:

CartCheckoutWorkflow - This workflow is executed upon submission of the cart for processing. It uses the following activities, in order:

Activities

AdjustInventoryActivity - If Inventory tracking is enabled, will adjust SKU inventory after purchase.

CalculateDiscountsActivity - Calculates the discounts associated with each lineitem, shipment discounts, and order discounts. Discounts are saved in line item Discounts, Shipment's Discounts collection, and OrderForm Discounts collection.

CalculateTaxActivity - Calculates tax associated with order. For more information on this, see the Taxes section.

CalculateTotalsActivity - Calculates total for cart based on lineitem price, quantity of each lineitem, shipping totals, handling totals, and taxes. OrderForm, Cart, and lineitem properties regarding totals are updated.

CheckInventoryActivity - If inventory tracking is enabled, determines whether sufficient stock is on hand for each lineitem. If not, quantities of SKUs greater than stock are removed and warnings returned to indicate the cart change.

ProcessHandlingActivity - No implementation is included.

ProcessPaymentActivity - Calls the ProcessPayment method associated with the payment providers associated with the cart.

ProcessShipmentsActivity - Determines the shipping rates associated with an order by calling the GetRate method associated with each Shipment of line items in a Cart.

RecordPromotionUsageActivity - Saves promotion usage data to the PromotionUsage table, where its used to track each promotion entry for enforcement of promotion redemption limits.

RemoveDiscountsActivity - Empties the discount collections associated with LineItem, Shipment, and OrderForm instances.

ShipmentSplitActivity - Associates items in cart with the shipments they've been added to previously. List of items in each Shipment are stored in LineItemIndexes field.

ValidateLineItemsActivity - Transfers catalog entry inventory properties to each LineItem, returns an error message if the price for a lineitem has changed. returns an error message if the quantity of a LineItem is reduced due to an inventory shortage, and removes SKUs (with an error message returned) that are no longer active or are members of inactive catalogs.

Customizing a Workflow

In order to replace or customize an existing workflow, the basic steps you need to perform are:

  1. Create and build a new sequential workflow in a Visual Studio project and add existing or new activities to a workflow.
  2. Update the ecf.workflow.config file to use the new workflow assembly.

Configuration

Workflow configuration is found in \Configs\ecf.workflow.config. The default content of ecf.workflow.config is below:

<?xml version="1.0"?>
 
<Workflow>
 
  <Workflows>
 
     <add name="CartValidate" displayname="Validate Cart" description="Checks inventory, populates extra fields" type="Mediachase.Commerce.Workflow.CartValidateWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
 
     <add name="CartPrepare" displayname="Cart Prepare" description="Creates shipments" type="Mediachase.Commerce.Workflow.CartPrepareWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
 
     <add name="CartCheckout" displayname="Cart Checkout" description="Processes credit card information" type="Mediachase.Commerce.Workflow.CartCheckoutWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
 
 
     <\!-\- Administration Workflows / mapped to toolbar actions \-->
 
     <add name="cmdPurchaseOrderCalculateTotals" displayname="Calculate Totals" description="Calculates totals" type="Mediachase.Commerce.Workflow.Admin.POCalculateTotalsWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
 
     <add name="cmdPurchaseOrderProcessPayment" displayname="Process Payment" description="Process Payment" type="Mediachase.Commerce.Workflow.Admin.POProcessPaymentWorkflow, Mediachase.Commerce.Workflow" xomlpath="" rulespath=""/>
 
  </Workflows>
</Workflow>

Modification of the Checkout Workflow - New Activity to Send Notifications

In the following example, we create a new workflow activity and then utilize it in a new workflow. We then use this workflow to replace the shipped CartCheckout workflow. The new activity sends an automated email to store customers after an order completion. The main aim of this example is to illustrate how a workflow can be updated and customized.

Create a New Activity

  1. Create a Workflow Activity Library project in Visual Studio, in this example we have named the project CustomActivityLibrary1.
  2. Add a new activity to the activities project for the email. Name the Activity SendNotification.
  3. Set the inheritence for the activity to System.Workflow.ComponentModel.Activity .
  4. Add references to the CommerceLib project and Mediachase.MetaDataPlus dll in the Components folder.
  5. Add the code into the new activity's code-behind file (SendNotification.cs) from the Code Sample section in this document. Ensure the naming is correct and that you create the stub of the method to send the email and that your method is called in the activity Execute method. A stub for the method is provided in the SendEmailNotification() method. See the SendEmails method code in the PublicLayer/SDKs/B2CSampleSite/Templates/Everything/BusinessControls/CheckoutControls/CheckoutWizardModule.cs file.
    Note that you should use the activity OrderGroup object reference to retrieve the information about the order that you want to put in the email. To generate the formatted copy of the email body text with the order information, use the Mediachase.Commerce.Engine.Template.TemplateService. Documentation for how to setup and use this template service can be found here: Sending Order Notifications.
  6. Build the project.
  7. Ensure that the activity has been added to the Visual Studio Toolbox. For information about this see the article on MSDN: How to: Add Activities to the Toolbox on MSDN: http://msdn.microsoft.com/en-us/library/dd797579.aspx

Add the New Activity to a Workflow

Here we shall add the activity (created above) along with other activities contained in the Mediachase.Commerce.Workflow.Activities.dll in a new workflow. We will then use this workflow to replace the CartCheckout workflow (Mediachase.Commerce.Workflow.CartCheckoutWorkflow).

  1. Create a Workflow project (.NET 3.5, blank project) in Visual Studio, in this example we name the project MyCustomCartCheckout.
  2. Add a sequential workflow to the project customCartCheckout - in this example we name the workflow customCartCheckout.
  3. Here we re-create the original CartCheckout Workflow and add the SendNotification activity created above. From the Toolbox, drag and drop activities onto the workflow design area in the following sequence:
    1. ProcessPaymentActivity
    2. AdjustInventoryActivity
    3. RecordPromotionUsageActivity
    4. SendNotification
  4. In the workflow code-behind file (in this example named customCartCheckout.cs), add the properties OrderGroup and Warnings. See the relevant code in the code sample section in this document.

  5. For each activity in the design view of the workflow, set the OrderGroup and Warning properties to the workflow OrderGroup and Warning properties. When you click on each activity in the workflow design view, you see the option to set this in the properties window.

  6. Add a reference to the CommerceLib project. Also add a reference to the Common.Logging and Mediachase.MetaDataPlus dlls in the Components folder.

  7. Build the project.

  8. Locate the newly built assembly and copy it to the bin folder of the project.

  9. Change the Configs\ecf.workflow.config file to point to the new assembly (workflow) which will handle the CartCheckout process. Update the CartCheckout element.

    Configuration for this example:
    <add name="CartCheckout" displayname="Cart Checkout" description="Processes credit card information" type="MyCustomCartCheckout.customCartCheckout, MyCustomCartCheckout" xomlpath="" rulespath="" />

  10. Finally - recompile, reset IIS, and access the website where this activity is used during checkout.

Code samples

Here's the starting code for the SendNotification activity.

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
 
using Mediachase.Commerce.Orders;
using Mediachase.Commerce.Catalog;
using Mediachase.Commerce.Catalog.Dto;
using Mediachase.Commerce.Orders.Managers;
using Mediachase.Commerce.Catalog.Managers;
 
namespace CustomActivity
{
    public partial class SendNotification: Activity
    {
        public static DependencyProperty OrderGroupProperty = DependencyProperty.Register("OrderGroup", typeof(OrderGroup), typeof(SendNotification));
 
        /// <summary>
        /// Gets or sets the order group.
        /// </summary>
        /// <value>The order group.</value>
        [ValidationOption(ValidationOption.Required)]
        [BrowsableAttribute(true)]
        public OrderGroup OrderGroup
        {
            get
            {
                return (OrderGroup)(base.GetValue(SendNotification.OrderGroupProperty));
            }
            set
            {
                base.SetValue(SendNotification.OrderGroupProperty, value);
            }
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="CalculateTaxActivity"/> class.
        /// </summary>
        public SendNotification()
        {
            InitializeComponent();
        }
 
        /// <summary>
        /// Called by the workflow runtime to execute an activity.
        /// </summary>
        /// <param name="executionContext">The <see cref="T:System.Workflow.ComponentModel.ActivityExecutionContext"/> to associate with this <see cref="T:System.Workflow.ComponentModel.Activity"/> and execution.</param>
        /// <returns>
        /// The <see cref="T:System.Workflow.ComponentModel.ActivityExecutionStatus"/> of the run task, which determines whether the activity remains in the executing state, or transitions to the closed state.
        /// </returns>
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            try
            {
                // Validate the properties at runtime
                this.ValidateRuntime();
 
                //Send notification
                this.SendEmailNotification();
 
                // Retun the closed status indicating that this activity is complete.
                return ActivityExecutionStatus.Closed;
            }
            catch
            {
                // An unhandled exception occured.  Throw it back to the WorkflowRuntime.
                throw;
            }
        }
 
        /// <summary>
        /// Calculates the sale taxes.
        /// </summary>
        private void SendEmailNotification()
        {
            //provide implementation here
        }
 
        /// <summary>
        /// Validates the runtime.
        /// </summary>
        /// <returns></returns>
        private bool ValidateRuntime()
        {
            // Create a new collection for storing the validation errors
            ValidationErrorCollection validationErrors = new ValidationErrorCollection();
 
            // Validate the Order Properties
            this.ValidateOrderProperties(validationErrors);
 
            // Raise an exception if we have ValidationErrors
            if (validationErrors.HasErrors)
            {
                string validationErrorsMessage = String.Empty;
 
                foreach (ValidationError error in validationErrors)
                {
                    validationErrorsMessage +=
                        string.Format("Validation Error:  Number {0} - '{1}' \n",
                        error.ErrorNumber, error.ErrorText);
                }
 
                // Throw a new exception with the validation errors.
                throw new WorkflowValidationFailedException(validationErrorsMessage, validationErrors);
 
            }
 
 
            // If we made it this far, then the data must be valid.
            return true;
        }
 
        private void ValidateOrderProperties(ValidationErrorCollection validationErrors)
        {
            // Validate the To property
            if (this.OrderGroup == null)
            {
                ValidationError validationError = ValidationError.GetNotSetValidationError(SendNotification.OrderGroupProperty.Name);
                validationErrors.Add(validationError);
            }
        }
    }
}

 

Here are the properties to be added to the workflow .cs file.

private OrderGroup _OrderGroup;
 
        /// <summary>
        /// Gets or sets the order group.
        /// </summary>
        /// <value>The order group.</value>
        public OrderGroup OrderGroup
        {
            get
            {
                return _OrderGroup;
            }
            set
            {
                _OrderGroup = value;
            }
        }
 
        private StringDictionary _Warnings = new StringDictionary();
        /// <summary>
        /// Gets the warnings.
        /// </summary>
        /// <value>The warnings.</value>
        public StringDictionary Warnings
        {
            get
            {
                return _Warnings;
            }
        }

Namespaces

Mediachase.Commerce.Workflow - contains the workflows

Mediachase.Commerce.Workflow.Activities.Cart - contains the activities

 


Version: EPiServer Commerce 1 R2 SP2| Last updated: 2012-06-29 | Copyright © EPiServer AB | Send feedback to us