There are two methods, in my opinion, of reading inventory information from an external source in Sitecore Experience Commerce, one is using a Minion or any other kind of scheduled job to import inventory information into Sitecore Commerce inventory sets and the other one is reading inventory information directly “live” from the external source. Deciding on whether to use one or the other one, depends on the project requirements. In this post we will see an example of how to implement the second method.
All the work needed to be done will be done in the Commerce Engine, so we can create a new Commerce Engine plugin for it or add this functionality in any custom plugin we already have. We are going to use the Commerce Engine sample solution that ships with Sitecore Experience Commerce installation and we are going to create a new plugin and call it: Sitecore.Commerce.Plugin.Sample.Inventory:

There are several pipelines and blocks in the Commerce Engine that work with inventory information, but in Sitecore Experience Commerce 9.2 there is also an InventoryCommander that is responsible for doing the reading operations and it is located in the OOTB Sitecore.Commerce.Plugin.Inventory. All we need to do is override the GetInventoryInformation method and we are done. Before that, let’s use a product as reference to see if the change we are going to perform will actually work, notice how the following product has 10000 units in inventory:

Let’s override the InventoryCommander:
using System;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.Plugin.Inventory;
using Sitecore.Commerce.Plugin.Sample.Inventory.Repositories;
using Sitecore.Framework.Conditions;
namespace Sitecore.Commerce.Plugin.Sample.Inventory.Commands
{
public class CustomInventoryCommander : InventoryCommander
{
private readonly IInventoryInformationRepository _inventoryInformationRepository;
public CustomInventoryCommander(IInventoryInformationRepository inventoryInformationRepository,
IServiceProvider serviceProvider) : base(serviceProvider)
{
_inventoryInformationRepository = inventoryInformationRepository;
}
public override Task<InventoryInformation> GetInventoryInformation(CommerceContext commerceContext,
SellableItemInventorySetArgument sellableItemInventorySetArgument,
bool fromContext = false)
{
Condition.Requires(commerceContext, nameof(commerceContext)).IsNotNull();
Condition.Requires(sellableItemInventorySetArgument, nameof(sellableItemInventorySetArgument)).IsNotNull();
var inventoryInformation =
_inventoryInformationRepository.GetInventoryInformation(sellableItemInventorySetArgument);
return Task.FromResult(inventoryInformation);
}
}
}
The code is self explanatory, we pretty much have created our new commander and named it CustomInventoryCommander, it inherits from InventoryCommander and overrides the GetInventoryInformation method. The GetInventoryInformation does a couple of null checks, and then uses the injected IInventoryInformationRepository to get the InventoryInformation object being requested. This repository will be the responsible for reading the inventory information from the external source. For the purpose of this post it is just a fake repository but in any real project it could read from a database, web service, file, etc.
This is how the repository interface looks like:
using Sitecore.Commerce.Plugin.Inventory;
namespace Sitecore.Commerce.Plugin.Sample.Inventory.Repositories
{
public interface IInventoryInformationRepository
{
InventoryInformation GetInventoryInformation(SellableItemInventorySetArgument sellableItemInventorySetArgument);
}
}
And this is the fake repository implementation:
using System;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.Plugin.Catalog;
using Sitecore.Commerce.Plugin.Inventory;
using Sitecore.Commerce.Plugin.ManagedLists;
namespace Sitecore.Commerce.Plugin.Sample.Inventory.Repositories
{
public class FakeInventoryInformationRepository : IInventoryInformationRepository
{
public InventoryInformation GetInventoryInformation(
SellableItemInventorySetArgument sellableItemInventorySetArgument)
{
var inventorySetId =
sellableItemInventorySetArgument.InventorySetId.Replace(CommerceEntity.IdPrefix<InventorySet>(),
string.Empty);
var sellableItemId =
sellableItemInventorySetArgument.SellableItemId.Replace(CommerceEntity.IdPrefix<SellableItem>(),
string.Empty);
var displayName = inventorySetId + "-" + sellableItemId + "-" +
sellableItemInventorySetArgument.VariationId;
var id = CommerceEntity.IdPrefix<InventorySet>() + displayName;
var inventoryInformation = new InventoryInformation
{
DateCreated = new DateTimeOffset(DateTime.Now),
DateUpdated = new DateTimeOffset(DateTime.Now),
InventorySet = new EntityReference(CommerceEntity.IdPrefix<InventorySet>() +
sellableItemInventorySetArgument.InventorySetId),
SellableItem = new EntityReference(CommerceEntity.IdPrefix<SellableItem>() +
sellableItemInventorySetArgument.SellableItemId),
VariationId = sellableItemInventorySetArgument.VariationId,
Quantity = 500,
InvoiceUnitPrice = new Money(0),
DisplayName = displayName,
FriendlyId = displayName,
Name = displayName,
Id = id,
Version = 1,
Published = true,
IsPersisted = true
};
inventoryInformation.GetComponent<BackorderableComponent>().Backorderable = false;
inventoryInformation.GetComponent<PreorderableComponent>().Preorderable = false;
inventoryInformation.GetComponent<ListMembershipsComponent>().Memberships.Add("InventoryInformations");
return inventoryInformation;
}
}
}
This fake repository is creating an InventoryInformation object, filling it with some fake data but it is using the proper values for the id, inventory set, sellable item and variant id, otherwise pipelines and blocks that uses the commander might not find it. Note in line 33 how we are setting the quantity to 500 units, this will help us to verify that it is working later on the product details page. As I said before, a real implementation of this repository would read from a database, file or web service, etc.
Next step is to register all this changes in the IoC container otherwise it will keep using the OOTB inventory commander, we do this in the plugin’s ConfigureSitecore class:
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.Plugin.Inventory;
using Sitecore.Commerce.Plugin.Sample.Inventory.Commands;
using Sitecore.Commerce.Plugin.Sample.Inventory.Repositories;
using Sitecore.Framework.Configuration;
namespace Sitecore.Commerce.Plugin.Sample.Inventory
{
public class ConfigureSitecore : IConfigureSitecore
{
public void ConfigureServices(IServiceCollection services)
{
var assembly = Assembly.GetExecutingAssembly();
services.RegisterAllPipelineBlocks(assembly);
var serviceDescriptor = services.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(InventoryCommander));
services.Remove(serviceDescriptor);
services.AddTransient<InventoryCommander, CustomInventoryCommander>();
services.AddTransient<IInventoryInformationRepository, FakeInventoryInformationRepository>();
services.RegisterAllCommands(assembly);
}
}
}
Note how we have removed the existing registration for the InventoryCommander and added the new one in lines 19-21, and we registered our repository in line 22.
After deploying the Commerce Engine we are ready to check that reference product again:

And now the product is displaying with 500 units as inventory.
As we could see, it is fairly easy the replace the inventory reading capabilities in Sitecore Experience Commerce 9.2. If you know of any other solution or recommendations, let me know in the comments.
Hope this helps!