VirtoCommerce/vc-storefront-deprecated

Inventory In Only the First Fulfillment Center is Accounted For

Closed this issue · 3 comments

vc-storefront/VirtoCommerce.Storefront/Services/CatalogSearchServiceImpl.cs

protected virtual async Task LoadProductInventoriesAsync(List<Product> products)
{
   var inventories = await _inventoryModuleApi.InventoryModule.GetProductsInventoriesAsync(products.Select(x => x.Id).ToList());

   foreach (var item in products)
   {
       item.Inventory = inventories.Where(x => x.ProductId == item.Id).Select(x => x.ToInventory()).FirstOrDefault();
    }
}

This function is assuming there are no other fulfillment centers to worry about.

Options

  • Add new property to return a collection of Inventory objects. (My Preference, continue to populate existing property for backwards compatibility.)
protected virtual async Task LoadProductInventoriesAsync(List<Product> products)
{
   var inventories = await _inventoryModuleApi.InventoryModule.GetProductsInventoriesAsync(products.Select(x => x.Id).ToList());

   foreach (var item in products)
   {
       item.Inventory = inventories.Where(x => x.ProductId == item.Id).Select(x => x.ToInventory()).FirstOrDefault();
       item.InventoryAll = inventories.Where(x => x.ProductId == item.Id).Select(x => x.ToInventory()).ToList();
    }
}

  • Merge multiple returned inventory objects into one. (The object has a property for the fulfillment center so this would not be a great way)

Yes, you can make these changes in your storefront code. But I would to suggest use for this separated partial Product class with your custom namespace and inherit CatalogSearchServiceImpl implementation by you own type with override LoadProductInventories method contains you mentioned above implementation.

Some like this:

/MyExtension/Product.cs

partial class Product
{
ICollection<Inventory> InventoryAll { get; set; }
}

/MyExtension/MyCatalogSearchServiceImpl.cs

public class MyCatalogSearchServiceImpl : CatalogSearchServiceImpl
{
   public CatalogSearchServiceImpl(
            Func<WorkContext> workContextFactory,
            ICatalogModuleApiClient catalogModuleApi,
            IInventoryModuleApiClient inventoryModuleApi,
            ISearchApiModuleApiClient searchApi,
            IPricingService pricingService,
            ICustomerService customerService,
            ISubscriptionModuleApiClient subscriptionApi,
            IProductAvailabilityService productAvailabilityService)
:base(workContextFactory, catalogModuleApi, inventoryModuleApi, searchApi, customerService, subscriptionApi, productAvailabilityService)
{
}
 protected override Task LoadProductInventoriesAsync(List<Product> products)
{
  var inventories = await _inventoryModuleApi.InventoryModule.GetProductsInventoriesAsync(products.Select(x => x.Id).ToList());

   foreach (var item in products)
   {
       item.Inventory = inventories.Where(x => x.ProductId == item.Id).Select(x => x.ToInventory()).FirstOrDefault();
       item.InventoryAll = inventories.Where(x => x.ProductId == item.Id).Select(x => x.ToInventory()).ToList();
    }
}

}

If you setup more than one fulfillment center and have stock for a product in only one of the fulfillment centers the current code will always show the product as having no stock if the fulfillment center with stock is not the first in the list due to this code.

This is a bug no?

I appreciate the reminder to use a partial class if I want to create some custom code but in this case the issue is a bug no?

Yes it looking as omission, just the current storefront code is not designed to work with multiple fulfillment centers and do not realize any logic for better inventory choose.
I have transformed this issue into a task for improvement, where we will try to implement a more correct logic for inventory calculating.