Automatically invoice/ship/complete order in Magento

Automatically invoice/ship/complete order in Magento

Various merchants, various demands. Imagine you have a private on site sale or something like that, where your checkout requirements are pretty simply: create invoice / ship and complete the order all at once. For example, you are doing the checkout for bunch of people standing in front of you, paying you money right on the spot. In such scenario overload of manually creating an invoice and shipment can be too much.

Thus, having your Magento automatically invoice/ship/complete orders can be a logical request. So how do we do that?

Easy! All you need to do is to observe the sales_order_save_after event or observe the controller_action_predispatch event and target the Mage_Checkout_OnepageController::successAction() catching the Mage::getSingleton(‘checkout/session’)->getLastOrderId(). In this example I decided to demonstrate the possible (not ideal, or not even the best) “sales_order_save_after event” approach.

If we where to code everything in form of a module/extension, then all we need are two files. Here is the content of config.xml of our module:

<config>
<modules>
<Inchoo_Invoicer>
<version>1.0.0.0</version>
</Inchoo_Invoicer>
</modules>
<global>
<models>
<inchoo_invoicer>
<class>Inchoo_Invoicer_Model</class>
</inchoo_invoicer>
</models>
<events>
<sales_order_save_after>
<observers>
<inchoo_invoicer_automatically_complete_order>
<class>inchoo_invoicer/observer</class>
<method>automaticallyInvoiceShipCompleteOrder</method>
</inchoo_invoicer_automatically_complete_order>
</observers>
</sales_order_save_after>
</events>
</global>
</config>

And here is the code for our observer model.

class Inchoo_Invoicer_Model_Observer
{
/**
* Mage::dispatchEvent($this->_eventPrefix.'_save_after', $this->_getEventData());
* protected $_eventPrefix = 'sales_order';
* protected $_eventObject = 'order';
* event: sales_order_save_after
*/
public function automaticallyInvoiceShipCompleteOrder($observer)
{
$order = $observer->getEvent()->getOrder();
 
$orders = Mage::getModel('sales/order_invoice')->getCollection()
->addAttributeToFilter('order_id', array('eq'=>$order->getId()));
$orders->getSelect()->limit(1);
 
if ((int)$orders->count() !== 0) {
return $this;
}
 
if ($order->getState() == Mage_Sales_Model_Order::STATE_NEW) {
 
try {
if(!$order->canInvoice()) {
$order->addStatusHistoryComment('Inchoo_Invoicer: Order cannot be invoiced.', false);
$order->save();
}
 
//START Handle Invoice
$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice();
 
$invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_OFFLINE);
$invoice->register();
 
$invoice->getOrder()->setCustomerNoteNotify(false);
$invoice->getOrder()->setIsInProcess(true);
$order->addStatusHistoryComment('Automatically INVOICED by Inchoo_Invoicer.', false);
 
$transactionSave = Mage::getModel('core/resource_transaction')
->addObject($invoice)
->addObject($invoice->getOrder());
 
$transactionSave->save();
//END Handle Invoice
 
//START Handle Shipment
$shipment = $order->prepareShipment();
$shipment->register();
 
$order->setIsInProcess(true);
$order->addStatusHistoryComment('Automatically SHIPPED by Inchoo_Invoicer.', false);
 
$transactionSave = Mage::getModel('core/resource_transaction')
->addObject($shipment)
->addObject($shipment->getOrder())
->save();
//END Handle Shipment
} catch (Exception $e) {
$order->addStatusHistoryComment('Inchoo_Invoicer: Exception occurred during automaticallyInvoiceShipCompleteOrder action. Exception message: '.$e->getMessage(), false);
$order->save();
}
}
 
return $this;
}
}

Before I go any further, I must emphasize that there is a certain known buggy code within automaticallyInvoiceShipCompleteOrder method :). This is to keep the example simple. Meaning this code should be written with more detailed checks if it where to go on live site. As you can see, the first thing I’m doing above is looking into the sales/order_invoice collection to check if there are any existing invoices created for the particular order. If there are none, then I proceed with creating both invoice and shipment.

Next in line is the simple check for order state, IF “new” (which means created right now) then do the entire process of invoicing and shipment. This IF condition is a good place if you wish ti limit this code to orders created by specific Payment gateway, for example Check/Money which has the “checkmo” code, in which case all you need to do is call if ($order->getPayment()->getMethod == ‘checkmo’), etc. The above code was tested on Magento 1.6.2.0. If you plan using it on some live site, please test it thoroughly before and adjust it to your specific needs.

Cheers.

You made it all the way down here so you must have enjoyed this post! You may also like:

Adding custom credit card type to your payment method in Magento Toni Peric
Toni Peric, | 2

Adding custom credit card type to your payment method in Magento

Magento Maximum Allowed Order Amount Branko Ajzele
Branko Ajzele, | 25

Magento Maximum Allowed Order Amount

Magento’s “Quote/Order/Invoice” workflow Branko Ajzele
Branko Ajzele, | 38

Magento’s “Quote/Order/Invoice” workflow

32 comments

  1. i have not work this type of error please help me or mail abdulkarimkhorajiya@gmail.com

    2018-04-17T05:21:18+00:00 ERR (3): User Error: Some transactions have not been committed or rolled back in C:\xampp\htdocs\magentoone\lib\Varien\Db\Adapter\Pdo\Mysql.php on line 4039

  2. This is working fine in Magento Ver: 1.9.3.3 but only New Order email sent to the customer when invoice and shipment mail not sent to the customer automatically.

    If you have any solution for direct sent invoice email and shipment email then please share here.

    1. Please share your some code here if possible.

      Because for me 1.9.3.3 version not working right now that is why.

  3. I’m using a similar script.
    Upgrading Magento from 1.7.0.2 to 1.9 a get a slow execution.

    Before upgrading the script processed about 3 order per second, after the upgrade the script takes about 2/3seconds per order… so 6 times slower.

    The slow line is

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

    In particular the save() method takes 2 seconds.

    I check for slow query in DB but no query is tracked.

    Could you give me a suggestion to solve this isssue?

    My script must process about 600 order and times are too longer.

    Please help me

    Thanks

  4. Hello,
    I tried it, but it doesn’t work.
    First test i did it removed the invoice button and after that the shipment was not created also the invoice was not.
    After the first test it doesn’t even do that.
    I created the files in app/code/local/inchoo_invoicer
    2 files observer.php and config.xml
    How can i make it work?

    Thanks in advance.
    Btw i have magento 1.9.2

  5. thank you for your code.
    but it seems have a bug when use paypal express payment, because the state is “process” when place a new order by paypal express payment. so it will lead to the if statement if ($order->getState() == Mage_Sales_Model_Order::STATE_NEW) is become to invalid.
    have some way to fix the bug?
    thanks!

  6. Is it possible create a “order shipped” status and use it through an automatic way on orders that already were shipped by the carrier (according to the tracking info) in Magento? This status is a “must have” status, and Magento does not have it by default. It’s very important to keep the orders updated for the sales team and would allow us to check with the carrier company if something wrong happened with the shipping, like because some thunderstorm, traffic, whatever… Most large ecommerce business has a “Order Shipped” status, it’s essential for sales management.

  7. Great Post!

    I use magento 1.5.1

    I am trying to set the order status to “Complete” and do not want to notify the customer when the order is set to complete.

    I used setState(‘complete’,true,”,false) method. The order status changed to “Complete”
    But, in magento admin back-end, under “comment history”, I see the below text
    Aug 20, 2013 9:05:54 AM | Complete | Customer Notified.
    Since I used false (4th parameter in setState method), it is supposed to say “Customer not notified” right?
    Any thoughts please?

    Thanks

  8. Thanks for the post, Branko

    Recently I’ve used this for auto-shipping and faced an issue that order wasn’t changing to ‘Complete’ status, this is due to in second transaction order doesn’t have data changed, to force save it I had to do $shipment->getOrder()->setHasDataChanges(true)

  9. Great post BTW, it was really helpful!

    I notice that it seems to be running through the function twice though, do you know why it would be doing this?

  10. Just wanted to point out something missing from main post but that gregc corrected.

    If you are wanting to filter by payment option you need to make sure there are double brackets after the getMethod

    eg

    if ($order->getPayment()->getMethod() == ‘checkmo’)

    These are missing in the original post

  11. Hi,

    We have master/slave setup. It is working fine in UAT server. But the same is not working in LIVE. We are getting “Can not create invoice. Order is not found”.

    Once i remove the master slave set up from local xml it is working fine.

    So its like a bottle neck.

  12. Great tip!

    Easily customized to auto-invoice Check/Money Order payments by changing the if statement in the observer code to

    if (($order->getState() == Mage_Sales_Model_Order::STATE_NEW) &&
    ($order->getPayment()->getMethod() == ‘checkmo’)) {

    It’s great to see simple, working examples of code.

  13. Hi, I tried to build this module, but I get this error:
    Mage registry key “_singleton / inchoo_invoicer_model / observer” already exists
    click on the ‘Place Order’ and pay with PayPal, I think it does well with other methods of payment

  14. We’re doing something similar but with EDI, how do you guys catch non-invoicable errors? We have this relatively ambiguous block of code,

    if (!$order->canInvoice()) {
    $this->_fault(‘data_invalid’, Mage::helper(‘sales’)->__(‘Cannot do invoice for order.’));
    }

    That really doesn’t tell you why it can invoice.

  15. First of all, thanks a lot for such a wonderful article with the code details. Appreciate!

    I have mainly 2 queries, which I’m writing it down. Please comment in details, if possible.

    In the Observer class, what is the purpose of line – 50, and what will happen if I don’t provide this line:-

    $order->setIsInProcess(true);

    Also why is this only mentioned for the Shipment handle like this

    $order->setIsInProcess(true);

    and not something like

    $shipment->getOrder()->setIsInProcess(true);

    (just like the Invoice handle of line – 36)?

    Thanks & Regards,
    Anutosh

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>.

Tell us about your project

Drop us a line. We'd love to know more about your project.