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.

  • 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 Jul 10 '13 at 6:35
  • Can we have this for Magento 2? – Jackson Apr 3 '17 at 5:08
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.

| improve this answer | |
  • Thanks Vinai. This worked for me. Do you know how I can take the shipping and handling fee out of the refund? – Nick Rolando Jul 11 '13 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! – Nick Rolando Jul 11 '13 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 Jul 12 '13 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. – Zvonimir Burić Apr 19 '16 at 13:53
  • Can we have this for Magento 2? – Jackson Apr 3 '17 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();
| improve this answer | |
  • Can we have this for Magento 2? – Jackson Apr 3 '17 at 5:07
  • Can we Refund Orders to Payment Method ? – Hamendra Sunthwal Nov 5 at 11:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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