2

I just completed a migration from M1 to M2 version 2.3.5-p1.

All of the product and stock data was correctly migrated (I can see it in the database) but no matter what I do, products are all showing as "Out of Stock" on the frontend product pages.

This seems to be because the $_product->isAvailable() check always returns false in the file vendor/magento/module-catalog/view/frontend/templates/product/view/type/default.phtml.

However I triple checked all the product and inventory data in the database and everything (stock, associated products, etc.) are definitely correct for my products.

I have done all the usual: re-indexing, clearing cache, deleting folders, etc. etc.

I even made a quick external script to check the products were salable programatically, and they are:

<?php

// ---------------------
// Config
// ---------------------
$sku     = "SOMESKU123";
$storeId = 1;
// ---------------------

// Get Object Manager
require __DIR__ . "/../app/bootstrap.php";
\Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();

// Set Magento area to frontend
$objectManager->get("Magento\Framework\App\State")->setAreaCode("frontend");

// Ensure correct store view is set
$storeManager = $objectManager->get("Magento\Store\Model\StoreManagerInterface");
$storeManager->setCurrentStore($storeId);

// Get product data
$myHelper = $objectManager->get("MyModule\MyFunctions\Helper\Data");
$product  = $objectManager->get("\Magento\Catalog\Model\ProductFactory")->create()->loadByAttribute("sku", $sku);

echo "\n Magento area code: " . $objectManager->get("\Magento\Framework\App\State")->getAreaCode();
echo "\n Product Name: "      . $product->getName();
echo "\n isSalable: "         . $product->isSalable();
echo "\n isAvailable: "       . $product->isAvailable();
echo "\n isInStock: "         . $product->isInStock();

The output of the above script when I run it on the same Magento 2 instance:

Area code: frontend
Name: Whatever Product
isSalable: 1
isAvailable: 1
isInStock: 1

But when I load the same product on the "real" Magento 2 frontend, it shows as Out of Stock and isAvailable() returns false.

Why would this happen?

Update

I've done more debugging. It turns out there are many files involved in displaying the stock status on the product page:

vendor\magento\module-catalog\view\frontend\templates\product\view\type\default.phtml
vendor\magento\module-catalog\Model\Product.php
vendor\magento\module-configurable-product\Model\Product\Type\Configurable.php
vendor\magento\module-catalog\Model\Product\Type\AbstractType.php
vendor\magento\module-catalog\Model\ProductRender.php
vendor\magento\module-catalog-inventory\Observer\AddInventoryDataObserver.php
vendor\magento\module-catalog-inventory\Helper\Stock.php
vendor\magento\module-catalog-inventory\Model\StockRegistryStorage.php
vendor\magento\module-inventory-catalog\Plugin\CatalogInventory\Helper\Stock\AdaptAssignStatusToProductPlugin.php
vendor\magento\module-inventory-catalog\etc\di.xml
vendor\magento\framework\Interception\Interceptor.php
vendor\magento\module-inventory-catalog\Plugin\InventorySalesApi\StockResolver\AdaptStockResolverToAdminWebsitePlugin.php
vendor\magento\module-inventory-configurable-product\etc\frontend\di.xml

However, I believe the problem is related to the assignStatusToProduct function, which is expanded upon by the aroundAssignStatusToProduct function inside vendor/magento/module-inventory-catalog/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php.

If I modify framework/Interception/Interceptor.php and add a hack to effectively skip the above plugin, the stock status starts to appear normally:

public function ___callParent($method, array $arguments)
{
    // Begin hack
    if ($method == "assignStatusToProduct") {
        return;
    }
    // End hack

    return parent::$method(...array_values($arguments));
}

Obviously this is not a real solution. I don't fully understand why this plugin or function would result in an incorrect stock status, but from what I've read it might be related to 2.3's new Multi Source Inventory functionality so I will try disabling those inventory modules next and see what happens.

Also, this is a wild guess, but the reason this issue only happens on the Magento frontend (and not via my PHP script above) might be because this function is inserted via dependency injection (module-inventory-catalog\etc\di.xml) which might not be injected when I call ObjectManager directly in my script.

Any suggestions are appreciated.

8
  • After migration, may be your products are not assigned to the website. Try on your test product, open product on admin and check if it is assigned on website in "Product in Websites" tab of edit product Jun 17 '20 at 5:31
  • @ShoaibMunir Thank you for the suggestion - I checked and they are assigned OK.
    – WackGet
    Jun 17 '20 at 14:06
  • Hi @WackGet can you tell me the magento version you are using ? Jun 17 '20 at 16:32
  • @Kumar Version 2.3.5-p1. I'm reading about the new MSI (multi source inventory) functionality which I suspect might be related to the issue.
    – WackGet
    Jun 17 '20 at 17:45
  • yes it is related , thats why i asked you about the version . The product will not displayed properly untill multisource inventory is set . Jun 18 '20 at 6:54
3

I'm posting this as an answer even though it's not the answer, but rather a workaround.

Looks like Multi Source Inventory, introduced in Magento 2.3, causes the issue. You can see details of my debugging attempts in my question update, above, but in summary: disabling the MSI modules stopped the issue.

I disabled them using a command from this answer:

php bin/magento module:status | grep Magento_Inventory | grep -v List | grep -v None | grep -v -e '^$' | xargs php bin/magento module:disable

After doing this (and setup:upgrade etc.) the products show normally.

Interestingly, Magento's official documentation suggests ...you may need to disable Inventory Management modules to speed up the upgrade process for merchants migrating....

I didn't investigate further because I'm happy to disable MSI, but I suspect the root cause is either:

  • Perhaps my products weren't correctly assigned to a MSI stock source; or
  • Perhaps a default source didn't exist at all (you can check via Stores > Inventory > Source); or
  • Perhaps MSI doesn't like the way my products were configured (configurable SKU with Manage Stock: Yes, Stock Status: In Stock, Qty: 0 plus associated child products with positive quantities).
  • Perhaps there's a bug in MSI (all I know is that isSalable was being set to 0 in the aroundAssignStatusToProduct plugin as I mention in the OP)

The following also helped me:

3
  • Just want to add a comment here : dependencies of modules here (not preferences) might stop from disabling all Magento_Inventory modules at once. For me I had to disable Magento_InventoryRequisitionList then run the same command again for succesful disabling of these modules. Also, optionally - php bin/magento module:disable --clear-static-content at the end to clear static content for these modules (if any). Jun 19 '20 at 9:53
  • I want to notify you that you should make sure ALL your configurables work - in my case: I disabled all modules like above, instead of out of stock; the configs now show the dropdowns, but there are no simple products in the dropdown. In my case this doesn't help me. Jun 19 '20 at 12:24
  • 1
    @CvRChameleon I had the same issue but it was because I'd accidentally messed up some of my M1 data before migrating; I think I'd cleared the link tables and/or removed one of my super attributes. In any case, installing a fresh copy of M2, disabling the MSI modules, then migrating again did the trick.
    – WackGet
    Jun 20 '20 at 1:50
2

I am assuming this is related to multi-source inventory that has been added to the recent Magento version, to check if your product has been assigned to multisource inventory correctly you can go to the backend and have a look at this.

enter image description here

you can check by disabling the multisource inventory to check if that solves your issue, you can always enable it if it does not.

php bin/magento module:disable –f Magento_Inventory Magento_InventoryAdminUi Magento_InventoryApi Magento_InventoryBundleProduct Magento_InventoryBundleProductAdminUi Magento_InventoryCatalog Magento_InventorySales Magento_InventoryCatalogAdminUi Magento_InventoryCatalogApi Magento_InventoryCatalogSearch Magento_InventoryConfigurableProduct Magento_InventoryConfigurableProductAdminUi Magento_InventoryConfigurableProductIndexer Magento_InventoryConfiguration Magento_InventoryConfigurationApi Magento_InventoryGroupedProduct Magento_InventoryGroupedProductAdminUi Magento_InventoryGroupedProductIndexer Magento_InventoryImportExport Magento_InventoryIndexer Magento_InventoryLowQuantityNotification Magento_InventoryLowQuantityNotificationAdminUi Magento_InventoryLowQuantityNotificationApi Magento_InventoryMultiDimensionalIndexerApi Magento_InventoryProductAlert Magento_InventoryReservations Magento_InventoryReservationsApi Magento_InventoryCache Magento_InventorySalesAdminUi Magento_InventorySalesApi Magento_InventorySalesFrontendUi Magento_InventoryShipping Magento_InventorySourceDeductionApi Magento_InventorySourceSelection Magento_InventorySourceSelectionApi Magento_InventoryShippingAdminUi Magento_InventoryDistanceBasedSourceSelectionAdminUi Magento_InventoryDistanceBasedSourceSelectionApi Magento_InventoryElasticsearch Magento_InventoryExportStockApi Magento_InventoryReservationCli Magento_InventoryExportStock Magento_InventorySetupFixtureGenerator Magento_InventoryDistanceBasedSourceSelection

you can get more information about disabling it from here link

or you can assign your products to multisource inventory using the below code this will be needed to do for all products

  $product_id = $product->getIdBySku("pbnddee");
  $_sourceItemsSaveInterface = $objectManager->get('Magento\InventoryApi\Api\SourceItemsSaveInterface');
  $_sourceItemFactory = $objectManager->create('Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory');
  $sourceItem = $_sourceItemFactory->create();
  $sourceItem->setSourceCode("default");
  $sourceItem->setSku(pbnddee);
  $sourceItem->setQuantity(10);
  $sourceItem->setStatus(1);
  $_sourceItemsSaveInterface->execute([$sourceItem]);

hope my answer would help

5
  • May you advise why anyone should disable the new CatalogInventory modules? Arent these things there to improve on older m2 tech? I have this exact same issue - out of stock products, and absolutely nothing works. Do you have any links or information specifying what this module does / or breaks? Jun 18 '20 at 8:44
  • 1
    @CvRChameleon MSI is more of an additional feature rather than a direct replacement. Magento even states in their documentation you may need to disable it.
    – WackGet
    Jun 19 '20 at 1:03
  • I read the link that you sent, as usual Magento documentation is all over the place, and easily missed. Thanks! Still do not understand why they build a huge new thing that isn't at least somewhat backward compatible... Jun 19 '20 at 10:02
  • @CvRChameleon , I disabled this msi module because the client did not have multiple source for its inventory ..so we disabled it . moreover this msi feature introduced some regarding inventory calculation which was highly undesirable for client business logic . github.com/magento/inventory/pull/2452 Jun 19 '20 at 10:12
  • 1
    @Kumar, "regarding inventory calculation which was highly undesirable for client business logic", that's my point as well - how and why do they create a new inventory system that are currently so contradicting than useful for business use, it's rather confusing and frustrating. Jun 19 '20 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.