Hide menu Last updated: Oct 12 2015

During checkout, Activity Flows check remaining item stock quantities and calculate cart totals. Episerver Commerce includes a check inventory activity that is incorporated into the activity flows. This activity checks the warehouse inventory when adding or changing a cart item.

Classes in this topic are available in the following namespaces:

How it works

The CartPrepare activity flow is run prior to rendering the page where the customer confirms the order. This flow performs the following tasks:

  • Determines if the item is available, based on the Active status and the item's start and end dates. If an item is not available, it is removed from the cart, and an error message appears on the Cart view page.
  • Determines if cart items 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 error message appears on the Cart view page.
  • Calculates the price of each cart item, 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 of a cart item, for instance multiplying the price by the quantity purchased.
  • Calculates discounts that apply to cart items.
  • Splits line items into respective shipping addresses, if multiple shipments are created.
  • Adds shipping costs to the cart.
  • Adds applicable taxes to the cart.

The CheckInventoryActivity determines whether cart items are available.

Example: the CheckInventoryActivity activity

C#

using Mediachase.Commerce.Orders;
using Mediachase.Commerce.WorkflowCompatibility;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Mediachase.Commerce.Workflow.Activities
{
    public class CheckInventoryActivity : OrderGroupActivityBase
    {
        ///
        /// Called by the workflow runtime to execute an activity.
        ///
        ///The  to associate with this  and execution.
        /// 
        /// The  of the run task, which determines whether the activity remains in the executing state, or transitions to the closed state.
        /// 
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            try
            {
                // Validate the properties at runtime
                this.ValidateRuntime();

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

        /// 
        /// Validate inventory in the order group.
        /// 
        /// We don't need to validate quantity in the wishlist.
        private void ValidateItems()
        {
            if (string.Equals(OrderGroup.Name, Mediachase.Commerce.Orders.Cart.WishListName, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            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 changeQtyReason = new List();

                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

To customize the inventory checking activity, create an activity flow which mirrors the CartPrepareActivityFlow and substitutes the CheckInventoryActivity activity with your implementation.

Related topics

Comments