This content is archived. See latest version here

Last updated: Nov 12 2014

CheckInventory activity

Introduction

During checkout, Microsoft Workflow Foundation workflows are used to check remaining item stock quantity, and calculate the cart totals. EPiServer Commerce includes a checking inventory activity that is incorporated into the workflows. This will check the remaining warehouse inventory when adding or changing an item in a cart.

Classes referred to here are available in the following namespaces:

Key classes and files

  • CartValidateWorkflow.xoml - calculates shopping cart totals, including discounts, and validates if a product is available.
  • CartPrepareWorkflow.xoml - associates line items with shipments in the OrderForm object, calculates shipment prices, tax totals, and order totals.
       

How it works

The CartPrepare workflow is run prior to rendering the page where the customer confirms the order.

This workflow performs the following tasks:

  • Determines whether the item is still available, based on the Active status and the start and end dates associated with each item. If they are not available, they are removed from the cart and an error message is returned which is displayed in the Cart view page.
  • Determines whether the items in the cart are still available based on remaining stock in inventory, reserved inventory stock, and whether backordering is permitted. If the item is not available, it is removed and an error message is returned.
  • Calculates the price for each item in the cart, based on tiered pricing. If pricing has changed, a message regarding the change is returned which can be displayed to the customer.
  • Calculates the extended price for an item in the cart, for instance multiplying the price by the quantity purchased.
  • Calculates the discounts that apply to the items in the cart.
  • Splits the line items into their respective shipment, if multiple shipments are created.
  • Adds shipping costs to the cart.
  • Adds applicable taxes to the cart.

The determination of whether the items in the cart are still available, is done inside the CheckInventoryActivity activity.

Example: the CheckInventoryActivity activity

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Workflow.ComponentModel;
using Mediachase.Commerce.Orders;

namespace Mediachase.Commerce.Workflow.Activities
{
	public partial class CheckInventoryActivity : OrderGroupActivityBase
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="CheckInventoryActivity"/> class.
		/// </summary>
		public CheckInventoryActivity()
		{
			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();

				// Calculate order discounts
				this.ValidateItems();

				// 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;
			}
		}

		private void ValidateItems()
		{
			//We don't need to validate quantity in the wishlist
			var orderForms = OrderGroup.OrderForms.ToArray();
			var lineItems = orderForms.SelectMany(x => x.LineItems.ToArray());
			var validLineItems = lineItems.Where(x => x.Code != "0" && !String.IsNullOrEmpty(x.Code) && !x.Code.StartsWith("@"));
			foreach (LineItem lineItem in validLineItems)
			{
				List<string> changeQtyReason = new List<string>();
                
                decimal newQty;
                if (lineItem.IsInventoryAllocated)
                {
                    newQty = lineItem.Quantity;
                }
                else
                {
                    newQty = GetNewLineItemQty(lineItem, changeQtyReason, null);
                }

				var changeQtyReasonDisplay = String.Join(" and ", changeQtyReason.ToArray());
				if (newQty == 0)
				{
					// Remove item if it reached this stage
                    Warnings.Add("LineItemRemoved-" + lineItem.LineItemId.ToString(), String.Format("Item \"{0}\" has been removed from the cart because it is no longer available or there is not enough available quantity.", lineItem.DisplayName));
					DeleteLineItemFromShipments(lineItem);
					// Delete item
					lineItem.Delete();
				}
				else
				{
					var delta = lineItem.Quantity - newQty;
					if (delta != 0)
					{
						lineItem.Quantity -= delta;
						ChangeShipmentsLineItemQty(lineItem, delta);
                        Warnings.Add("LineItemQtyChanged-" + lineItem.LineItemId.ToString(),
									 String.Format("Item \"{0}\" quantity has been changed {1}", lineItem.DisplayName, changeQtyReasonDisplay));
					}
				}
			}

            //delete shipment if it has no item.
            var shipments = orderForms.SelectMany(of => of.Shipments.ToArray());
            foreach (Shipment shipment in shipments)
            {
                if (shipment.LineItemIndexes.Length == 0)
                {
                    CancelOperationKeys(shipment);
                    shipment.Delete();
                }
            }
         }

        private void DeleteLineItemFromShipments(LineItem lineItem)
		{
			var orderForm = OrderGroup.OrderForms.ToArray().FirstOrDefault();
			if (orderForm != null)
			{
                int lineItemIndex = orderForm.LineItems.IndexOf(lineItem);
                var allShipmentContainsLineItem = orderForm.Shipments.ToArray().Where(x => Shipment.GetShipmentLineItems(x).Contains(lineItem));
                foreach (var shipment in allShipmentContainsLineItem)
                {
                    shipment.RemoveLineItemIndex(lineItemIndex);
                }

                ReorderIndexes(orderForm, lineItem);
			}
		}
		private void ChangeShipmentsLineItemQty(LineItem lineItem, decimal delta)
		{
			var orderForm = OrderGroup.OrderForms.ToArray().FirstOrDefault();
			if (orderForm != null)
			{
				var lineItemIndex = orderForm.LineItems.IndexOf(lineItem);
				var allShipmentContainsLineItem = orderForm.Shipments.ToArray().Where(x => Shipment.GetShipmentLineItems(x).Contains(lineItem));
				foreach (var shipment in allShipmentContainsLineItem)
				{
					//Decrease qty in all shipment contains line item
					var shipmentQty = Shipment.GetLineItemQuantity(shipment, lineItem.LineItemId);
					var newShipmentQty = shipmentQty - delta;
					newShipmentQty = newShipmentQty > 0 ? newShipmentQty : 0;
					//Set new line item qty in shipment
					shipment.SetLineItemQuantity(lineItemIndex, newShipmentQty);
					delta -= Math.Min(delta, shipmentQty);

					if (delta == 0)
						break;
				}
			}
		}
	}
}

Customizing the CheckInventory activity

If you wish to customize the checking inventory, you will need to create your own workflow which mirrors the CartPrepareWorkflow workflow, and substitutes the CheckInventoryActivity activity with your own implementation. Refer to Customizing order processing workflow for more information.

See also


Do you have feedback on this documentation? Send an email to documentation@episerver.com. For development-related questions and discussions, refer to our Forums on https://world.episerver.com/forum/