1

I enter formatted text here in Mage_Sales_Model_Order::canCreditMemo() on line 632 Magento calls a function hasForcedCanCreditmemo().

I observe an event that happens before this function and want to make sure that hasForcedCanCreditmemo() returns true.

In my observer I set i order->setForcedCanCreditmemo(true);, however in the canCreditMemo() that function still returns false.

My observer looks as follows:

<?php
class Custom_Sales_Model_Observer
{
    public function forceCreditMemoForFreeTransactions(Varien_Event_Observer $observer)
    {
        $orderId = $observer->getControllerAction()->getRequest()->getParam('order_id');
        $order = Mage::getModel('sales/order')->load($orderId);
        $order->setForcedCanCreditmemo(true);
        $order->save();
    }
}

and the first few lines of the canCreditMemo() function in Mage_Sales_Model_Order look like this:

public function canCreditmemo()
{
    if ($this->hasForcedCanCreditmemo()) {
        return $this->getForcedCanCreditmemo();
    }

When I add debug, I can see that my observer's code runs through before the canCreditMemo() function, but once I get to the canCreditMemo() function, the order no longer has a forced_can_creditmemo property that I assigned in my oberver.

Is there some other way I need to set that property other than setForcedCanCreditmemo()?

EDIT - I have found that setting an existing property to a different value in the observer will carry through to the canCreditMemo() function, although any new properties I set on the order get discarded somewhere along the way.

2

The ForcedCanCreditmemo property you are trying to save to the order is just a flag that can be set and checked for only on the current instance of the order model. There is no database table field for it, so it cannot be saved.

What happens to it? It just gets ignored. To better understand how Magento ORM does this, you can check \Mage_Core_Model_Abstract::save, that calls \Mage_Core_Model_Resource_Db_Abstract::save, that calls \Mage_Core_Model_Resource_Db_Abstract::_prepareDataForSave, that calls \Mage_Core_Model_Resource_Abstract::_prepareDataForTable, which will finally filter object properties that do not belong to the table:

$fields = $this->_getWriteAdapter()->describeTable($table);
    foreach (array_keys($fields) as $field) {
        if ($object->hasData($field)) {  //

You have not specified the event you are observing, so first thing is to check if the event on which you hooked passes you the order model instance. If yes, you can do like this:

$order = $observer->getEvent()->getOrder();
$order->setForcedCanCreditmemo(true);

If you are lucky (this meaning the event you hooked to is dispatched with the same order model instance as the one on which the hasForcedCanCreditmemo() is called), this should work.

Aside from this, what are you trying to do? From your observer method name it seems that you are trying to allow CreditMemo for free transactions. This should be allowed by core functionality, by looking at this method: \Mage_Payment_Model_Observer::salesOrderBeforeSave:

/**
 * Set forced canCreditmemo flag
 *
 * @param Varien_Event_Observer $observer
 * @return Mage_Payment_Model_Observer
 */
public function salesOrderBeforeSave($observer)
{
    $order = $observer->getEvent()->getOrder();

    if ($order->getPayment()->getMethodInstance()->getCode() != 'free') {
        return $this;
    }

    if ($order->canUnhold()) {
        return $this;
    }

    if ($order->isCanceled() || $order->getState() === Mage_Sales_Model_Order::STATE_CLOSED) {
        return $this;
    }
    /**
     * Allow forced creditmemo just in case if it wasn't defined before
     */
    if (!$order->hasForcedCanCreditmemo()) {
        $order->setForcedCanCreditmemo(true);
    }
    return $this;
}

Hope this gives you a hint for what you are trying to achieve.

UPDATE: After a bit of more in-depth looking I see indeed that Creditmemo for free transactions is not possible but I guess it's needed to return items to stock for inventory management purposes. So, here's a solution to this:

Adjust your config.xml file to include:

<config>
    <global>
        <models>
            <matgento_refundfree>
                <class>Matgento_RefundFree_Model</class>
            </matgento_refundfree>
        </models>
    </global>
    <adminhtml>
        <events>
            <sales_order_load_after>
                <observers>
                    <matgento_refundfree>
                        <type>model</type>
                        <class>matgento_refundfree/observer</class>
                        <method>forceRefundFree</method>
                    </matgento_refundfree>
                </observers>
            </sales_order_load_after>
        </events>
    </adminhtml>
</config>

And the observer class:

class Matgento_RefundFree_Model_Observer {
    public function forceRefundFree( Varien_Event_Observer $observer ) {
        $order = $observer->getEvent()->getOrder();
        if($order->getPayment()->getMethodInstance()->getCode() === 'free') {
            $order->setForcedCanCreditmemo(true);
        }
        return $this;
    }
}

Adjust it to your module, add the extra checks (check if paid, check all other conditions that core code does, except our allow free) on the observer method and it will do the job.

|improve this answer|||||
  • Thanks for the detailed answer! I'm hooking into controller_action_predispatch_adminhtml and it does not, unfortunately, get the order instance passed to it :( You are also correct - my aim is to allow CreditMemo for free transactions. However, this is not allowed, since the order being saved in salesOrderBeforeSave is also not passed through to the canCreditMemo function in Mage_Sales_Model Order. I'll keep looking for a solution, thanks for the help :) – Skytiger Sep 8 '16 at 7:02
  • I've updated the answer with a solution that should fit your needs. – Mihai MATEI Sep 15 '16 at 8:56
  • I'm marking your answer as accepted. I eventually chose to abandon the observer, and just rewrite the canCreditMemo function, but I try out your method and it does work, so in case anyone else finds this question they will have an answer available :) – Skytiger Sep 15 '16 at 9:30

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.