Menu Close
    Convert your store into Magento Multi Vendor Marketplace

    Introduction to Magento2 Multi Source Inventory (MSI)

    Introduction to Magento2 Multi Source Inventory (MSI)

    What is MSIMagento2 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 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 magento2 – For managing inventory in Magento 2.3 we will need to follow given below steps-

    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.
    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.
    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.
    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.

    How to programmatically create source and assign to stock in magento2 –

    /**
     * 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\InventorySales\Model\GetAssignedStockIdForWebsiteInterface;
    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 GetAssignedStockIdForWebsiteInterface
         */
        private $getAssignedStockIdForWebsite;
    
        /**
         * @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 GetAssignedStockIdForWebsiteInterface $getAssignedStockIdForWebsite
         * @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,
            GetAssignedStockIdForWebsiteInterface $getAssignedStockIdForWebsite,
            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->getAssignedStockIdForWebsite = $getAssignedStockIdForWebsite;
            $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.'));
            }
        }
    }
    

     

    . . .
    Discuss on Helpdesk

    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