How to Create Shipment Programmatically with Source Selection MSI Magento 2

How to Create Shipment Programmatically with Source Selection MSI Magento 2

Hello Guys!

Welcome back to my blog. There are many tutorials and stack Exchange answers available for programmatically create a shipment in Magento 2. But today in this article I’ll talk about how we can programmatically create a shipment in multi stock inventory MSI in Magento 2.

For MSI we need to assign a source to shipment. So here is the basic steps to find source with ordered by priority and assgjned to the shipment

  1. First of all, we need to find stock Id by class Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface
  2. Then we need to find the source code ordered by priority by class Magento\InventoryApi\Api\GetSourcesAssignedToStockOrderedByPriorityInterface
  3. If there is no source added, it means it has a default source. So we need to assign the default source to the shipment. we can find default source code by class Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface
  4. Now we need to assign source code to the shipment extension attributes and save shipment.

Here is the sample code for create shipment

<?php

namespace MagePrince\Shipment\Model;

use Magento\InventoryApi\Api\GetSourcesAssignedToStockOrderedByPriorityInterface;
use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface;
use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface;
use Magento\Sales\Model\Convert\Order as ConvertOrder;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\ShipmentRepository;
use Magento\Sales\Model\OrderRepository;
use Magento\Shipping\Model\ShipmentNotifier;
use Psr\Log\LoggerInterface;
use Magento\Framework\Exception\LocalizedException;

class CreateShipment
{
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var OrderRepository
     */
    private $orderRepository;

    /**
     * @var ConvertOrder
     */
    private $convertOrder;

    /**
     * @var StockByWebsiteIdResolverInterface
     */
    private $stockByWebsiteIdResolver;

    /**
     * @var GetSourcesAssignedToStockOrderedByPriorityInterface
     */
    private $getSourcesAssignedToStockOrderedByPriority;

    /**
     * @var DefaultSourceProviderInterface
     */
    private $defaultSourceProvider;

    /**
     * @var ShipmentNotifier
     */
    private $shipmentNotifier;

    /**
     * @var ShipmentRepository
     */
    private $shipmentRepository;

    /**
     * ProcessUpdateOrder constructor.
     * @param LoggerInterface $logger
     * @param OrderRepository $orderRepository
     * @param ConvertOrder $convertOrder
     * @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver
     * @param GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority
     * @param DefaultSourceProviderInterface $defaultSourceProvider
     * @param ShipmentNotifier $shipmentNotifier
     * @param ShipmentRepository $shipmentRepository
     */
    public function __construct(
        LoggerInterface $logger,
        OrderRepository $orderRepository,
        ConvertOrder $convertOrder,
        StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver,
        GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority,
        DefaultSourceProviderInterface $defaultSourceProvider,
        ShipmentNotifier $shipmentNotifier,
        ShipmentRepository $shipmentRepository
    ) {
        $this->logger = $logger;
        $this->orderRepository = $orderRepository;
        $this->convertOrder = $convertOrder;
        $this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver;
        $this->getSourcesAssignedToStockOrderedByPriority = $getSourcesAssignedToStockOrderedByPriority;
        $this->defaultSourceProvider = $defaultSourceProvider;
        $this->shipmentNotifier = $shipmentNotifier;
        $this->shipmentRepository = $shipmentRepository;
    }

    /**
     * @param Order $order
     */
    private function createShipment($orderId)
    {
        try {
            $order = $this->orderRepository->get($orderId);
            $shipment = $this->prepareShipment($order);
            try {
                $this->shipmentRepository->save($shipment);
            } catch (\Exception $e) {
                throw new LocalizedException(__($e->getMessage()));
            }
            //$this->shipmentNotifier->notify($shipment);
            $order->setStatus(Order::STATE_COMPLETE)
                ->setState(Order::STATE_COMPLETE)
                ->addStatusToHistory($order->getStatus())
                ->addCommentToStatusHistory('Order Shipped and Completed');
        } catch (\Exception $e) {
            $this->logger->info(__('Cannot create shipment for order #%1', $order->getIncrementId()));
            $message = 'Cannot create shipment for order #'. $order->getIncrementId() . $e->getMessage();
            $order->addCommentToStatusHistory($message);
        }
        $this->orderRepository->save($order);
    }

    private function prepareShipment($order)
    {
        $shipment = $this->convertOrder->toShipment($order);
        foreach ($order->getAllItems() as $orderItem) {
            // Check if order item has qty to ship or is virtual
            if ($orderItem->getIsVirtual()) {
                continue;
            }
            $qtyShipped = $orderItem->getQtyOrdered();
            // Create shipment item with qty
            $shipmentItem
                = $this->convertOrder->itemToShipmentItem($orderItem)
                ->setQty($qtyShipped);
            // Add shipment item to shipment
            $shipment->addItem($shipmentItem);
        }
        $shipment->register();
        $shipment->getOrder()->setIsInProcess(true);

        $websiteId = $order->getStore()->getWebsiteId();
        $stockId = $this->stockByWebsiteIdResolver->execute((int)$websiteId)
            ->getStockId();
        $sources
            = $this->getSourcesAssignedToStockOrderedByPriority->execute((int)$stockId);

        if (!empty($sources) && count($sources) === 1) {
            $sourceCode = $sources[0]->getSourceCode();
        } else {
            $sourceCode = $this->defaultSourceProvider->getCode();
        }

        $shipment->getExtensionAttributes()->setSourceCode($sourceCode);

        return $shipment;
    }
}

So now just use the above class and call function createShipment($orderId) with order Id.

If you have any questions feel free to comment below.

Thanks for reading. Keep sharing 🙂