Is it possible to recalculate discount applied to order after some changes done in it?
For example: customer has ordered to 2 products and discount 10% was applied to order, we remove one of order items and discount rules are not met for this case.
Magento doesn't support updating an order. The workflow would be to cancel one order and to create a new one.
In my case, I needed to update the prices of order items because an external system would give us updated prices. We could not cancel the order and create a new one because we needed to stick to one order ID.
A quote object does have all the functionalities of updating and recalculating, so my logic was to grab the original quote of the order, to update that quote and to get the changes back onto the order.
On this project, we were using Magento EE 1.12.0.2 (comparable to CE 1.7.0.2).
The code that I wrote was like below (inside a model that would extend Mage_Sales_Model_Service_Quote
, check that one, could be useful for you):
$orderId = 1;
$order = Mage::getModel('sales/order')->load($orderId);
/* @var $order Mage_Sales_Model_Order */
$quote = Mage::getModel('sales/quote')->load($order->getQuoteId());
/* @var $quote Mage_Sales_Model_Quote */
$convertor = Mage::getSingleton('sales/convert_quote');
/* @var $convertor Mage_Sales_Model_Convert_Quote */
/**
* Update the quote items now...
* If you want to delete an item, delete it from the quote and also from the order
* You could also improve the code below to flag all handled order items and
* at the end delete the unhandled ones (so these were deleted in the original quote)
*/
$quote->collectTotals();
$convertor->toOrder($quote, $order);
foreach ($quote->getAllItems() as $quoteItem) {
/* @var $quoteItem Mage_Sales_Model_Quote_Item */
$orderItem = $convertor->itemToOrderItem($quoteItem);
/* @var $orderItem Mage_Sales_Model_Order_Item */
$origOrderItem = $order->getItemByQuoteItemId($quoteItem->getId());
/* @var $origOrderItem Mage_Sales_Model_Order_Item */
if ($origOrderItem) {
$origOrderItem->addData($orderItem->getData());
} else {
// New item
if ($quoteItem->getParentItem()) {
$orderItem->setParentItem(
$order->getItemByQuoteItemId($quoteItem->getParentItem()->getId())
);
}
$order->addItem($orderItem);
}
}
$order->setSubtotal($quote->getSubtotal())
->setBaseSubtotal($quote->getBaseSubtotal())
->setGrandTotal($quote->getGrandTotal())
->setBaseGrandTotal($quote->getBaseGrandTotal());
$quote->save();
$order->save();
$orderItem->cancel()->delete();
should work. That's because the CatalogInventory module is listening on the sales_order_item_cancel
event.
Commented
Oct 7, 2015 at 10:15
origOrderItem
is null if quoteItem id isn't set (like on non-saved quoteItem) which without it resulted in a merged config/simple item with data from both. Also added parentItemId
to orderItem so that $order->getAllVisibleItems()
works as expected. See stackoverflow.com/questions/32767744/#32949997
Commented
Oct 15, 2015 at 2:21
It really isn't the best customer experience, but I found that canceling this order and creating new orders worked best for us.
$item->delete()
(it deletes item from database also)sales_flat_order
,sales_flat_order_item
,sales_flat_order_grid
,sales_flat_invoice
,sales_order_tax
,sales_order_tax_item
, etc. It's not preferable.