5

Given an order that has been invoiced (by cash on delivery for testing) and is in state "processing", how can I create a credit memo for a given sku and quantity (in code that is outside of Magento routing; e.g. /test.php)?

Say my order has an item of sku "100-0055-BRO-S" and quantity 2, and I want to refund 1 of these. My current code looks like this:

require 'app/Mage.php';
umask(0);
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
setlocale(LC_MONETARY, 'en_US');

$order = Mage::getModel('sales/order')->loadByIncrementId(1117575);
$convertor = Mage::getModel('sales/convert_order');

$refundSku = '100-0055-BRO-S';
$refundQty = 1;
$refundOi = null;
//$refundInvoice = null;

foreach($order->getAllItems() as $oi) {
    if($oi->getSku() == $refundSku) {
        $refundOi = Mage::getModel('sales/order_item')->load($oi->getId());
        break;
    }
}

$mcm = $convertor->toCreditmemo($order);
$mcmi = $convertor->itemToCreditmemoItem($refundOi);
$mcmi->setQty($refundQty);

$mcmi->setDiscountAmount($refundOi->getDiscountAmount() * ($mcmi->getQty() / $mcmi->getOrderItem()->getQtyOrdered()));
$mcmi->setBaseDiscountAmount($refundOi->getBaseDiscountAmount() * ($mcmi->getQty() / $mcmi->getOrderItem()->getQtyOrdered()));
$mcmi->setTaxAmount($refundOi->getTaxAmount() * ($mcmi->getQty() / $mcmi->getOrderItem()->getQtyOrdered()));
$mcmi->setBaseTaxAmount($refundOi->getBaseTaxAmount() * ($mcmi->getQty() / $mcmi->getOrderItem()->getQtyOrdered()));
$mcmi->setHiddenTaxAmount($refundOi->getHiddenTaxAmount() * ($mcmi->getQty() / $mcmi->getOrderItem()->getQtyOrdered()));
$mcmi->setBaseHiddenTaxAmount($refundOi->getBaseHiddenTaxAmount() * ($mcmi->getQty() / $mcmi->getOrderItem()->getQtyOrdered()));

$mcm->addItem($mcmi);
$mcm->register();

Mage::getModel('core/resource_transaction')
    ->addObject($mcm)
    ->addObject($mcm->getOrder())
    ->save();

This results in a credit memo being created on the order and showing up in the admin panel. However, the item looks like this:

Price    Qty  Subtotal   Tax Amount   Discount Amount    Row Total
$38.00    1    $0.00       $1.62           $19.00         -$17.39

I don't understand why the subtotal is 0. Maybe it has something to do with not utilizing the invoice? But I'm not sure how to go even about that. It seems like it is taking the discount amount and subtracting it from the subtotal + tax amount. Also all of the credit memo totals (subtotal, adjustment refund, adjustment fee, tax, grand total) are $0.00.

The math I'm doing to manually set the values for $mcmi (Mage Credit Memo Item) seems sketchy. I would think that Magento would set these for me given the qty I set it to, but it seems like it does not so I have to do it myself. If there is a better way to set these values, I would appreciate the help there too.

2
  • I haven't tried this so far, but the only advice I can give you is to follow (and replicate) the code used in the controller for creating a credit memo. This has worked for me so far for creating by code every entity I've ever needed. Take a look at Mage_Adminhtml_Sales_Order_CreditmemoController::saveAction() and Mage_Adminhtml_Sales_Order_CreditmemoController::_initCreditmemo()
    – Marius
    Commented Jul 10, 2013 at 6:35
  • Can we have this for Magento 2?
    – Jackson
    Commented Apr 3, 2017 at 5:08

2 Answers 2

12

Instead of using the converter directly, it would be better to use the order service model instead:

Mage::app('admin');

$order = Mage::getModel('sales/order')->loadByIncrementId('1117575');
$orderItem = $order->getItemsCollection()->getItemByColumnValue('sku', '100-0055-BRO-S');
$service = Mage::getModel('sales/service_order', $order);
$data = array(
    'qtys' => array(
        $orderItem->getId() => 1
    )
);
$creditMemo = $service->prepareCreditmemo($data)->register()->save();

At least that worked fine for me so far.

5
  • Thanks Vinai. This worked for me. Do you know how I can take the shipping and handling fee out of the refund? Commented Jul 11, 2013 at 17:43
  • Ah, I got it. I mimicked the code in $service->prepareCreditmemo() and before I call $cm->collectTotals(), I set the shipping amounts to 0. Thanks again! Commented Jul 11, 2013 at 18:00
  • 1
    If you set $data['shipping_amount'] = 0 it should do the trick as well without having to duplicate the code, see Mage_Sales_Model_Service_Order::_initCreditmemoData()
    – Vinai
    Commented Jul 12, 2013 at 11:06
  • 1
    I would suggest to use Mage::getModel('core/resource_transaction') ->addObject($memo) ->addObject($order) ->save(); instead of $invoice->save(). That way you are saving the total_refunded and similar data in the orders table. Commented Apr 19, 2016 at 13:53
  • Can we have this for Magento 2?
    – Jackson
    Commented Apr 3, 2017 at 5:07
7

If you need to refund online you should use as this way:

$order = Mage::getModel('sales/order')->loadByIncrementId('1117575');
$service = Mage::getModel('sales/service_order', $order);

$creditmemo = $service->prepareInvoiceCreditmemo($invoice);
$creditmemo->setRefundRequested(true)
    ->setOfflineRequested(false) // request to refund online
    ->register();

Mage::getModel('core/resource_transaction')
    ->addObject($creditmemo)
    ->addObject($creditmemo->getOrder())
    ->addObject($creditmemo->getInvoice())
    ->save();
2
  • Can we have this for Magento 2?
    – Jackson
    Commented Apr 3, 2017 at 5:07
  • Can we Refund Orders to Payment Method ? Commented Nov 5, 2020 at 11:08

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.