Automatically cancel pending orders older than 90 minutes

Featured Image

There are situations where you might with to automatically cancel orders older than 90 minutes. They stay in “pending_payment” state. This usually happen when the customer didn’t complete the checkout due any reason.

We created observer class “Inchoo_Order_Model_Observer” with public method “cancelPendingOrders“.
We are calling this method over cron script, take a look at our config.xml file below and everting will be clear.
In this method we filtered orders which have state “pending_payment” and older than 90 minutes. Also we set limit to 10 orders, because we want to process them in one loop.
Before save order we set new order state to “canceled_pendings“. All canceled orders will have new state “canceled_pendings“.

class Inchoo_Order_Model_Observer
{
	public function cancelPendingOrders()
	{
            $orderCollection = Mage::getResourceModel('sales/order_collection');
 
            $orderCollection
                    ->addFieldToFilter('state', 'pending_payment')
                    ->addFieldToFilter('created_at', array(
'lt' =>  new Zend_Db_Expr("DATE_ADD('".now()."', INTERVAL -'90:00' HOUR_MINUTE)")))
                    ->getSelect()
                    ->order('e.entity_id')
                    ->limit(10)                   
            ;
 
 
           $orders ="";
            foreach($orderCollection->getItems() as $order)
            {
              $orderModel = Mage::getModel('sales/order');
              $orderModel->load($order['entity_id']);
 
 
              if(!$orderModel->canCancel())
				continue;
 
              $orderModel->cancel();
              $orderModel->setStatus('canceled_pendings');
              $orderModel->save();
 
            }
 
 
 
	}
 
}

Below you can see chunk of config.xml file where we set cron execution every 5 minutes.

    <crontab>
        <jobs>
            <order_cancel_orders>
                <schedule><cron_expr>*/5 * * * *</cron_expr></schedule>
                <run><model>order/observer::cancelPendingOrders</model></run>
            </order_cancel_orders>
    </crontab>
</config>

That’s it 😉


About Domagoj Potkoc

Backend Developer

Domagoj is Magento Certified Developer who enjoys playing tennis after long hours in front of computer screen.

Read more posts by Domagoj / Visit Domagoj's profile

44 comments

  1. In cpanel the cron command will look like this only?- order/observer::cancelPendingOrders

  2. How can this be implemented in 1.9.2 version. I am unable to cancel any pending_payment order as invoice has already generated. How to stop invoicing for pending_payment order and cancel them if required.
    Stock quantity should be released on cancellation.

    Thanks

  3. Hi! Would this work on magento 1.9? Does it re-stock items or just cancels the order? Thanks in advance.

  4. HI,
    How about sending an email to the order customer to notify that the order has been pending for too long before canceling the order? Any help with the email sending part of the code?

  5. is there anything like this (auto cancelled order) that work for offline accounting software?

  6. @Artega : I have copied all files and enabled the plugin through backend too.
    However my pending orders are not going to canceled orders state after every 10 mins (configured 10 mins as cron setting to test).
    I am using version 1.7 . Is there anything else to be done besides copying the files and folders and enabling the plugin through backend?

  7. thanks for the code but when it cancels the order it doesn’t stock the items back into system.

    Is there something you can guide me to please.

    Regards

  8. Hi,

    We’ve been indded struggling for ages with orders that are left pending and polluting the backend while decreasing stock for items that will never be shipped!

    So to address this problem, we have developped a community / free extension that makes automatic order cancellation so easier (I guess!).

    I would be very happy to learn that Inchoo’s readers found a ready-to-use way to manage this automatic order cancellation and, dear Inchooers, please feel free to use, reuse, modify the source code or even discuss it!

    You may find this extension here :
    http://www.magentocommerce.com/magento-connect/soon-stockreleaser-annuler-automatiquement-les-commandes-impayees-6365.html

    Have fun magentoing!

  9. Everyone having problem

    exception ‘PDOException’ with message ‘SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘e.entity_id’ in ‘order clause”

    as Longboard

    just put entity_id instead of e.entity_id

  10. Thanks, this has solved the previous issue (I am using 1.3.2.4) – I can see ‘ordermonitor_cron’ entries which are now successful. Despite this, I believe the script is cancelling the wrong orders rather than the ones with a status of ‘Pending Payment’. Please see below:

    3 Oct 2011 15:38:27 | Canceled
    Customer Not Notified
    Payment expired.

    3 Oct 2011 15:38:27 | Complete
    Customer Not Notified

    2 Oct 2011 23:08:44 | To be Processed
    Customer Not Notified
    0000 : The Authorisation was Successful.

    The script appears to cancel ‘To be processed’ orders instead – first completing them then cancelling immediately afterwards. Is there something I’m doing wrong here?

  11. Ahh, now here I have several rows, either with a status of ‘pending’ and a message of NULL, or the more interesting one with a status of ‘error’ with the following message:

    Invalid method Mage_Sales_Model_Order::addStatusHistoryComment(Array
    (
    [0] => Payment expired.
    )
    )

  12. Chris, check cron_schedule table in your database whether there are any rows with ordermonitor_cron in job_code column. For example, execute

    SELECT * FROM cron_schedule WHERE job_code = 'ordermonitor_cron'
  13. I’ve set it to ‘Yes’, and I’ve set up a script to log whenever the cron is running, which shows that it’s running every 15 minutes.

    Really weird, don’t know why it isn’t working…

  14. Chris, make sure your cron is working besides check module configuration in System->Configuration->Sales->OrderMonitor because pending payments are set to cancel after 60 minutes by default!

  15. I’ve cancelled all the backlogged pending orders now, realised it was the better option!

    I’ve installed your module and for some reason it still isn’t playing ball – I placed a pending order at 12.11pm and it hasn’t been cancelled as of yet.

    Is there any way to track what is going wrong for me?

  16. Artega, thanks for the extension!
    Is there anyway to use this code to add a new canceled state like “canceled – pending” too keep track of wich are canceled through the extension (and makes it possible to use that other canceled state like i asked before..)

    /Johan

  17. Chris, I suggest that you deal with old orders as soon as possible because if you delay, the problem will be bigger and it will bite you sooner or later. Avoiding the problem is NOT a solution 🙂

  18. Thanks artega this seems like it’s just what I’m looking for.

    One thing I was hoping to do though was to only cancel pending order which had occured on the current date (I have many backlogged pending orders which would drastically affect my stock count if they were now cancelled).

    I assume I just need an extra filter in the cron – could you help with this?

  19. Hi,
    I have a question after reading this post.

    Is it possible to create a new Order Status that are exactly like the Canceled state, but with another name, something like Canceled2 or whatever.

    The reason i want this i because i want pending online payment orders to be “canceled2” until the payment is finished and the order status is moved forward to processing. This is because in low stock quantity situtation the item can look like its out of stock if the customer goes back to the store to choose another payment method.

    Any hint if this is even possible would be appreciated!

    Regards
    Johan

  20. config.xml

    <config>   
        <modules>
            <Corp_Order>
                <version>0.1.0</version>
         </Corp_Order>
        </modules>
    	<global>
    		<models>
    		<order>
    		<class>Corp_Order_Model</class>
    		</order>
    		</models>
    	</global>
        <crontab>
            <jobs>
                <order_cancel_orders>
                    <schedule><cron_expr>*/5 * * * *</cron_expr></schedule>
                    <run><model>order/observer::cancelPendingOrders</model></run> 
                </order_cancel_orders>
            </jobs>
        </crontab>
    </config>

    But some other error on execution:

    exception ‘PDOException’ with message ‘SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘e.entity_id’ in ‘order clause”

  21. Yes I agree. I don’t know very much of config.xml. A step by step example would really help. I’m learning as i go.

  22. I really really want this in my shop, but i don’t know where to put the code snippets..

    Please do update the post with locations or full files to put into the code.

  23. example of config.xml file is chunk of code, you have to add other elements, blocks, models, helpers and so on.
    Author assumed that you know what is “config.xml” file and what it contains…..

  24. Can some one tell me what Im doing wrong?

    app/etc/modules/ Inchoo_Order.xml

    <config>
        <modules>
            <Inchoo_Order>
                <active>true</active>
                <codePool>local</codePool>
            </Inchoo_Order>
        </modules>
    </config>

    app/code/local/ Inchoo / Order/etc/config.xml

    <config>
    <crontab>
            <jobs>
                <order_cancel_orders>
                    <schedule><cron_expr>*/5 * * * *</cron_expr></schedule>
                    <run><model>order/observer::cancelPendingOrders</model></run>
                </order_cancel_orders>
        </crontab>
    </config>

    app/code/local/ Inchoo / Order /Model/ Observer.php

    <?php 
    class Inchoo_Order_Model_Observer
    {
        public function cancelPendingOrders()
        {
                $orderCollection = Mage::getResourceModel('sales/order_collection');
    
                $orderCollection
                        ->addFieldToFilter('state', 'pending_payment')
                        ->addFieldToFilter('created_at', array(
    'lt' =>  new Zend_Db_Expr("DATE_ADD('".now()."', INTERVAL -'90:00' HOUR_MINUTE)")))
                        ->getSelect()
                        ->order('e.entity_id')
                        ->limit(10)
                ;
    
               $orders ="";
                foreach($orderCollection->getItems() as $order)
                {
                  $orderModel = Mage::getModel('sales/order');
                  $orderModel->load($order['entity_id']);
    
                  if(!$orderModel->canCancel())
                    continue;
    
                  $orderModel->cancel();
                  $orderModel->setStatus('canceled_pendings');
                  $orderModel->save();
    
                }
    
        }
    
    }
    ?> 

    Any help would be greatly appreciated! 🙂

  25. Hi Anton,

    I corrected the post.
    Thank you for comment, I made mistake. It should use php time.

    Please, instead of
    new Zend_Db_Expr(“DATE_ADD(now(), INTERVAL -’90:00′ HOUR_MINUTE)”)
    put:
    new Zend_Db_Expr(“DATE_ADD(‘”.now().”‘, INTERVAL -’90:00’ HOUR_MINUTE)”)

    😉

  26. Cool. But this might just fail if mysql and magento are set to different (default) timezone?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.