Magento 2 : How to create shipment programatically?

Create shipment programatically
Today we are going to cover how to create shipment and adding tracking information programatically in Magento 2. This could help if you are creating shipment in Magento 2 from your ERP or WMS or any third party systems.

As usual we did our research and managed to find the best of way of achieving this so we thought we should follow with our Magento 2 Developers.

Let’s crack on with creating shipment model class to create shipment in Magento 2 along with tracking number-:

<?php
/**
* @var \Magento\Sales\Model\Order\Shipment\TrackFactory
*/
protected $_shipmentTrackFactory;

/**
* @var \Magento\Sales\Model\Order\ShipmentFactory
*/
protected $_shipmentFactory;

/**
 * @var \Magento\Framework\DB\TransactionFactory
 */
protected $_transactionFactory;

/**
* @var \Magento\Sales\Api\OrderRepositoryInterface
*/
protected $_orderRepository;

/**
* @param \Magento\Sales\Model\Order\Shipment\TrackFactory $shipmentTrackFactory
* @param \Magento\Sales\Model\Order\ShipmentFactory $shipmentFactory
* @param \Magento\Framework\DB\TransactionFactory $transactionFactory
* @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
*/
public function __construct(
    \Magento\Sales\Model\Order\Shipment\TrackFactory $shipmentTrackFactory,
    \Magento\Sales\Model\Order\ShipmentFactory $shipmentFactory,
    \Magento\Framework\DB\TransactionFactory $transactionFactory,
    \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
    ) {
      $this->_shipmentTrackFactory = $shipmentTrackFactory;
      $this->_shipmentFactory = $shipmentFactory;
      $this->_transactionFactory = $transactionFactory;
      $this->_orderRepository = $orderRepository;
}

/**
 * @param int $orderId
 * @param string $trackingNumber
 * @return \Magento\Sales\Model\Shipment $shipment
 */
protected function createShipment($orderId, $trackingNumber)
{
    try {
        $order = $this->_orderRepository->get($orderId);
        if ($order){
            $data = array(array(
                'carrier_code' => $order->getShippingMethod(),
                'title' => $order->getShippingDescription(),
                'number' => $trackingNumber,
            ));
            $shipment = $this->prepareShipment($order, $data);
            if ($shipment) {
                $order->setIsInProcess(true);
                $order->addStatusHistoryComment('Automatically SHIPPED', false);
                $transactionSave =  $this->_transactionFactory->create()->addObject($shipment)->addObject($shipment->getOrder());
                $transactionSave->save();
            }
            return $shipment;
        }
    } catch (\Exception $e) {
        throw new \Magento\Framework\Exception\LocalizedException(
            __($e->getMessage())
        );
    }
}

/**
* @param $order \Magento\Sales\Model\Order
* @param $track array
* @return $this
*/
protected function prepareShipment($order, $track)
{
   $shipment = $this->_shipmentFactory->create(
       $order,
       $this->prepareShipmentItems($order),
       $track
   );
   return $shipment->getTotalQty() ? $shipment->register() : false;
}

/**
* @param $order \Magento\Sales\Model\Order
* @return array
*/
protected function prepareShipmentItems($order)
{
   $items = [];

   foreach($order->getAllItems() as $item) {
       $items[$item->getItemId()] = $item->getQtyOrdered();
   }
   return $items;
}

There are two main functions prepareShipment of \Magento\Sales\Model\Order\ShipmentFactory class which prepare shipment and addObject of \Magento\Framework\DB\TransactionFactory class which helps to create shipment and associate the shipment with original order in Magento 2.

That’s it, Hope this article helped you in some way. Please leave us your comment and let us know what do you think? Thanks.

4 thoughts on “Magento 2 : How to create shipment programatically?

  1. Thanks for this thorough example. One question I have is should we be using factories and that DB Transaction class to save the order and shipment over service contracts?

    Everything I’ve read recently states we should be using service contracts for handling data from the database and there are such articles on this site too?

    1. Completely agree with you James but unfortunately not all service contracts are available to handle all sort of data operations. We will share in another article how to do the same thing via service contract as this can be done via service contract, thanks for your feedback.

Leave a Reply

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