Magento 2 Modify exist order

Modify orders in your Magento 2 store without canceling them!
You can easily edit, add items, shipping, order data, as well for order details.

Load your order, quote and items need to modify.

Init construct :
public function __construct(
        Action\Context $context,
        \Magento\Sales\Model\OrderFactory $orderFactory,
        \Magento\Quote\Model\Quote\Item\ToOrderItem $toOrderItem,
        \Magento\Quote\Model\QuoteFactory $quoteFactory,
        \Magento\Sales\Model\Order\ItemRepository $orderItemRepository,
        \Magento\Sales\Model\Order\Email\Sender\OrderSender $sender,
        \Magento\Catalog\Model\ProductRepository $productRepository,
        \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
    )
    {
        parent::__construct($context);
        $this->orderFactory = $orderFactory;
        $this->toOrderItem = $toOrderItem;
        $this->quoteFactory = $quoteFactory;
        $this->orderItemRepository = $orderItemRepository;
        $this->sender = $sender;
        $this->productRepository = $productRepository;
        $this->quoteRepository = $quoteRepository;
    }
Load order, quote and specify items/products
$orderId = 100;//order id = 100 
$itemRemoves = ['100'=> true];//item id= 100
$itemQtys = ['1001'=> 1,'1002'=>2];//item ids 1001 with qty=1, 1002 with qty=2 
$newProductQty = ['1003'=> 3,'1004'=>4];// product id 1003 with qty=3, 1004 with qty=4 
$order = $this->orderFactory->create()->load($orderId);
$quote = $this->quoteFactory->create()->load($order->getQuoteId());
Edit/Update/Add New need to same with quote and orders.

Remove items from quote and order:
foreach ($itemRemoves as $id => $value){
    if($value){
      foreach ($quote->getAllVisibleItems() as $quoteItem) {
            if ($id == $quoteItem->getId()){
                $quoteItem->delete();
                $quote->collectTotals();

                foreach ($order->getItemsCollection() as $orderItem){
                    if($orderItem->getQuoteItemId() == $id){
                       $this->orderItemRepository->deleteById($orderItem->getId());
                    }
                }
            }
       }
       unset($itemQtys[$id]);
    }
}
Update Items from quote and convert to order:
foreach ($itemQtys as $id => $qty){
   if($qty> 0){
       $item = $quote->getItemById($id);
        if(!$item) return;

        $taxAmount = $item->getTaxPercent() * $qty * ($item->getProduct()->getPrice()/100);
        $rowTotalInclTax = $item->getPrice() * $qty + $taxAmount;
        $item
            ->setQty($qty)
            ->setTaxAmount($taxAmount)
            ->setRowTotalInclTax($rowTotalInclTax);
        $existData = $order->getItemByQuoteItemId($item->getId());
        if(!$existData) continue;
        $orderItem = $this->toOrderItem->convert($item);
        $existData->addData($orderItem->getData());
    }
}

Add new item to quote and order:
foreach ($newProductQty as $productId => $qty){
    if($qty> 0){
        $product = $this->productRepository->getById($productId);
        if(!$product) return;

        $params = [
            'product'   => $productId,
            'qty'       => $qty
        ];
        $request = new \Magento\Framework\DataObject();
        $request->setData($params);
        $quote->addProduct($product, $request);
        $quote->collectTotals();

        foreach ($quote->getAllVisibleItems() as $item){
            if($productId == $item->getProduct()->getId()){
                /**
                 * collect item price
                 */
                $item->save();
                $existData = $order->getItemByQuoteItemId($item->getId());
                $orderItem = $this->toOrderItem->convert($item);

                if ($existData) {
                    $existData->addData($orderItem->getData());
                }else{
                    /**
                     * add new product to
                     */
                    if ($item->getParentItem()) {
                        $item->setParentItem(
                            $order->getItemByQuoteItemId($item->getParentItem()->getId())
                        );
                    }
                    $order->addItem($orderItem);
                }
            }
        }
    }
}
Re-Calculate order totals:
        /**
         * collect shipping amount
         */
        $quote->getShippingAddress()->requestShippingRates();
        $itemTax = 0;
        $subTotal = 0;
        foreach ($order->getAllVisibleItems() as $item){
            $itemTax += $item->getTaxAmount();
            $subTotal += $item->getRowTotalInclTax() - $item->getTaxAmount();
        }

        $order->setSubtotal($subTotal)
            ->setBaseSubtotal($subTotal)
            ->setShippingAmount($quote->getShippingAddress()->getShippingAmount())
            ->setBaseShippingAmount($quote->getShippingAddress()->getBaseShippingAmount())
            ->setShippingTaxAmount($quote->getShippingAddress()->getShippingTaxAmount())
            ->setBaseShippingTaxAmount($quote->getShippingAddress()->getBaseShippingTaxAmount())
            ->setTaxAmount($quote->getShippingAddress()->getShippingTaxAmount() + $itemTax)
            ->setBaseTaxAmount($quote->getShippingAddress()->getShippingTaxAmount() + $itemTax)
            ->setGrandTotal($subTotal + $quote->getShippingAddress()->getShippingAmount() + $quote->getShippingAddress()->getShippingTaxAmount() + $itemTax)
            ->setBaseGrandTotal($subTotal + $quote->getShippingAddress()->getBaseShippingAmount() + $quote->getShippingAddress()->getBaseshippingTaxAmount() + $itemTax);
        $order->save();
Finally, Send email again for customer with new order update
$this->sender->send($order);
This is full code
public function __construct(
        Action\Context $context,
        \Magento\Sales\Model\OrderFactory $orderFactory,
        \Magento\Quote\Model\Quote\Item\ToOrderItem $toOrderItem,
        \Magento\Quote\Model\QuoteFactory $quoteFactory,
        \Magento\Sales\Model\Order\ItemRepository $orderItemRepository,
        \Magento\Sales\Model\Order\Email\Sender\OrderSender $sender,
        \Magento\Catalog\Model\ProductRepository $productRepository,
        \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
    )
    {
        parent::__construct($context);
        $this->orderFactory = $orderFactory;
        $this->toOrderItem = $toOrderItem;
        $this->quoteFactory = $quoteFactory;
        $this->orderItemRepository = $orderItemRepository;
        $this->sender = $sender;
        $this->productRepository = $productRepository;
        $this->quoteRepository = $quoteRepository;
    }

    /**
     * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void
     */
    public function execute()
    {
        $data = $this->getRequest()->getParams();
        $orderId = (isset($data['order_id']))? $data['order_id'] : null;
        $itemRemoves = (isset($data['remove'])) ? array_filter($data['remove']) : [];
        $itemQtys = (isset($data['qty'])) ? array_filter($data['qty']) : [];
        $newProductQty = (isset($data['new_product_qty'])) ? array_filter($data['new_product_qty']) : [];

        if(!$orderId || (empty($itemQtys) && empty($itemRemoves) && empty($newProductQty))){
            $this->messageManager->addErrorMessage('empty data update');
            $url = $this->getUrl('sales/order/view',array('order_id'=> $orderId));
            $this->_redirect($url);
            return;
        }

        $order = $this->orderFactory->create()->load($orderId);
        $quote = $this->quoteFactory->create()->load($order->getQuoteId());
        try{
            /**
             * remove items
             */
            foreach ($itemRemoves as $id => $value){
                if($value){
                    $this->removeItemFromOrder($order, $quote, $id);
                    unset($itemQtys[$id]);
                }
            }
            /**
             * update items
             */
            foreach ($itemQtys as $id => $qty){
                if($qty> 0){
                    $this->updateItemFromOrder($order, $quote, $id, $qty);
                }
            }
            /**
             * add new items
             */
            foreach ($newProductQty as $productId => $qty){
                if($qty> 0){
                    $this->addNewItem($order, $quote, $productId, $qty);
                }
            }

            $this->collectQuoteOrder($quote, $order);
            if(isset($data['notify_customer'])){
                $this->sender->send($order);
            }
            $this->messageManager->addSuccessMessage('Update Order Successfully!');
        }catch(\Exception $e){
            $this->messageManager->addErrorMessage($e->getMessage());
        }
        $url = $this->getUrl('sales/order/view',array('order_id'=> $orderId));
        $this->_redirect($url);
    }

    /**
     * @param \Magento\Sales\Model\Order $order
     * @param \Magento\Quote\Model\Quote $quote
     * @param $productId
     * @param $qty
     * @throws \Magento\Framework\Exception\LocalizedException
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function addNewItem($order, $quote, $productId, $qty){
        $product = $this->productRepository->getById($productId);
        if(!$product) return;

        $params = [
            'product'   => $productId,
            'qty'       => $qty
        ];
        $request = new \Magento\Framework\DataObject();
        $request->setData($params);
        $quote->addProduct($product, $request);
        $quote->collectTotals();

        foreach ($quote->getAllVisibleItems() as $item){
            if($productId == $item->getProduct()->getId()){
                /**
                 * collect item price
                 */
                $item->save();
                $existData = $order->getItemByQuoteItemId($item->getId());
                $orderItem = $this->toOrderItem->convert($item);

                if ($existData) {
                    $existData->addData($orderItem->getData());
                }else{
                    /**
                     * add new product to
                     */
                    if ($item->getParentItem()) {
                        $item->setParentItem(
                            $order->getItemByQuoteItemId($item->getParentItem()->getId())
                        );
                    }
                    $order->addItem($orderItem);
                }
            }
        }

    }
    /**
     * @param \Magento\Sales\Model\Order $order
     * @param \Magento\Quote\Model\Quote $quote
     * @param $id
     * @throws \Exception
     */
    protected function removeItemFromOrder($order, $quote, $id){
        foreach ($quote->getAllVisibleItems() as $quoteItem) {
            if ($id == $quoteItem->getId()){
                $quoteItem->delete();
                $quote->collectTotals();

                foreach ($order->getItemsCollection() as $orderItem){
                    if($orderItem->getQuoteItemId() == $id){
                        $this->orderItemRepository->deleteById($orderItem->getId());
                    }
                }
            }
        }
    }

    /**
     * @param \Magento\Sales\Model\Order $order
     * @param \Magento\Quote\Model\Quote $quote
     * @param $id
     * @param $qty
     */
    protected function updateItemFromOrder($order, $quote, $id, $qty){
        $item = $quote->getItemById($id);
        if(!$item) return;

        $taxAmount = $item->getTaxPercent() * $qty * ($item->getProduct()->getPrice()/100);
        $rowTotalInclTax = $item->getPrice() * $qty + $taxAmount;
        $item
            ->setQty($qty)
            ->setTaxAmount($taxAmount)
            ->setRowTotalInclTax($rowTotalInclTax);
        $existData = $order->getItemByQuoteItemId($item->getId());
        $orderItem = $this->toOrderItem->convert($item);
        $existData->addData($orderItem->getData());
    }

    /**
     * @param \Magento\Quote\Model\Quote $quote
     * @param \Magento\Sales\Model\Order $order
     * @throws \Exception
     */
    protected function collectQuoteOrder($quote, $order){
        /**
         * collect shipping amount
         */
        $quote->getShippingAddress()->requestShippingRates();
        $itemTax = 0;
        $subTotal = 0;
        foreach ($order->getAllVisibleItems() as $item){
            $itemTax += $item->getTaxAmount();
            $subTotal += $item->getRowTotalInclTax() - $item->getTaxAmount();
        }

        $order->setSubtotal($subTotal)
            ->setBaseSubtotal($subTotal)
            ->setShippingAmount($quote->getShippingAddress()->getShippingAmount())
            ->setBaseShippingAmount($quote->getShippingAddress()->getBaseShippingAmount())
            ->setShippingTaxAmount($quote->getShippingAddress()->getShippingTaxAmount())
            ->setBaseShippingTaxAmount($quote->getShippingAddress()->getBaseShippingTaxAmount())
            ->setTaxAmount($quote->getShippingAddress()->getShippingTaxAmount() + $itemTax)
            ->setBaseTaxAmount($quote->getShippingAddress()->getShippingTaxAmount() + $itemTax)
            ->setGrandTotal($subTotal + $quote->getShippingAddress()->getShippingAmount() + $quote->getShippingAddress()->getShippingTaxAmount() + $itemTax)
            ->setBaseGrandTotal($subTotal + $quote->getShippingAddress()->getBaseShippingAmount() + $quote->getShippingAddress()->getBaseshippingTaxAmount() + $itemTax);       
        $order->save();
    }
Goodluk!

Leave a Reply