7

I need to split my orders for every item (because I need an order status and delivery note for every single item in my cart).

I tried to do this according to following resources:

Magento2 Split Order Based on Vendor

How to split order in magento 2

Magento multiple order on one checkout or order splitting

But I can not get it to work, the first order is created and the second crashes. (Having onepage-checkout enabled)

What am I doing wrong?

Magento/Quote/Model/QuoteManagement.php

public function placeOrder($cartId, PaymentInterface $paymentMethod = null)
{
    $quote = $this->quoteRepository->getActive($cartId);
    if ($paymentMethod) {
        $paymentMethod->setChecks([
            \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_CHECKOUT,
            \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_COUNTRY,
            \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_CURRENCY,
            \Magento\Payment\Model\Method\AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX,
            \Magento\Payment\Model\Method\AbstractMethod::CHECK_ZERO_TOTAL,
        ]);
        $quote->getPayment()->setQuote($quote);

        $data = $paymentMethod->getData();
        $quote->getPayment()->importData($data);
    }

    if ($quote->getCheckoutMethod() === self::METHOD_GUEST) {
        $quote->setCustomerId(null);
        $quote->setCustomerEmail($quote->getBillingAddress()->getEmail());
        $quote->setCustomerIsGuest(true);
        $quote->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID);
    }


    $this->eventManager->dispatch('checkout_submit_before', ['quote' => $quote]);

    $tempQuote = $quote;

    foreach($tempQuote->getAllItems() as $tempItem) {

        foreach($quote->getAllItems() as $item) {

            $quote->getItemsCollection()->removeItemByKey($item->getId());   
        }

        $quote->getItemsCollection()->addItem($tempItem);
        $quote->setTotalsCollectedFlag(false)->collectTotals();
        $order = $this->submit($quote);
        $orders[] = $order;
        $quote = $tempQuote;

        if (null == $order) {
            throw new LocalizedException(
                __('An error occurred on the server. Please try to place the order again.')
            );
        }


        $this->checkoutSession->setLastQuoteId($quote->getId());
        $this->checkoutSession->setLastSuccessQuoteId($quote->getId());
        $this->checkoutSession->setLastOrderId($order->getId());
        $this->checkoutSession->setLastRealOrderId($order->getIncrementId());
        $this->checkoutSession->setLastOrderStatus($order->getStatus());

    }


    $this->eventManager->dispatch('checkout_submit_all_after', ['orders' => $orders, 'quote' => $quote]);

    return $orders;
}

I edited the core files just for testing. I'll build a plugin or an overwrite for the placeOrder method once it works.

  • How you set grandtotal and subtotal of each order after order split? – user00247 Oct 31 '17 at 10:38
  • How can we split orders by using third-party payment gateway?(like PayPal) – user00247 Feb 20 '18 at 10:17
3
+50

I've been checking this on my own Magento 2.2 Enterprise local implementation, and I got it working using this:

    public function placeOrder($cartId, PaymentInterface $paymentMethod = null)
{
    $quote = $this->quoteRepository->getActive($cartId);
    $paymentMethodString = $quote->getPayment()->getMethod(); // edit 19.10.17

    // get data from addresses and remove ids
    $billingAddress = $quote->getBillingAddress()->getData();
    $shippingAddress = $quote->getShippingAddress()->getData();
    unset($billingAddress['id']);
    unset($billingAddress['quote_id']);
    unset($shippingAddress['id']);
    unset($shippingAddress['quote_id']);

    foreach($quote->getAllItems() as $item) {
        // init Quote Split
        $quoteSplit = $this->quoteFactory->create();
        $quoteSplit->setStoreId($quote->getStoreId());
        $quoteSplit->setCustomer($quote->getCustomer());
        $quoteSplit->setCustomerIsGuest($quote->getCustomerIsGuest());
        if ($quote->getCheckoutMethod() === self::METHOD_GUEST) {
            $quoteSplit->setCustomerEmail($quote->getBillingAddress()->getEmail());
            $quoteSplit->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID);
        }

        // save quoteSplit in order to have a quote id for item
        $this->quoteRepository->save($quoteSplit);

        // add item
        $item->setId(null); // init item id for force to be added to quoteSplit collection
        $quoteSplit->addItem($item);

        // set addresses
        $quoteSplit->getBillingAddress()->setData($billingAddress);
        $quoteSplit->getShippingAddress()->setData($shippingAddress);

        // recollect totals into the quote
        $quoteSplit->setTotalsCollectedFlag(false)->collectTotals();

        // set payment method // edit 19.10.17
        $quoteSplit->getPayment()->setMethod($paymentMethodString);
        if ($paymentMethod) {
            $quoteSplit->getPayment()->setQuote($quoteSplit);
            $data = $paymentMethod->getData();
            $quoteSplit->getPayment()->importData($data);
        }

        // dispatch this event as Magento standard once per each quote split
        $this->eventManager->dispatch('checkout_submit_before', ['quote' => $quoteSplit]);
        $this->quoteRepository->save($quoteSplit);
        $order = $this->submit($quoteSplit);
        $orders[] = $order;

        if (null == $order) {
            throw new LocalizedException(
                __('An error occurred on the server. Please try to place the order again.')
            );
        }

    }
    // disable origin quote
    $quote->setIsActive(false);
    $this->quoteRepository->save($quote); // edit 19.10.17

    $this->checkoutSession->setLastQuoteId($quoteSplit->getId());
    $this->checkoutSession->setLastSuccessQuoteId($quoteSplit->getId());
    $this->checkoutSession->setLastOrderId($order->getId());
    $this->checkoutSession->setLastRealOrderId($order->getIncrementId());
    $this->checkoutSession->setLastOrderStatus($order->getStatus());

    $this->eventManager->dispatch('checkout_submit_all_after', ['orders' => $orders, 'quote' => $quote]);

    /**
     * the API declaration and interface describe this function returning int, you can't return an array.
     * in order to do that you will have to create a new end point for that.
     */
    return $order->getId();
}

Please let me know if this fix the issue for you or if you got some error.

  • Thank you for your answer, don't know if it works yet because magento has problems getting the payment method (CE 2.2) . Call to a member function getData() on null on $data = $paymentMethod->getData(); . – TrytoFly Oct 19 '17 at 9:14
  • 2
    Ok got it, had a few changes to make, but you saved me! I'll edit the post with thouse little changes, hope someone else can also profit from this! – TrytoFly Oct 19 '17 at 15:08
  • That's great TrytoFly, thanks for reply and fix the issue for sure will help others, I was out yesterday so I found this today. – Matias Hidalgo Oct 20 '17 at 4:15
  • @MatiasHidalgo How can I set grand total and subtotal of each order after order split? – user00247 Oct 31 '17 at 10:37
  • @user00247 the line "$quoteSplit->setTotalsCollectedFlag(false)->collectTotals()" should be doing that work, if you want a custom grand total amount should be better to add a custom total or apply some discount into the quote items... just thinking if you just set GT and ST will probably cause errors later – Matias Hidalgo Nov 3 '17 at 4:08
0

You can easily do this courtesy of multishipping provided with magento 2 which separate order for each item product in cart, if you want to implement for normal checkout too then you just have to apply plugin on Magento\Quote\Model\Quote\Item class and its RepresentProduct function; Paste this code in your di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Quote\Item">
        <plugin name="orders-quote-item" type="Vendor\Module\Plugin\Quote\Item" />
</type>
</config>

And in plugin i.e Item.php paste following code

use Magento\Quote\Model\Quote\Item as QuoteItem;
class Item
{
    public function aroundRepresentProduct(QuoteItem $subject, \Closure $proceed, $product)
    {
        return false;
    }
}

All Done. Feel free to ping me if you have any other problem.

  • The suggested plugin is for adding one item at a time to the cart, but what I would need is some way to split the order into multiple orders not just multiple items in my cart. Though still a helpful plugin. – TrytoFly Oct 17 '17 at 8:54
  • @TrytoFly sorry I got your question wrong, should I delete this answer if it wasn't helpful? – Vivek Kumar Oct 17 '17 at 10:12
  • I'd leave it, maybe someone will stumble upon this thread searching for exactly that plugin. – TrytoFly Oct 19 '17 at 15:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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