Magento 2 — How to delete Shipment?

Jickson Johnson Koottala
4 min readNov 17, 2020
hellomage.com

Before we go into the methods to delete a shipment, let’s have a look at the shipment tables first, as well as the Important tables that keep the shipment data.

Main Shipment Tables

shipment main tables

Important tables that store shipment data.

Table 1: sales_order_item

qty_shipped

Lets write the code

First thing is to get the shipment ID and using that, get the shipment object. when we developing this as a module, we used to pass the shipment ID either to the controller, or maybe we might use a console command with shipment ID as an argument. In any case, we have to get the shipment ID to get the object.

Below is the controller execute function. I have used a button inside the shipment detail page that points to the below controller action.

You can see more about defining route and controller here

<?php

namespace HelloMage\DeleteShipment\Controller\Adminhtml\Delete;

use Exception;
use HelloMage\DeleteShipment\Model\Shipment\Delete;
use Magento\Backend\App\Action;

// our controller

/**
* Class Shipment
*
@package HelloMage\DeleteShipment\Controller\Adminhtml\Delete
*/
class Shipment extends Action
{
/**
*
@var \Magento\Sales\Model\Order\Shipment
*/
protected $shipment;

/**
*
@var Delete
*/
protected $delete;

/**
* Shipment constructor.
*
@param Action\Context $context
*
@param \Magento\Sales\Model\Order\Shipment $shipment
*
@param Delete $delete
*/
public function __construct(
Action\Context $context,
\Magento\Sales\Model\Order\Shipment $shipment,
Delete $delete

) {
$this->shipment = $shipment;
$this->delete = $delete;

parent::__construct($context);
}
public function execute()
{
// getting shipment id from request
$shipmentId = $this->getRequest()
->getParam('shipment_id');

// using shipment repository, loading object
$shipment = $this->shipment
->load($shipmentId);

// fetching order-id from shipment object
$orderId = $shipment->getOrderId();

try {
// this is the function does delete operation
$this->delete->deleteShipment($shipmentId);

$this->messageManager->addSuccessMessage(
__(
'Successfully deleted shipment #%1.',
$shipment->getIncrementId()
)
);
} catch (Exception $e) { $this->messageManager
->addErrorMessage(
__(
'Error delete shipment #%1.',
$shipment->getIncrementId()
)
);
}

$resultRedirect = $this->resultRedirectFactory->create();

// redirecting to relative order page
$resultRedirect->setPath(
'sales/order/view',
[
'order_id' => $orderId
]
);

return $resultRedirect;
}
}

So, let’s create a class called “Delete

<?php

namespace HelloMage\DeleteShipment\Model\Shipment;

use Exception;
use Magento\Sales\Api\ShipmentRepositoryInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Shipment;
/**
*
Class Delete
*
@package HelloMage\DeleteShipment\Model\Shipment
*/
class Delete
{
/**
*
@var ShipmentRepositoryInterface
*/
protected $shipmentRepository;

/**
*
@var Shipment
*/
protected $shipment;
/**
*
@var Order
*/
protected $order;
/**
*
Delete constructor.
*
@param Order $order
*
@param ShipmentRepositoryInterface $shipmentRepository
*/
public function __construct(
Shipment $shipment,
Order $order,
ShipmentRepositoryInterface $shipmentRepository

) {
$this->order = $order;
$this->shipment = $shipment;
$this->shipmentRepository = $shipmentRepository;
}

/**
*
@param $shipmentId
*
@return \Magento\Sales\Model\Order
*
@throws \Exception
*/
public function deleteShipment($shipmentId)
{
// delete operations.
}
}

So, the delete operations include the following.

  1. load the shipment object by its ID.
  2. load shipment items.
  3. from the shipment object, get the order object, and ordered items.
  4. match the ordered items against shipment items and adjust ordered item shipment field values ( Table 1: sales_order_item ).
  5. delete shipment.
  6. save order.
  1. load the shipment object by its ID.
$shipment = $this->shipment->load($shipmentId);

2. load shipment items.

$shipmentItems = $shipment->getAllItems();

3. from the shipment object, get the order object and ordered items.

$orderId = $shipment->getOrder()->getId();
$order = $this->order->load($orderId);
$orderItems = $order->getAllItems();

4. match the ordered items against shipment items and adjust ordered item shipment field values ( Table 1: sales_order_item ).

// comparing ordered items against shipment itemsforeach ($orderItems as $item) {
foreach ($shipmentItems as $shipmentItem) {
if ($shipmentItem->getOrderItemId() == $item->getItemId()) {
// adjusting qty_shipped$item->setQtyShipped($item->getQtyShipped() - $shipmentItem->getQty());}
}
}

Once adjustment on shipment depended field values done, we can go with deleting shipment. we have the shipmentRepository already on the constructor and the object is also loaded. use below to delete a shipment.

// delete shipment by shipment object
$this->shipmentRepository->delete($shipmentData);

An alternative way to delete a shipment.

// using resource connection load the tables.$connection = $this->resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION);$shipmentTable = $connection->getTableName($this->data->getTableName('sales_shipment'));$shipmentGridTable = $connection->getTableName($this->data->getTableName('sales_shipment_grid'));// deleting shipment$connection->rawQuery('DELETE FROM `' . $shipmentGridTable . '` WHERE entity_id=' . $shipmentId);
$connection->rawQuery('DELETE FROM `' . $shipmentTable . '` WHERE entity_id=' . $shipmentId);

Finally, we have to save the order. This adjusts the order state

// checking whether the order state needs to be processing.if ($order->hasShipments() || $order->hasInvoices() || $order->hasCreditmemos()) {$order->setState(Order::STATE_PROCESSING)->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING))->save();} else {// if none of the above, the state must be NEW/PENDING.$order->setState(Order::STATE_NEW)->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_NEW))->save();}

--

--