3

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.

4
  • How do you remove 1 of the order items? By a credit, or a reorder? Jan 22, 2015 at 15:09
  • I am removing directly item from order using this kind of code : $item->delete() (it deletes item from database also)
    – rafal-kos
    Jan 22, 2015 at 15:28
  • Isn't is possible to just create a refund or reorder? That way, Magento calculates all the correct totals. You can do it manually of course, but you'll have to change a lot of data by hand, for example, entries in: 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. Jan 22, 2015 at 15:32
  • @MaikelKoek, if you are thinking in terms of database tables then you don't understand the concept of abstraction and ORM. You should use existing Magento classes to get the result you need and let those classes handle all the data changes which will automatically result in the correct changes in your database.
    – 7ochem
    Jan 28, 2015 at 10:24

2 Answers 2

5

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();
5
  • Thanks for this! One question, how come you don't simply loop the order object items and delete them all before running $order->addItem()? Could it be because of how stock status on items are handled in Magento? Oct 6, 2015 at 19:12
  • I did not check stock handling with this code as in my case this was not needed. Do test this and please post your findings here. Also, I'd always rather update stuff than delete and add the very same things again.
    – 7ochem
    Oct 7, 2015 at 7:14
  • I investigated it now, and it would seem like you have to call the cancel method before doing delete, so this $orderItem->cancel()->delete(); should work. That's because the CatalogInventory module is listening on the sales_order_item_cancel event. Oct 7, 2015 at 10:15
  • Btw, the cancel() method looks safe to call, it doesn't touch on invoices or payments in any way. Oct 7, 2015 at 10:17
  • Added two bugfixes just now. One for making sure 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 Oct 15, 2015 at 2:21
1

It really isn't the best customer experience, but I found that canceling this order and creating new orders worked best for us.

1
  • Yes, but the problem is that we want to do this programmatically as we are using preauthorization on payments and a lot of order items are not valid after payment.
    – rafal-kos
    Jan 23, 2015 at 12:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.