HomeGuides
Submit Documentation FeedbackJoin Developer CommunityLog In

AdjustInventory activity

This topic describes how to re-calculate and adjust warehouse inventories after placing an order.

During checkout, activity flows process payments, adjust item stock quantity, and record promotion usage. Optimizely Commerce includes an adjust inventory activity that is incorporated into activity flows. The activity re-calculates and adjusts warehouse inventories after placing an order.

Classes in this topic are available in the following namespace:

  • Mediachase.Commerce.Workflow.Activities.Cart. Contains AdjustInventoryActivity.

How it works

The CartCheckout activity flow is executed when a cart is submitted for processing during order placement. The flow performs these tasks:

  • If the Process payment flag is true, calls the ProcessPayment method associated with the cart's payment providers.
  • Calculates totals for the cart based on line item price, item quantity, shipping totals, handling totals, and taxes. OrderForm, Cart, and LineItem properties are updated for totals.
  • If inventory tracking is enabled, the SKU inventory is adjusted after purchase.
  • Saves promotion usage data to the PromotionUsage table, where it tracks each promotion entry for enforcement of promotion redemption limits.

The AdjustInventory activity recalculates and adjusts the stock quantity of the cart's items.

Example: the AdjustInventoryActivity activity

using EPiServer.Commerce.Internal;
    using EPiServer.Commerce.Order;
    using EPiServer.Logging;
    using EPiServer.ServiceLocation;
    using Mediachase.Commerce.Inventory;
    using Mediachase.Commerce.InventoryService;
    using Mediachase.Commerce.Orders;
    using Mediachase.Commerce.Orders.Managers;
    using Mediachase.Commerce.WorkflowCompatibility;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Mediachase.Commerce.Workflow.Activities.Cart
      {
        public class AdjustInventoryActivity : CartActivityBase
          {
            private Injected OrderRepository;
    
            ///
            /// 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)
              {
                // Check for multiple warehouses. In the default, we simply reject processing an order if the application has
                //  multiple warehouses. Any corresponding fulfillment process is the responsibility of the client.
                CheckMultiWarehouse();
    
                // Validate the properties at runtime
                ValidateRuntime();
    
                // Return close status if order group is Payment Plan
                if (OrderGroup is PaymentPlan)
                  {
                    return ActivityExecutionStatus.Closed;
                  }
    
                AdjustInventoryOrRemoveLineItems();
    
                // Retun the closed status indicating that this activity is complete.
                return ActivityExecutionStatus.Closed;
              }
    
            private void AdjustInventoryOrRemoveLineItems()
              {
                var orderGroup = OrderGroup as IOrderGroup;
                var validationIssues = new Dictionary<ILineItem, IList>();
    
                RemoveEmptyShipment(orderGroup);
    
                //This is needed for backward compatibility
                foreach (var lineItem in orderGroup.GetAllLineItems())
                  {
                    if (!InventoryTrackingEnabled(lineItem))
                      {
                        Warnings[$"InventoryNotRequested:{lineItem.Code}"] = "Inventory tracking is disabled.";
                      }
                  }
    
                orderGroup.AdjustInventoryOrRemoveLineItems((item, issue) => AddValidationIssues(validationIssues, item, issue));
    
                //This is needed for backward compatibility
                foreach (var validationIssue in validationIssues)
                  {
                    var lineItem = validationIssue.Key;
    
                    foreach (var issue in validationIssue.Value)
                      {
                        switch (issue)
                          {
                            case ValidationIssue.RemovedDueToInsufficientQuantityInInventory:
                                Warnings.Add($"LineItemRemoved-{lineItem.LineItemId.ToString()}", $"Item \"{lineItem.DisplayName}\" has been removed from the cart because there is not enough available quantity.");
                                break;
                            case ValidationIssue.RejectedInventoryRequestDueToInsufficientQuantity:
                                Warnings[OrderGroupWorkflowManager.RejectedInventoryDueToInsufficientQuantityWarning] = "Can not request inventory due to not enough quantity.";
                                Warnings[OrderGroupWorkflowManager.RejectedInventoryDueToInsufficientQuantityWarning + ":" + lineItem.Code] = "Can not request inventory due to not enough quantity.";
                                break;
                            default:
                                break;
                          }
                      }
                  }
    
                RemoveEmptyShipment(orderGroup);
              }
    
            private void RemoveEmptyShipment(IOrderGroup orderGroup)
              {
                //Remove empty shipment
                var deletedShipments = new List();
    
                foreach (var shipment in orderGroup.Forms.SelectMany(form => form.Shipments))
                  {
                    if (!shipment.LineItems.Any())
                      {
                        deletedShipments.Add(shipment);
                      }
                  }
    
                if (!deletedShipments.Any())
                  {
                    return;
                  }
    
                foreach (var shipment in deletedShipments)
                  {
                    var form = orderGroup.Forms.FirstOrDefault(f => f.Shipments.Contains(shipment));
                    // Keep at least one shipment in the order form.
                    if (form.Shipments.Count > 1)
                      {
                        form.Shipments.Remove(shipment);
    
                        InventoryRequester.CancelReservedQuantityForShipment((IShipmentInventory) shipment);
                      }
                  }
    
                OrderRepository.Service.Save(orderGroup);
              }
    
            private void CheckMultiWarehouse()
              {
                var warehouses = WarehouseRepository.List().Where(w => w.IsActive && w.IsFulfillmentCenter);
                if (warehouses.Count() > 1)
                  {
                    throw new NotSupportedException("Multiple fulfillment centers without custom fulfillment process.");
                  }
              }
          } 
      }

Example: AdjustInventoryOrRemoveLineItems

/// Adjusts the inventory.
    ///
    ///The order group.
    ///A callback that is invoked if a validation issue is detected.
    ///The inventory processor.
    public static void AdjustInventoryOrRemoveLineItems(this 
      IOrderGroup orderGroup, 
      Action<ILineItem,
      ValidationIssue> onValidationError,
      IInventoryProcessor inventoryProcessor)
      {
        foreach (var shipment in orderGroup.Forms.SelectMany(form => form.Shipments))
          {
            inventoryProcessor.AdjustInventoryOrRemoveLineItem(shipment, orderGroup.OrderStatus, onValidationError);
          }
      }

Customizing the AdjustInventory activity

To customize the AdjustInventory activity, create an activity flow that mirrors the CartCheckout activity flow, and substitute the AdjustInventoryActivity with your implementation.


Did this page help you?