Magento 2 – How to Split Order for Every Item

magento2onepage-checkout

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.

Best Answer

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.

Related Topic