Store products are supplied from different vendors. It is required to create multiple order for every vendor based on its products in the cart during one checkout. Is there any extension to achieve this task or should I start to develop custom checkout module. What about hot points for creating such extension vision of experienced developers of Magento? Can you explain me brief checkout flow architecture Magento friendly (as possible as code level)? Thanks much more!

  • Have a look on the Multishipping which magento offers out of the box. And there is dropship but I have no idea whether it is good or what it is able to do. unirgy.com/products/udropship Commented Jun 12, 2013 at 13:52
  • @mageUz, have you worked below answer?. it's not worked for me.. Can post your code? Commented Dec 15, 2014 at 11:46
  • @ManojKumar, yes I have already implement order splitting using multiaddress checkout logic, I am sure that the logic given below also works perfectly.
    – mageUz
    Commented Dec 15, 2014 at 13:48
  • @mageUz, when use this code Shopping Cart is Empty showing.. Any suggestions.. Commented Dec 17, 2014 at 13:22
  • Hi that can be done using marketplace split cart module store.webkul.com/Magento-Marketplace-Split-Cart.html . Thanks
    – webkul
    Commented Jun 1, 2015 at 11:43

2 Answers 2


It is doable quite easily with a rewrite of the checkout/type_onepage model.
In that class override the saveOrder() method as follows:

public function saveOrder()
    $quote = $this->getQuote();

    // First build an array with the items split by vendor
    $sortedItems = array();
    foreach ($quote->getAllItems() as $item) {
        $vendor = $item->getProduct()->getVendor(); // <- whatever you need
        if (! isset($sortedItems[$vendor])) {
            $sortedItems[$vendor] = $item;
    foreach ($sortedItems as $vendor => $items) {
        // Empty quote
        foreach ($quote->getAllItems() as $item) {
        foreach ($items as $item) {
        // Update totals for vendor

        // Delegate to parent method to place an order for each vendor
    return $this;

But be aware that in Magento a payment is associated with an invoice, and each invoice is associated with an order.

In consequence this means that as soon as you have multiple orders, you will also have split the payments. So this is only feasible if the payment method doesn't require user interaction during the payment.

UPDATE: The orginal answer delegated to parent::save() which had to be parent:saveOrder(). It is fixed in the example code now.

  • Would appreciate if you could have a look at my similar question! magento.stackexchange.com/questions/6974/… Commented Aug 20, 2013 at 21:22
  • were you able to split the order using above code, as i am trying to do the same but not without any luck Commented Feb 26, 2014 at 5:41
  • 1
    Roy, of course you have to extend the original class for there to be a parent, but that is simple PHP, it has nothing to do with Magento. The method saveOrder still is present and active in Magento CE 1.9 just like it has always been.
    – Vinai
    Commented Jun 7, 2014 at 7:10
  • 3
    I had a problem with this snippet as the 2 order have the grandtotal and subtotal equal the entire order. After debugging i found out that even removing and re-adding the items, it would just use address cached items after collecting Total form the address... To solve it just clear for each address the items cache: $address->unsetData('cached_items_all'); $address->unsetData('cached_items_nominal'); $address->unsetData('cached_items_nonnominal'); Commented Oct 22, 2015 at 17:25
  • 1
    @Vinai $quote->addItem($item); this code not working. After adding item I echo $quote->getAllItems() with help of foreach loop. But there was no item. Can you help me on this please?
    – Amit Bera
    Commented Mar 30, 2016 at 5:59

The following is tested in CE ver 1.9.0.x

 * Overwrite core
class Vendor_Module_Model_Checkout_Type_Onepage extends Mage_Checkout_Model_Type_Onepage
    protected $_oriAddresses = array();

     * Prepare order from quote_items  
     * @param   array of Mage_Sales_Model_Quote_Item 
     * @return  Mage_Sales_Model_Order
     * @throws  Mage_Checkout_Exception
    protected function _prepareOrder2($quoteItems)
        $quote = $this->getQuote();

        // new instance of quote address
        $quote->setIsMultiShipping(true); // required for new instance of Mage_Sales_Model_Quote_Address
        $address = Mage::getModel('sales/quote_address');
        $weight = 0;
        $addressType = 'billing';
        foreach ($quoteItems as $quoteItem) {
            $address->addItem($quoteItem, $quoteItem->getQty());
            $weight += $quoteItem->getWeight();
            if (!$quoteItem->getIsVirtual()) {
                $addressType = 'shipping';
        // get original shipping address that contains multiple quote_items
        if (!isset($this->_oriAddresses[$addressType])) {
            $this->_oriAddresses[$addressType] = Mage::getResourceModel('sales/quote_address_collection')
                ->addFieldToFilter('address_type', $addressType)
        Mage::helper('core')->copyFieldset('sales_convert_quote_address', 'to_customer_address', $this->_oriAddresses[$addressType], $address);
        Mage::helper('core')->copyFieldset('sales_convert_quote_address', 'to_order', $this->_oriAddresses[$addressType], $address);

        $convertQuote = Mage::getSingleton('sales/convert_quote');
        $order = $convertQuote->addressToOrder($address);

        if ($address->getAddressType() == 'billing') {
        } else {

        if (Mage::app()->getStore()->roundPrice($address->getGrandTotal()) == 0) {

        foreach ($quoteItems as $quoteItem) {
            $orderItem = $convertQuote->itemToOrderItem($quoteItem);  // use quote_item to transfer is_qty_decimal
            if ($quoteItem->getParentItem()) {

        return $order;

     * Overwrite core function
    public function saveOrder()
        $quote = $this->getQuote();
        if ($quote->getItemsCount() > 1) {
            $items = $quote->getAllVisibleItems();
            $group = array();
            $split = array();
            foreach ($items as $item) {
                if (Mage::helper('vendor')->checkSku($item->getSku())) {
                    $split[] = array($item); // one item per order
                } else {
                    $group[] = $item; // all other items in one order
            if (count($split)) {
                if (count($group)) {
                    $split[] = $group;
                return $this->_splitQuote($split);
        return parent::saveOrder();

     * Split quote to multiple orders
     * @param array of Mage_Sales_Model_Quote_Item
     * @return Mage_Checkout_Model_Type_Onepage
    protected function _splitQuote($split)
        $isNewCustomer = false;
        switch ($this->getCheckoutMethod()) {
            case self::METHOD_GUEST:
            case self::METHOD_REGISTER:
                $isNewCustomer = true;
        if ($isNewCustomer) {
            try {
            } catch (Exception $e) {

        $quote = $this->getQuote()->save();
        $orderIds = array();

         * a flag to set that there will be redirect to third party after confirmation
         * eg: paypal standard ipn
        $redirectUrl = $quote->getPayment()->getOrderPlaceRedirectUrl();

        foreach ($split as $quoteItems) {
            $order = $this->_prepareOrder2($quoteItems);
                array('order'=>$order, 'quote'=>$quote));
             * we only want to send to customer about new order when there is no redirect to third party
            if (!$redirectUrl && $order->getCanSendNewEmailFlag()) {
            $orderIds[$order->getId()] = $order->getIncrementId();


        // add order information to the session

        // as well a billing agreement can be created
        $agreement = $order->getPayment()->getBillingAgreement();
        if ($agreement) {

        // add recurring profiles information to the session
        $service = Mage::getModel('sales/service_quote', $quote);
        $profiles = $service->getRecurringPaymentProfiles();
        if ($profiles) {
            $ids = array();
            foreach ($profiles as $profile) {
                $ids[] = $profile->getId();
            // TODO: send recurring profile emails

            array('order' => $order, 'quote' => $quote, 'recurring_profiles' => $profiles)

        return $this;

IMPORTANT You need to customize your payment methods to retrieve the grand total from the quote.

  • It is working. Thanks. How to group cart quote by vendor? other then "// one item per order".. Commented Sep 27, 2018 at 14:37
  • 1
    $group[] = $item; // all other items in one order can hold multiple items per order, you can easily modify such that each vendor has its own $group.
    – kiatng
    Commented Sep 28, 2018 at 2:28
  • Updated and it is working. But Payment is captured only for the last order(If we split multiple order). How we need to update this? I need to collect combined payment total. Commented Sep 28, 2018 at 9:46
  • The total amount is in the quote object, you can load it with $quote->Mage::getModel('sales/quote')->load($lastOrder->getQuoteId()). Then there are many totals you can retrieve from $quote, one of which is $quote->getGrandTotal()
    – kiatng
    Commented Oct 1, 2018 at 8:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.