Reading list Switch to dark mode

    Introduction to Magento 2 Multi Source Inventory (MSI)

    Updated 5 April 2023

    Introduction to Magento 2 Multi Source Inventory (MSI)

    What is MSIMagento 2 Multi Source Inventory (MSI) is newly introduced in Magento 2.3 version(upcoming release) which is used to manage inventory by source (location).

    Till now, Magento manages a single inventory system and due to this it’s really hard to manage multi-channel inventories from a single website, but using MSI extension, inventory will be managed by each channel. Also in¬†dropship¬†extension really hard to manage inventory for each Magento 2 warehouse but using MSI we can manage inventory for each warehouse.

    You can get the Magento 2.3 Multi Source Inventory (MSI) version from https://github.com/magento-engcom/msi.

    How to use Multi Source Inventory (MSI) in Magento 2 – For managing inventory in Magento 2.3 we will need to follow given below steps-

    Searching for an experienced
    Magento Company ?
    Find out More
    1. Manage Sources – If we are selling products from multi-location like Florida US, California US etc then we will need to create sources for each inventory location.image
    2. Manage Stocks – Here we will need to create stock for each channel (Magento Websites, if there are multi websites configure in your Magento) so that we can assign sources(from where we are selling our products) for each channel.imageimage
    3. Manage Catalog Product Inventory – When adding or editing a product we will need to assign sources to that product and for the source will need to add product available quantity for that source.image
    4. Order Management – When an order is placed for a product than using the source selection algorithm, ordered qty is reserved for available source inventory after shipment with available source ordered qty is deducted from that source.image

    How to programmatically create source and assign to stock in Magento 2 –

    /**
     * Webkul Software.
     *
     * @category  Webkul
     * @package   Webkul_CustomMsi
     * @author    Webkul
     * @copyright Copyright (c) Webkul Software Private Limited (https://webkul.com)
     * @license   https://store.webkul.com/license.html
     */
    namespace Webkul\CustomMsi\Helper;
    
    use Magento\InventoryApi\Api\Data\SourceInterface;
    use Magento\InventoryApi\Api\Data\SourceInterfaceFactory;
    use Magento\InventoryApi\Api\SourceRepositoryInterface;
    use Magento\InventoryAdminUi\Model\Source\SourceHydrator as InventorySourceHydrator;
    use Magento\InventoryApi\Api\Data\StockSourceLinkInterface;
    use Magento\Framework\Message\ManagerInterface;
    use Magento\Store\Model\StoreManagerInterface;
    use Magento\Framework\Api\SearchCriteriaBuilder;
    use Magento\InventoryApi\Api\GetStockSourceLinksInterface;
    use Magento\InventoryApi\Api\Data\StockSourceLinkInterfaceFactory;
    use Magento\Framework\Api\DataObjectHelper;
    use Magento\InventoryApi\Api\StockSourceLinksSaveInterface;
    
    class InventorySource extends \Magento\Framework\App\Helper\AbstractHelper
    {
        /**
         * @var SourceInterfaceFactory
         */
        private $sourceInterfaceFactory;
    
        /**
         * @var SourceRepositoryInterface
         */
        private $sourceRepositoryInterface;
    
        /**
         * @var InventorySourceHydrator
         */
        private $inventorySourceHydrator;
    
        /**
         * @var ManagerInterface
         */
        private $messageManager;
    
        /**
         * @var StoreManagerInterface
         */
        private $storeManager;
    
    
        /**
         * @var SearchCriteriaBuilder
         */
        private $searchCriteriaBuilder;
    
        /**
         * @var GetStockSourceLinksInterface
         */
        private $getStockSourceLinks;
    
        /**
         * @var StockSourceLinkInterfaceFactory
         */
        private $stockSourceLink;
    
        /**
         * @var DataObjectHelper
         */
        private $dataObject;
    
        /**
         * @var StockSourceLinksSaveInterface
         */
        private $stockSourceLinksSave;
    
        /**
         * @param \Magento\Framework\App\Helper\Context $context
         * @param SourceInterfaceFactory                $sourceInterfaceFactory
         * @param SourceRepositoryInterface             $sourceRepositoryInterface
         * @param InventorySourceHydrator               $inventorySourceHydrator
         * @param ManagerInterface                      $messageManager
         * @param \Magento\Framework\Event\Manager      $eventManager
         * @param StoreManagerInterface                 $storeManager
         * @param SearchCriteriaBuilder                 $searchCriteriaBuilder
         * @param GetStockSourceLinksInterface          $getStockSourceLinks
         * @param StockSourceLinkInterfaceFactory       $stockSourceLink
         * @param DataObjectHelper                      $dataObject
         * @param StockSourceLinksSaveInterface         $stockSourceLinksSave
         */
        public function __construct(
            \Magento\Framework\App\Helper\Context $context,
            SourceInterfaceFactory $sourceInterfaceFactory,
            SourceRepositoryInterface $sourceRepositoryInterface,
            InventorySourceHydrator $inventorySourceHydrator,
            ManagerInterface $messageManager,
            StoreManagerInterface $storeManager,
            SearchCriteriaBuilder $searchCriteriaBuilder,
            GetStockSourceLinksInterface $getStockSourceLinks,
            StockSourceLinkInterfaceFactory $stockSourceLink,
            DataObjectHelper $dataObject,
            StockSourceLinksSaveInterface $stockSourceLinksSave
        ) {
            $this->_request = $context->getRequest();
            $this->sourceInterfaceFactory = $sourceInterfaceFactory;
            $this->sourceRepositoryInterface = $sourceRepositoryInterface;
            $this->inventorySourceHydrator = $inventorySourceHydrator;
            $this->messageManager = $messageManager;
            $this->storeManager = $storeManager;
            $this->searchCriteriaBuilder = $searchCriteriaBuilder;
            $this->getStockSourceLinks = $getStockSourceLinks;
            $this->stockSourceLink = $stockSourceLink;
            $this->dataObject = $dataObject;
            $this->stockSourceLinksSave = $stockSourceLinksSave;
            parent::__construct($context);
        }
    
        public function prepareSourceRequestData()
        {
            $sourceCode = null;
            $request = $this->_request;
            $sourceData = [];
            $sourceData['id_field_name'] = 'source_code';
            $sourceData['source_code'] = 'us-florida';
            $sourceData['name'] = 'US Florida';
            $sourceData['email'] = '';
            $sourceData['contact_name'] = '';
            $sourceData['enabled'] = 1;
            $sourceData['description'] = '';
            $sourceData['latitude'] = '';
            $sourceData['longitude'] = '';
            $sourceData['country_id'] = 'US';
            $sourceData['region_id'] = 12;
            $sourceData['city'] = '';
            $sourceData['postcode'] = '95004';
            $sourceData['use_default_carrier_config'] = 1;
            $sourceData['carrier_codes'] = '';
            $sourceData['disable_source_code'] = true;
            $sourceData['phone'] = '';
            $sourceData['fax'] = '';
            $sourceData['region'] = '';
            $sourceData['street'] = '';
            $request->setPostValue('general', $sourceData);
            $request->setPostValue('form_key', 'or1eYs7K4PBsLbO1');
        }
    
        public function processSource()
        {
            // prepare source request data
            $this->prepareSourceRequestData();
            $sourceCode = null;
            $newSourceData = [];
            $request = $this->_request;
            $requestData = $request->getPost()->toArray();
            $sourceCodeQueryParam = $request->getQuery(SourceInterface::SOURCE_CODE);
            try {
                $inventorySource = (null !== $sourceCodeQueryParam)
                    ? $this->sourceRepositoryInterface->get($sourceCodeQueryParam)
                    : $this->sourceInterfaceFactory->create();
    
                $inventorySource = $this->inventorySourceHydrator->hydrate($inventorySource, $requestData);
    
                $this->_eventManager->dispatch(
                    'controller_action_inventory_populate_source_with_data',
                    [
                        'request' => $request,
                        'source' => $inventorySource,
                    ]
                );
    
                $this->sourceRepositoryInterface->save($inventorySource);
    
                $this->_eventManager->dispatch(
                    'controller_action_inventory_source_save_after',
                    [
                        'request' => $request,
                        'source' => $inventorySource,
                    ]
                );
                $sourceCode = $requestData['general']['source_code'];
                $newSourceData['source_code'] = $sourceCode;
                $newSourceData['name'] = $requestData['general']['name'];
                $newSourceData['position'] = 1;
                $newSourceData['record_id'] = $sourceCode;
                $newSourceData['priority'] = 1;
    
                $this->messageManager->addSuccessMessage(__('The Source has been saved.'));
            } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
                $sourceCode = 0;
                $this->messageManager->addErrorMessage(__('The Source does not exist.'));
            } catch (\Magento\Framework\Validation\ValidationException $e) {
                $sourceCode = 0;
                foreach ($e->getErrors() as $localizedError) {
                    $this->messageManager->addErrorMessage($localizedError->getMessage());
                }
            } catch (\Magento\Framework\Exception\CouldNotSaveException $e) {
                $sourceCode = 0;
                $this->messageManager->addErrorMessage($e->getMessage());
            } catch (\Exception $e) {
                $sourceCode = 0;
                $this->messageManager->addErrorMessage(__('Could not save Source.'));
            }
    
            $this->assignSourceToStocks($sourceCode, $newSourceData);
        }
    
        public function assignSourceToStocks($sourceCode, $newSourceData)
        {
            try {
                if ($sourceCode) {
                    $allWebsites = $this->storeManager->getWebsites();
                    foreach ($allWebsites as $website) {
                        $websiteCode = $website->getCode();
                        $stockId = $this->getAssignedStockIdForWebsite->execute($websiteCode);
    
                        $searchCriteria = $this->searchCriteriaBuilder->addFilter(
                            StockSourceLinkInterface::STOCK_ID,
                            $stockId
                        )->create();
    
                        $result = [];
                        foreach ($this->getStockSourceLinks->execute($searchCriteria)->getItems() as $link) {
                            $result[$link->getSourceCode()] = $link;
                        }
    
                        if (isset($result[$sourceCode])) {
                            $link = $result[$sourceCode];
                        } else {
                            /** @var StockSourceLinkInterface $link */
                            $link = $this->stockSourceLink->create();
                        }
    
                        $newSourceData[StockSourceLinkInterface::STOCK_ID] = $stockId;
                        $this->dataObject->populateWithArray($link, $newSourceData, StockSourceLinkInterface::class);
    
                        $result[] = $link;
    
                        if (!empty($result)) {
                            $this->stockSourceLinksSave->execute($result);
                        }
                    }
                }
            } catch (\Exception $e) {
                $this->messageManager->addErrorMessage(__('Could not save Source to Stock.'));
            }
        }
    }
    

    . . .

    Leave a Comment

    Your email address will not be published. Required fields are marked*


    2 comments

  • Ajith
    does MSI module has Branch concepts..?
    • gunjita joshi (Moderator)
      Hello Ajith,

      Did you mean ‘branch’ as ‘warehouse’? In Magento 2.3, the admin can manage multiple sources of product and handle stock for every warehouse.

      For a detailed discussion, please create a ticket or send an email.

      Thanks

  • Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home