Automatically invoice/ship/complete order in Magento

Featured Image

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.

16
Top

Care to rate this post?

Author

Branko Ajzele

Ex Inchooer

Worked at Inchoo as a Backend Developer/CTO from 2008 to 2013

Other posts from this author

Discussion 16 Comments

Add Comment
  1. Anutosh Ghosh

    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

  2. Thanks, nice code, exactly what I was looking for!

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

  4. luca

    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

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

  6. graphicmist

    Its giving singleton/module/observer already exist error?? need help on that

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

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

  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. $order->getState() is coming blank. Please help??

  11. Jayanta

    Thanks,

    for this great post…….. :)

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

  13. mmmh, thank you, but….how can i use this code? A noob explanation?
    thanks a lot…

    By(t)e

  14. Alex

    How to send email with invoice to customer and shop admin?

  15. Mike

    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

  16. Balaji

    How to send email with invoice to customer and shop admin?

Add Your Comment

Please wrap all source codes with [code][/code] tags.
Top