Magento 2 — How to delete the Invoice?

Jickson Johnson Koottala
5 min readNov 18, 2020
hellomage.com

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

Main invoice tables.

Important tables that store refunded data.

Table 1: sales_order_item

qty_invoiced
tax_invoiced
base_tax_invoiced
discount_tax_compensation_invoiced
base_discount_tax_compensation_invoiced
discount_invoiced
base_discount_invoiced
row_invoiced
base_row_invoiced

Table 2: sales_order

total_invoiced
base_total_invoiced
subtotal_invoiced
base_subtotal_invoiced
tax_invoiced
base_tax_invoiced
discount_tax_compensation_invoiced
base_discount_tax_compensation_invoiced
shipping_tax_invoiced
base_shipping_tax_invoiced
shipping_invoiced
base_shipping_invoiced
discount_invoiced
base_discount_invoiced
base_total_invoiced_cost

If the invoice state is “PAID”, we have to adjust the total_paid and base_total_paid

total_paid
base_total_paid

Let’s write the code

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

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

You can see more about defining route and controller here

<?php

namespace HelloMage\DeleteInvoice\Controller\Adminhtml\Delete;

use HelloMage\DeleteInvoice\Model\Invoice\Delete;
use Magento\Backend\App\Action;
use Magento\Sales\Api\InvoiceRepositoryInterface;

// our controller

/**
*
Class Invoice
*
@package HelloMage\DeleteInvoice\Controller\Adminhtml\Delete
*/
class Invoice extends \Magento\Backend\App\Action
{
/**
*
@var InvoiceRepositoryInterface
*/
protected $invoiceRepository;

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

/**
* Invoice constructor.
*
@param Action\Context $context
*
@param InvoiceRepositoryInterface $invoiceRepository
*
@param Delete $delete
*/
public function __construct(
Action\Context $context,
InvoiceRepositoryInterface $invoiceRepository,
Delete $delete

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

parent::__construct($context);
}

public function execute()
{
// getting invoice id from request
$invoiceId = $this->getRequest()
->getParam('invoice_id');

// using invoice repository, loading object
$invoice = $this->invoiceRepository
->get($invoiceId);

// fetching order_id from invoice object
$orderId = $invoice->getOrderId();

try {
// this is the function does the delete
$this->delete->deleteInvoice($invoiceId);

$this->messageManager->addSuccessMessage(
__(
'Successfully deleted invoice #%1.',
$invoice->getIncrementId()
)
);
} catch (\Exception $e) {
$this->messageManager->addErrorMessage(
__(
'Error delete invoice #%1.',
$invoice->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\DeleteInvoice\Model\Invoice;

use Magento\Sales\Api\InvoiceRepositoryInterface;
use Magento\Sales\Model\Order;

/**
*
Class Delete
*
@package HelloMage\DeleteInvoice\Model\Invoice
*/
class Delete
{

/**
*
@var InvoiceRepositoryInterface
*/
protected $invoiceRepository;

/**
*
@var Order
*/
protected $order;

/**
* Delete constructor.
*
@param InvoiceRepositoryInterface $invoiceRepository
*
@param Order $order
*/
public function __construct(
InvoiceRepositoryInterface $invoiceRepository,
Order $order

) {
$this->invoiceRepository = $invoiceRepository;
$this->order = $order;

}

/**
*
@param $invoiceId
*
@return void
*/
public function deleteInvoice($invoiceId)
{
// delete operations.
}
}

so, to perform this delete operation, we have to do the following.

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

2. load invoiced items.

$invoiceItems = $invoice->getAllItems();

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

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

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

// comparing ordered items against invoiced itemsforeach ($orderItems as $item) {foreach ($invoiceItems as $invoiceItem) {if ($invoiceItem->getOrderItemId() == $item->getItemId()) {// adjusting qty_invoiced
$item->setQtyInvoiced($item->getQtyInvoiced() - $invoiceItem->getQty());
// adjusting tax_invoiced
$item->setTaxInvoiced($item->getTaxInvoiced() - $invoiceItem->getTaxAmount());
// adjusting base_tax_invoiced
$item->setBaseTaxInvoiced($item->getBaseTaxInvoiced() - $invoiceItem->getBaseTaxAmount());
// adjusting discount_tax_compensation_invoiced
$item->setDiscountTaxCompensationInvoiced($item->getDiscountTaxCompensationInvoiced() - $invoiceItem->getDiscountTaxCompensationAmount());
$baseDiscountTaxItem = $item->getBaseDiscountTaxCompensationInvoiced();$baseDiscountTaxInvoice = $invoiceItem->getBaseDiscountTaxCompensationAmount();// adjusting base_discount_tax_compensation_invoiced
$item->setBaseDiscountTaxCompensationInvoiced($baseDiscountTaxItem - $baseDiscountTaxInvoice);
// adjusting discount_invoiced
$item->setDiscountInvoiced($item->getDiscountInvoiced() - $invoiceItem->getDiscountAmount());
// adjusting base_discount_invoiced
$item->setBaseDiscountInvoiced($item->getBaseDiscountInvoiced() - $invoiceItem->getBaseDiscountAmount());
// adjusting row_invoiced
$item->setRowInvoiced($item->getRowInvoiced() - $invoiceItem->getRowTotal());
// adjusting base_row_invoiced
$item->setBaseRowInvoiced($item->getBaseRowInvoiced() - $invoiceItem->getBaseRowTotal());
}}}

5. adjust the sale order invoice field values ( Table 2: sales_order ).

// adjusting total_invoiced
$order->setTotalInvoiced($order->getTotalInvoiced() - $invoice->getGrandTotal());
// adjusting base_total_invoiced
$order->setBaseTotalInvoiced($order->getBaseTotalInvoiced() - $invoice->getBaseGrandTotal());
// adjusting subtotal_invoiced
$order->setSubtotalInvoiced($order->getSubtotalInvoiced() - $invoice->getSubtotal());
// adjusting base_subtotal_invoiced
$order->setBaseSubtotalInvoiced($order->getBaseSubtotalInvoiced() - $invoice->getBaseSubtotal());
// adjusting tax_invoiced
$order->setTaxInvoiced($order->getTaxInvoiced() - $invoice->getTaxAmount());
// adjusting base_tax_invoiced
$order->setBaseTaxInvoiced($order->getBaseTaxInvoiced() - $invoice->getBaseTaxAmount());
// adjusting discount_tax_compensation_invoiced
$order->setDiscountTaxCompensationInvoiced($order->getDiscountTaxCompensationInvoiced() - $invoice->getDiscountTaxCompensationAmount());
// adjusting base_discount_tax_compensation_invoiced
$order->setBaseDiscountTaxCompensationInvoiced($order->getBaseDiscountTaxCompensationInvoiced() - $invoice->getBaseDiscountTaxCompensationAmount());
// adjusting shipping_tax_invoiced
$order->setShippingTaxInvoiced($order->getShippingTaxInvoiced() - $invoice->getShippingTaxAmount());
// adjusting base_shipping_tax_invoiced
$order->setBaseShippingTaxInvoiced($order->getBaseShippingTaxInvoiced() - $invoice->getBaseShippingTaxAmount());
// adjusting shipping_invoiced
$order->setShippingInvoiced($order->getShippingInvoiced() - $invoice->getShippingAmount());
// adjusting base_shipping_invoiced
$order->setBaseShippingInvoiced($order->getBaseShippingInvoiced() - $invoice->getBaseShippingAmount());
// adjusting discount_invoiced
$order->setDiscountInvoiced($order->getDiscountInvoiced() - $invoice->getDiscountAmount());
// adjusting base_discount_invoiced
$order->setBaseDiscountInvoiced($order->getBaseDiscountInvoiced() - $invoice->getBaseDiscountAmount());
// adjusting base_total_invoiced_cost
$order->setBaseTotalInvoicedCost($order->getBaseTotalInvoicedCost() - $invoice->getBaseCost());
// checking if invoice id PAID
if ($invoice->getState() == \Magento\Sales\Model\Order\Invoice::STATE_PAID) {
// if PAID, adjusting total_paid
$order->setTotalPaid($order->getTotalPaid() - $invoice->getGrandTotal());
// if PAID, adjusting base_total_paid
$order->setBaseTotalPaid($order->getBaseTotalPaid() - $invoice->getBaseGrandTotal());
}

Once adjustment on invoice depended field values done, we can go with the deleting invoice. we already have the invoiceRepository on the constructor and also the invoice object. use below to delete an invoice.

// delete invoice by invoice object
$this->invoiceRepository->delete($invoiceData);

An alternative way to delete invoices.

// using resource connection load the tables.$connection = $this->resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION);$invoiceGridTable = $connection->getTableName($this->data->getTableName('sales_invoice_grid'));
$invoiceTable = $connection->getTableName($this->data->getTableName('sales_invoice'));
// delete invoice info$connection->rawQuery('DELETE FROM `' . $invoiceGridTable . '` WHERE entity_id=' . $invoiceId);
$connection->rawQuery('DELETE FROM `' . $invoiceTable . '` WHERE entity_id=' . $invoiceId);

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(\Magento\Sales\Model\Order::STATE_PROCESSING)
->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_PROCESSING))->save();
} else {// if none of the above, the state must be NEW/PENDING.$order->setState(\Magento\Sales\Model\Order::STATE_NEW)
->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW))->save();
}

--

--