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– Fabian BlechschmidtJun 12, 2013 at 13:52
-
@mageUz, have you worked below answer?. it's not worked for me.. Can post your code?– Manoj KumarDec 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.– mageUzDec 15, 2014 at 13:48
-
@mageUz, when use this code Shopping Cart is Empty showing.. Any suggestions..– Manoj KumarDec 17, 2014 at 13:22
-
Hi that can be done using marketplace split cart module store.webkul.com/Magento-Marketplace-Split-Cart.html . Thanks– webkulJun 1, 2015 at 11:43
2 Answers
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) {
$quote->getItemsCollection()->removeItemByKey($item->getId());
}
foreach ($items as $item) {
$quote->addItem($item);
}
// Update totals for vendor
$quote->setTotalsCollectedFlag(false)->collectTotals();
// Delegate to parent method to place an order for each vendor
parent::saveOrder();
}
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/… 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 Feb 26, 2014 at 5:41
-
1Roy, 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.– VinaiJun 7, 2014 at 7:10 -
3I 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'); 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 ♦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();
$quote->unsReservedOrderId();
$quote->reserveOrderId();
// 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')
->setQuoteFilter($quote->getId())
->addFieldToFilter('address_type', $addressType)
->getFirstItem();
}
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);
$address->setQuote($quote)
->setWeight($weight)
->setSubtotal(0)
->setBaseSubtotal(0)
->setGrandTotal(0)
->setBaseGrandTotal(0)
->setCollectShippingRates(true)
->collectTotals()
->collectShippingRates()
;
$convertQuote = Mage::getSingleton('sales/convert_quote');
$order = $convertQuote->addressToOrder($address);
$order->setBillingAddress(
$convertQuote->addressToOrderAddress($quote->getBillingAddress())
);
if ($address->getAddressType() == 'billing') {
$order->setIsVirtual(1);
} else {
$order->setShippingAddress($convertQuote->addressToOrderAddress($address));
}
$order->setPayment($convertQuote->paymentToOrderPayment($quote->getPayment()));
if (Mage::app()->getStore()->roundPrice($address->getGrandTotal()) == 0) {
$order->getPayment()->setMethod('free');
}
foreach ($quoteItems as $quoteItem) {
$orderItem = $convertQuote->itemToOrderItem($quoteItem); // use quote_item to transfer is_qty_decimal
if ($quoteItem->getParentItem()) {
$orderItem->setParentItem($order->getItemByQuoteItemId($quoteItem->getParentItem()->getId()));
}
$order->addItem($orderItem);
}
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)
{
$this->validate();
$isNewCustomer = false;
switch ($this->getCheckoutMethod()) {
case self::METHOD_GUEST:
$this->_prepareGuestQuote();
break;
case self::METHOD_REGISTER:
$this->_prepareNewCustomerQuote();
$isNewCustomer = true;
break;
default:
$this->_prepareCustomerQuote();
break;
}
if ($isNewCustomer) {
try {
$this->_involveNewCustomer();
} catch (Exception $e) {
Mage::logException($e);
}
}
$quote = $this->getQuote()->save();
$orderIds = array();
Mage::getSingleton('core/session')->unsOrderIds();
$this->_checkoutSession->clearHelperData();
/**
* 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);
$order->place();
$order->save();
Mage::dispatchEvent('checkout_type_onepage_save_order_after',
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()) {
$order->sendNewOrderEmail();
}
$orderIds[$order->getId()] = $order->getIncrementId();
}
Mage::getSingleton('core/session')->setOrderIds($orderIds);
// add order information to the session
$this->_checkoutSession
->setLastQuoteId($quote->getId())
->setLastSuccessQuoteId($quote->getId())
->setLastOrderId($order->getId())
->setRedirectUrl($redirectUrl)
->setLastRealOrderId($order->getIncrementId());
// as well a billing agreement can be created
$agreement = $order->getPayment()->getBillingAgreement();
if ($agreement) {
$this->_checkoutSession->setLastBillingAgreementId($agreement->getId());
}
// 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();
}
$this->_checkoutSession->setLastRecurringProfileIds($ids);
// TODO: send recurring profile emails
}
Mage::dispatchEvent(
'checkout_submit_all_after',
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".. 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
.– kiatngSep 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. 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()
– kiatngOct 1, 2018 at 8:46