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.
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:
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.
In order to replace or customize an existing workflow, the basic steps you need to perform are:
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>
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.
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).
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.
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.
Add a reference to the CommerceLib project. Also add a reference to the Common.Logging and Mediachase.MetaDataPlus dlls in the Components folder.
Build the project.
Locate the newly built assembly and copy it to the bin folder of the project.
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="" />
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; } }
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