Programmatically create order in Magento

Related Inchoo Services

Surprisingly one of the trickiest parts of “under the hood” Magento is how to create order programmatically. At least for me, this was/is the most complex area of Magento development. Reason why it is so difficult is that the order creation process is all but not straightforward. You cannot simply instantiate order model, set some data and call upon the save() method. If you ask me, this is how it should be done.

So why cannot we apply approach like generic one show below?

//$order = new Mage_Sales_Model_Order();
$order = Mage::getModel('sales/order');
$order->setQuote($quoteModelInstance);
$order->setCustomer($customerModelInstance);
$order->setPayment($paymentModelInstance);
$order->setShipping($customerModelInstance->getShippingRelatedInfo());
//...
$order->save();

As it turns out, there are two “types” of order creation process. One is called One Page Checkout or as I like to call it “frontend order creation” and other one is “admin order creation”. Frontend order creation relies heavily on AJAX-ed approach for almost anything, from getting shipping calculations from shipping gateways to handling credit card processing from payment gateways. On one hand it’s a simple, yet extremely complex process to trace and try to simulate into straight forward order creation by your custom code. Little less complex is the admin order creation, or at least it looks like less complex.

Basically if you as an admin try to create an order from Magento, you have these certain steps where you first choose the customer for which you wish to create order, then you choose store, then you choose product, shipping method, payment method, etc. Each of this steps actually manipulates current session values as we are talking about AJAX-ed behavior. So basically Magento models internally use session reading for setting the necessary values in place of having the direct methods by which you yourself can set those values like customer id, store id, etc.

With enough time on your side, strong will and determination one can monitor, analyze and trace the process of such order creation in order to try to execute it with it’s own custom code.
Below you will find an example of code that does exactly that, it programmatically creates order in Magento.

< ?php
 
class Company_Module_Model_HandleOrderCreate extends Mage_Core_Model_Abstract
{
 
private $_storeId = '1';
private $_groupId = '1';
private $_sendConfirmation = '0';
 
private $orderData = array();
private $_product;
 
private $_sourceCustomer;
private $_sourceOrder;
 
public function setOrderInfo(Varien_Object $sourceOrder, Mage_Customer_Model_Customer $sourceCustomer)
{
$this->_sourceOrder = $sourceOrder;
$this->_sourceCustomer = $sourceCustomer;
 
//You can extract/refactor this if you have more than one product, etc.
$this->_product = Mage::getModel('catalog/product')->getCollection()
->addAttributeToFilter('sku', 'Some value here...')
->addAttributeToSelect('*')
->getFirstItem();
 
//Load full product data to product object
$this->_product->load($this->_product->getId());
 
$this->orderData = array(
'session'       => array(
'customer_id'   => $this->_sourceCustomer->getId(),
'store_id'      => $this->_storeId,
),
'payment'       => array(
'method'    => 'checkmo',
),
'add_products'  =>array(
$this->_product->getId() => array('qty' => 1),
),
'order' => array(
'currency' => 'USD',
'account' => array(
'group_id' => $this->_groupId,
'email' => $this->_sourceCustomer->getEmail()
),
'billing_address' => array(
'customer_address_id' => $this->_sourceCustomer->getCustomerAddressId(),
'prefix' => '',
'firstname' => $this->_sourceCustomer->getFirstname(),
'middlename' => '',
'lastname' => $this->_sourceCustomer->getLastname(),
'suffix' => '',
'company' => '',
'street' => array($this->_sourceCustomer->getStreet(),''),
'city' => $this->_sourceCustomer->getCity(),
'country_id' => $this->_sourceCustomer->getCountryId(),
'region' => '',
'region_id' => $this->_sourceCustomer->getRegionId(),
'postcode' => $this->_sourceCustomer->getPostcode(),
'telephone' => $this->_sourceCustomer->getTelephone(),
'fax' => '',
),
'shipping_address' => array(
'customer_address_id' => $this->_sourceCustomer->getCustomerAddressId(),
'prefix' => '',
'firstname' => $this->_sourceCustomer->getFirstname(),
'middlename' => '',
'lastname' => $this->_sourceCustomer->getLastname(),
'suffix' => '',
'company' => '',
'street' => array($this->_sourceCustomer->getStreet(),''),
'city' => $this->_sourceCustomer->getCity(),
'country_id' => $this->_sourceCustomer->getCountryId(),
'region' => '',
'region_id' => $this->_sourceCustomer->getRegionId(),
'postcode' => $this->_sourceCustomer->getPostcode(),
'telephone' => $this->_sourceCustomer->getTelephone(),
'fax' => '',
),
'shipping_method' => 'flatrate_flatrate',
'comment' => array(
'customer_note' => 'This order has been programmatically created via import script.',
),
'send_confirmation' => $this->_sendConfirmation
),
);
}
 
/**
* Retrieve order create model
*
* @return  Mage_Adminhtml_Model_Sales_Order_Create
*/
protected function _getOrderCreateModel()
{
return Mage::getSingleton('adminhtml/sales_order_create');
}
 
/**
* Retrieve session object
*
* @return Mage_Adminhtml_Model_Session_Quote
*/
protected function _getSession()
{
return Mage::getSingleton('adminhtml/session_quote');
}
 
/**
* Initialize order creation session data
*
* @param array $data
* @return Mage_Adminhtml_Sales_Order_CreateController
*/
protected function _initSession($data)
{
/* Get/identify customer */
if (!empty($data['customer_id'])) {
$this->_getSession()->setCustomerId((int) $data['customer_id']);
}
 
/* Get/identify store */
if (!empty($data['store_id'])) {
$this->_getSession()->setStoreId((int) $data['store_id']);
}
 
return $this;
}
 
/**
* Creates order
*/
public function create()
{
$orderData = $this->orderData;
 
if (!empty($orderData)) {
 
$this->_initSession($orderData['session']);
 
try {
$this->_processQuote($orderData);
if (!empty($orderData['payment'])) {
$this->_getOrderCreateModel()->setPaymentData($orderData['payment']);
$this->_getOrderCreateModel()->getQuote()->getPayment()->addData($orderData['payment']);
}
 
$item = $this->_getOrderCreateModel()->getQuote()->getItemByProduct($this->_product);
 
$item->addOption(new Varien_Object(
array(
'product' => $this->_product,
'code' => 'option_ids',
'value' => '5' /* Option id goes here. If more options, then comma separate */
)
));
 
$item->addOption(new Varien_Object(
array(
'product' => $this->_product,
'code' => 'option_5',
'value' => 'Some value here'
)
));
 
Mage::app()->getStore()->setConfig(Mage_Sales_Model_Order::XML_PATH_EMAIL_ENABLED, "0");
 
$_order = $this->_getOrderCreateModel()
->importPostData($orderData['order'])
->createOrder();
 
$this->_getSession()->clear();
Mage::unregister('rule_data');
 
return $_order;
}
catch (Exception $e){
Mage::log("Order save error...");
}
}
 
return null;
}
 
protected function _processQuote($data = array())
{
/* Saving order data */
if (!empty($data['order'])) {
$this->_getOrderCreateModel()->importPostData($data['order']);
}
 
$this->_getOrderCreateModel()->getBillingAddress();
$this->_getOrderCreateModel()->setShippingAsBilling(true);
 
/* Just like adding products from Magento admin grid */
if (!empty($data['add_products'])) {
$this->_getOrderCreateModel()->addProducts($data['add_products']);
}
 
/* Collect shipping rates */
$this->_getOrderCreateModel()->collectShippingRates();
 
/* Add payment data */
if (!empty($data['payment'])) {
$this->_getOrderCreateModel()->getQuote()->getPayment()->addData($data['payment']);
}
 
$this->_getOrderCreateModel()
->initRuleData()
->saveQuote();
 
if (!empty($data['payment'])) {
$this->_getOrderCreateModel()->getQuote()->getPayment()->addData($data['payment']);
}
 
return $this;
}
}

Usage would go something like:

$flatOrderDataObject = new Varien_Object();
$flatOrderDataObject->set(...);
//...
$order = new Company_Module_Model_HandleOrderCreate()
$order->setOrderInfo($flatOrderDataObject);
$order->create();

Please study the above code well. Code covers only one “combination”, adding simple product with one custom option to cart, using “Check/Money” payment method and flat rate shipping.

There are far more complex combinations you might need. Hope this code serves you as a good starting point.

In case you’ll need any help with your Magento development, our team of experts would be happy to review your code and offer insights in building an even better store. Our Magento Technical Audit is the perfect way to start – feel free to get in touch.

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

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

Magento Maximum Allowed Order Amount

Magento Admin Order Notifier Branko Ajzele
Branko Ajzele, | 28

Magento Admin Order Notifier

Automatically invoice/ship/complete order in Magento Branko Ajzele
Branko Ajzele, | 32

Automatically invoice/ship/complete order in Magento

100 comments

  1. how can save the order in the custom payment.
    Is this correct?Order is not stored and i used the code in my response function.

    $order = Mage::getModel(‘sales/order’);
    $order_id = Mage::getSingleton(‘checkout/session’)->getLastRealOrderId();
    $order->loadByIncrementId($order_id);
    $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true, ‘Gateway has authorized the payment.’);
    $order->sendNewOrderEmail();
    $order->setEmailSent(true);

    🙁 please give me solun

    Thnx
    vasa

  2. Hello,

    I want to Create an Order for Bundle Products.

    Can anyone tell me how to maintain the Child and Parent
    Relationship.

    Thanks in Advance.

  3. I am writing a “Recurring ” payment module for asiapay, and the asiapay server it will send back a datafeed to mangeto to create orders periodically,

    How can i create a reucrring order? or how can i create an order that link up with the recurring profile??

  4. @Mike, yep, I see that as well. Looks like the $quote->collectTotals()->save() call is resetting the credit card number. So I went ahead and put that call *after* the $quote->getPayment()->importData() call, and that seemed to resolve it.

  5. Vinai’s script is working fine, but… When I create an order I want all shopping cart and catalog rules to be applied. I tried to assign a coupon code which gives me 10% discount and it is applied correctly. But if I have a rule without code like for example free shipping if over 50$, then the total amount is not decreased and shipping still appears. the same is with fixed amount or % discount rules without coupons. When i debug the quote it says that the 2 rules were applied but the amounts are totally wrong.
    Any ideas/suggestions are highly welcome.
    Thanks in advance

  6. I have been created an order programmatically. It’s has worked with simple products. However, with configure products that have some more options like color, size, bundle items…, it’s doesn’t work. How can I create popup like popup in backend of magento that show in the following image. Do you have any idea?

  7. In Admin Panel, I want to create a New Order for a customer, set a \”custom price\” for that product and then let the user complete checkout of that custom price product.

    At the moment this is not possible.

    It is only possible to create a New Order in Admin, set a \”custom price\” and then on clicking submit, the new order with its custom price is created and appears in the Customer\’s My Account, however in there the customer has no ability to \”Checkout\” that custom price product. How are the customer supposed to pay for the product ? ( Yeah I know outside of magento , etc )

    The point of this is to let the user complete a checkout for a custom price product as normal in Magento, taking advantage of already configured payment methods on the Checkout.As it is now I can create a custom price order, send it to customer\’s My Account and then they cannot pay for it on Magento.

    In New Order in Admin where you set a Custom Price, there is a dropdown that lets you move the product to the Shopping Cart of the user. The command is called \”Move To Shopping Cart\”. However on doing this, the product looses its custom price and appears with its default price in the customer\’s shopping cart.

  8. Great tutorial Branko, thanks for that.
    Maybe someone knows how to duplicate an order? I want to have 2 the same orders but with different ids.
    Any help with that will be appreciate.

  9. @Amr: some products have required custom options (like a shirt MUST have a specified size). You can find this out with $product->getRequiredOptions(). If it’s true, you can get your hands on all the customizable options with

    			$_attributes = $product->getTypeInstance(true)->getConfigurableAttributes($product);
    			foreach($_attributes as $_attribute)
    			{
    print_r($_attribute->getProductAttribute()->getAttributeId()->debug());
    }

    That should be a good starting point to finding the options you’re looking for. Then to add them, expand the ‘add_products’ part of the array from the code from the article and add your custom attributes:

    'product_id' => array(
    		'qty' => quantity,
    		'super_attribute' => array(
    			'attribute_id' => 'value_index',
    			'attribute_id' => 'value_index',
    			'attribute_id' => 'value_index',
    		)
    	),

    Hope it helps

  10. Hi,

    really good post. It as a great starting point for my project. I was just looking for other shipping methods besides just ‘flatrate_flatrate’. I know you can get your hands on ALL shipping methods with

    Mage::getStoreConfig('carriers');

    and parse through the active ones. The only problem is finding out if they’re available for the given address and what the shipping rate will be. I’ve been trying to reverse engineer the process for showing shipping methods for the past few days but it’s been in vain so far.

    Can anyone help me out?

  11. @Amr, check whether the product you are adding to create order has required custom option at backend. Change Requred from Yes to No. I hope this will solve the error at least.

  12. Hello and thanks a lot,
    i got this error when i tried the code “The product has required options”.

    Any idea how to fix?

  13. I wonder if it is possible to do authorize.net transaction using the above script? Any help would be appreciated!

  14. Has anyone used this to create an order with a credit card, or are you all doing check/money order transactions online?

    If you’re doing cc transactions, have you run into any issues with the cid being lost and cards not processing?

  15. I have to place an order for simple product with custom options…… its not working for this kind of order 😛 any help would be appreciated. 🙂

  16. Its working for me for me :).
    But now i have to place an order for simple product with custom options…… its not working for this kind of order 😛 any help would be appreciated. 🙂

  17. Here is My code.

    require_once 'app/Mage.php';
    
    Mage::app();
    
    $quote = Mage::getModel('sales/quote')->setStoreId(Mage::app()->getStore('default')->getId());
    
    if ('do customer orders') { 
    	// for customer orders:
    	$customer = Mage::getModel('customer/customer')
    		->setWebsiteId(1)
    		->loadByEmail('rdongre@raveinfosys.com');
    	$quote->assignCustomer($customer);
    } else {
    	// for guesr orders only:
    	$quote->setCustomerEmail('rdongre@raveinfosys.com');
    }
    
    // add product(s)
    $product = Mage::getModel('catalog/product')->load(9);
    //print_r($product);die();
    $buyInfo = array(
    	'qty' => 1,
    	// custom option id => value id
    	// or
    	// configurable attribute id => value id
    );
    $quote->addProduct($product, new Varien_Object($buyInfo));
    
    $addressData = array(
    	'firstname' => 'Rahul 13',
    	'lastname' => 'Dongre',
    	'street' => '121 test street address',
    	'city' => 'test city',
    	'postcode' => '452369',
    	'telephone' => '963258741',
    	'country_id' => 'US',
    	'region_id' => 12, // id from directory_country_region table
    );
    
    $billingAddress = $quote->getBillingAddress()->addData($addressData);
    $shippingAddress = $quote->getShippingAddress()->addData($addressData);
    
    $shippingAddress->setCollectShippingRates(true)->collectShippingRates()
    		->setShippingMethod('flatrate_flatrate')
    		->setPaymentMethod('checkmo'); // for check money order
    
    $quote->getPayment()->importData(array('method' => 'checkmo'));
    
    $quote->collectTotals()->save();
    //print_r($quote);die();
    
    $service = Mage::getModel('sales/service_quote', $quote);
    $service->submitAll();
    $order = $service->getOrder();
    print_r($order);
    printf("Created order %s\n", $order->getIncrementId());
  18. Hi Vinai,

    I am having following error when i am executing your script. Please let me know what i missed ?
    “Fatal error: Call to a member function getIncrementId() on a non-object in D:\wamp\www\www.mystore_test.com\order_script.php on line 59”

  19. Hello,

    I also used the script of Vinai. Thanks a lot, it works great. I also got the same problem as Emerson (19-04-2011 at 18:52) though: it said ‘Please specify a shipment method’ for the ‘tablerate’ shipment method. The issue appeared to be in my case that you have to set the free_method_weight for the shipping address. I’ve modified the shipping address processing line therefore to $shippingAddress->setFreeMethodWeight(0)->setCollectShippingRates(true)->collectShippingRates()->setShippingMethod($shipping_method)->setPaymentMethod($payment_method);

    Hope it helps someone.

  20. Hi,
    Can we create an order with configurable product ?

    I have referenced http://pastebin.com/8cft4d8v site to create an order with simple product but now i need to create an order with configurable product.

    It looks like we need to store configurable product info into sales oder item table along with simple, i tried to do that but config product is not saved into quote.

    Can you please me suggest me!

    Thanks in advance

    Thanks

  21. Hi,

    I’ve just had to re-import my orders due to a couple of errors that had slipped through, one thing I’ve noticed is that the orders are created and submitted but all the orders are also added to the ‘adandonded carts’ report. I have tried to set the orders complete but they still show as an abandoned cart. Should there be a bit of code at the end of the order import script that empties the customers cart after submitting the order?

  22. I’ve modified script to add more than one product and adding orders in the loop, but Each order after first has products from previous orders, and newly added products has price = 0.00, and whole order is 0 too. Probably becouse it’s using singleton.

    How other import are made that they allow to import more than one order?

  23. Hi Vinai,

    One more quick question to you…

    Does shipping price also calculated automatically depending on shipping address & products similar to Tax?

  24. Suresh,

    each product is associated with a tax class ID, as is every customer group (see the admin manage products and manage customer interfaces for details).
    Then set up your tax rules under Sales > Tax, and Magento will calculate the correct tax corresponding to the customers address.
    So during the order creation all you need to do is make sure the customer is associated with the correct customer group and has the correct shipping/billing address (depending on your settings).

  25. Hi Branko / Vinai Kopp,

    For more clarity,

    I added tax rates for CA thru Sales => Tax => Tax Zones & Rate section.

    Now, how can i apply this tax rate for california customers when i create orders programmatically?

    Please advice.

    Thank you!

  26. Hi Branko,

    How can I add Taxes to the programmatically created order? for example, if i would like to add sales tax for California people – 8.5%, how can i add it?

    Thank you in advance!

  27. Thank you Branko and Vinai. Code works like a charm.

    I used this to create a custom checkout method, implemented directly in product view page.

  28. I got this working fine and then tried it on my live server and got the same error. I think it was because I had modified the shipping methods and duplicated the flatrate several times, this seemed to break everything though and I couldnt set freeshipping or any of the others. I got around it by commenting out the line in …/…/../order.php or whatever file it refers to in the error. The script then worked and I just uncommented the line after import.

  29. Hi Vinai,

    I trying to use the script nut I get the msg Uncaught exception ‘Mage_Core_Exception’ with message ‘Please specify a shipping method.’. Why?

  30. Right, I’ve figured out a way to override the price but I cant figure out a way to add in a discount to the order. Can anyone help me?

  31. Hi,

    Gunnar – I think you need to set the catalog prices to include tax in your Magento backend within system > config.

    Im using Vinai’s method but my problem is that the orders Im creating are all the back orders from the old site and many of the prices of the products have changed since then and so I need to override the price pulled fom the product with the price paid at the time of purchase.

    Can anyone help me with this?

    Thanks

  32. Hi! I tried the approach that Vinai mentioned and it seems to be a pretty good solution for programmatic order creation.

    Unfortunatly the sales tax is always at 0%. I also tried to set the tax class id on the product, but then the product gets more expensive but the Magento backend still says that 0% taxes are included. What can i do to create orders with the right Tax class?

  33. Hi Branko,
    thanks for your reply.

    Unfortunately I can’t use the checkout process because I need also to update orders with csv files. It will be a mess, i know, but the client has that exigency and we have to make significant changes to the core in order to manage updates.

    Anyway, I can’t use checkout mode but I need to use admin mode. I found a method in Admin class called something like “updateQuote” in which it checks also if the product contains custom_price option. I putted this option inside product array and I tried also to call the method in your _processQuote method after addProducts() instruction but the options isn’t interpreted and the order returns the original price.

  34. Hi Branko,
    I found your post illuminating for my CSV import orders module.

    I’ve only a doubt in order to create my module. Could I set custom price and custom quantiy shipped for each product item somewhere with that class?

  35. I’m confused on this:

    $flatOrderDataObject = new Varien_Object();
    $flatOrderDataObject->set(...);
    //...
    $order = new Company_Module_Model_HandleOrderCreate()
    $order->setOrderInfo($flatOrderDataObject);
    
    public function setOrderInfo(Varien_Object $sourceOrder, Mage_Customer_Model_Customer $sourceCustomer)

    do you need the Varien_Object ?

  36. Thanks for the code snippet. It seems that Vinai’s code is simpler. Any idea which method is faster?

  37. I prefer creating a quote model, adding products using $quote->addProduct(…) (I find it easier to add configurable products or custom product options that way, too), and then calling $order = Mage::getModel(‘sales/service_quote’)->submitAll() ;
    Of course the shipping and billing address and the payment and shipping method need to be set on the quote as well.
    Just thought I’d post that as another alternative method of programmatical order creation.

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.