0

I'm developing a Multivendor marketplace module. I need to split orders based on seller id's which assigned to the product. I tried using this free module. Link But this is not working with attributes. So I tried to split with SKU. Where I modified the code as below.

<?php

namespace Magestat\SplitOrder\Plugin;

use Magento\Quote\Model\QuoteManagement;
use Magento\Framework\Exception\LocalizedException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Model\QuoteFactory;
use Magento\Framework\Event\ManagerInterface;
use Magestat\SplitOrder\Api\QuoteHandlerInterface;

/**
 * Class SplitQuote
 * Interceptor to \Magento\Quote\Model\QuoteManagement
 */
class SplitQuote
{
    /**
     * @var CartRepositoryInterface
     */
    private $quoteRepository;

    /**
     * @var QuoteFactory
     */
    private $quoteFactory;

    /**
     * @var ManagerInterface
     */
    private $eventManager;

    /**
     * @var QuoteHandlerInterface
     */
    private $quoteHandler;

    /**
     * @param CartRepositoryInterface $quoteRepository
     * @param QuoteFactory $quoteFactory
     * @param ManagerInterface $eventManager
     * @param QuoteHandlerInterface $quoteHandler
     */
    public function __construct(
        CartRepositoryInterface $quoteRepository,
        QuoteFactory $quoteFactory,
        ManagerInterface $eventManager,
        QuoteHandlerInterface $quoteHandler
    ) {
        $this->quoteRepository = $quoteRepository;
        $this->quoteFactory = $quoteFactory;
        $this->eventManager = $eventManager;
        $this->quoteHandler = $quoteHandler;
    }

    /**
     * Places an order for a specified cart.
     *
     * @param QuoteManagement $subject
     * @param callable $proceed
     * @param int $cartId
     * @param string $payment
     * @return mixed
     * @throws LocalizedException
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     * @see \Magento\Quote\Api\CartManagementInterface
     */
    public function aroundPlaceOrder(QuoteManagement $subject, callable $proceed, $cartId, $payment = null)
    {
        $currentQuote = $this->quoteRepository->getActive($cartId);

        // Separate all items in quote into new quotes.
        $quote = $this->quoteRepository->getActive($cartId);
        $quotes = [];
        foreach ($quote->getAllVisibleItems() as $item) {
            $quotes[$item->getProduct()->getSku()][] = $item;
        } //Here where I modified to split with skus

        // Collect list of data addresses.
        $addresses = $this->quoteHandler->collectAddressesData($currentQuote);

        /** @var \Magento\Sales\Api\Data\OrderInterface[] $orders */
        $orders = [];
        $orderIds = [];
        foreach ($quotes as $items) {
            /** @var \Magento\Quote\Model\Quote $split */
            $split = $this->quoteFactory->create();

            // Set all customer definition data.
            $this->quoteHandler->setCustomerData($currentQuote, $split);
            $this->toSaveQuote($split);

            // Map quote items.
            foreach ($items as $item) {
                // Add item by item.
                $item->setId(null);
                $split->addItem($item);
            }
            $this->quoteHandler->populateQuote($quotes, $split, $items, $addresses, $payment);

            // Dispatch event as Magento standard once per each quote split.
            $this->eventManager->dispatch(
                'checkout_submit_before',
                ['quote' => $split]
            );

            $this->toSaveQuote($split);
            $order = $subject->submit($split);

            $orders[] = $order;
            $orderIds[$order->getId()] = $order->getIncrementId();

            if (null == $order) {
                throw new LocalizedException(__('Please try to place the order again.'));
            }
        }
        $currentQuote->setIsActive(false);
        $this->toSaveQuote($currentQuote);

        $this->quoteHandler->defineSessions($split, $order, $orderIds);

        $this->eventManager->dispatch(
            'checkout_submit_all_after',
            ['orders' => $orders, 'quote' => $currentQuote]
        );
        return $this->getOrderKeys($orderIds);
    }

    /**
     * Save quote
     *
     * @param \Magento\Quote\Api\Data\CartInterface $quote
     * @return \Magestat\SplitOrder\Plugin\SplitQuote
     */
    private function toSaveQuote($quote)
    {
        $this->quoteRepository->save($quote);

        return $this;
    }

    /**
     * @param array $orderIds
     * @return array
     */
    private function getOrderKeys($orderIds)
    {
        $orderValues = [];
        foreach (array_keys($orderIds) as $orderKey) {
            $orderValues[] = (string) $orderKey;
        }
        return array_values($orderValues);
    }    
}

but it returns the below error in the console

Failed to load resource: the server responded with a status of 500 (Internal Server Error)

/master/bike/rest/bike/V1/carts/mine/payment-information:1 
<message>
The consumer isn't authorized to access %resources.
</message>

It shows that customer is not authenticated. The rest api throws error in onepage. I need a experts help. Thanks in advanced.

9
  • The consumer isn't authorized to access %resources. Mean you dont have permission with the api consumer, check this first May 26, 2020 at 2:09
  • All permission is enabled in admin. the issue is with above code
    – Ramesh KR
    May 26, 2020 at 8:31
  • I think you may need to do some customization for Orders. Same as multi-shipping module done in core Magento. You might need to create a module same like that multi-shipping. not sure.
    – kunj
    May 26, 2020 at 14:02
  • can you explain how the payment process is happening with your order? would be good to understand if the payment is taken May 26, 2020 at 14:20
  • 1
    I've done order splitting in multiple projects. The best way to split orders is after the checkout. I split quote items using QuoteManagement plugin aroundSubmit. Then split orders based on these quote items after the checkout using checkout_submit_all_after event. Also one more important point, just splitting the quote will not work. You will have to create new orders using order entity object. May 29, 2020 at 4:49

2 Answers 2

1

In the CheckoutAllSubmitAfterObserver, I split orders based on order(quote) items.

public function execute(EventObserver $observer)
{
    $order = $observer->getEvent()->getOrder();
    $quote = $observer->getEvent()->getQuote();
    if (!$order) {
        return $this;
    }
    $orders = $this->splitOrders($order);
 }

public function splitOrders($order)
{
    $orderItems = $order->getAllItems();
    //rearrange my items based on vendor. so order is split vendor wise.
    $list = [
        'vendor1' => [orderItem 1, orderItem 2],
        'vendor2' => [orderItem 3, orderItem 4],
    ]
    $c = 1;
    $baseShippingAmnt = $order->getBaseShippingAmount();
    if ($baseShippingAmnt) {
        $baseShippingAmnt = round($order->getBaseShippingAmount() / count($list), 4);
    }
    foreach ($list as $vid => $orderItems) {
        if ($c > 1) {
            $newOrder = $this->orderFactory->create();
            $newOrder->setData($this->getRequiredDataOfOrder($order->getData()));
            $payment = $order->getPayment();
            $payment->setId(null);
            $payment->setParentId(null);
            $newOrder->setPayment($payment);

            $addresses = $order->getAddresses();
            foreach ($addresses as $address) {
                $address->setId(null);
                $address->setParentId(null);
                $newOrder->addAddress($address);
            }

            /** Save state and status value for next save to leave order pending */
            $state = $newOrder->getState();
            $status = $newOrder->getStatus();
            $this->orderRepository->save($newOrder);

            foreach ($orderItems as $orderItem) {
                if ($orderItem->getParentItemId()) {
                    $parentOrderItem = $this->orderItemRepository->get($orderItem->getParentItemId());
                    $parentOrderItem->setOrderId($newOrder->getId());
                    $this->orderItemRepository->save($parentOrderItem);
                }
                $orderItem->setOrderId($newOrder->getId());
                $this->orderItemRepository->save($orderItem);
            }
            /** Change state from complete */
            if ($newOrder->getState() != $state || $newOrder->getStatus() != $status) {
                $newOrder->setState($state);
                $newOrder->setStatus($status);
                $this->orderRepository->save($newOrder);
            }

            $order = $this->setRequiredDataOfOrder(
                $orderItems,
                $newOrder,
                $this->setShippingNewAmnt($newOrder, $vid, $baseShippingAmnt)
            );
            $orders[] = $order;
        } else {
            $order = $this->setRequiredDataOfOrder(
                $orderItems,
                $order,
                $this->setShippingNewAmnt($order, $vid, $baseShippingAmnt)
            );
            $orders[] = $order;
        }
        $c++;
    }

    return $orders;
}

private function setShippingNewAmnt($order, $vid, $amount)
{
    //set shipping amount as per your requirements
    $method = $order->getShippingMethod(true);
    return $amount;
}

private function getRequiredDataOfOrder($data)
{
    $unsetKeys = ['entity_id', 'parent_id', 'item_id', 'order_id'];
    foreach ($unsetKeys as $key) {
        if (isset($data[$key])) {
            unset($data[$key]);
        }
    }

    $unsetKeys = ['increment_id', 'items', 'addresses', 'payment'];
    foreach ($unsetKeys as $key) {
        if (isset($data[$key])) {
            $data[$key] = null;
        }
    }

    return $data;
}

private function setRequiredDataOfOrder($orderItems, $order, $baseShippingAmnt)
{
    $totalQty = 0;
    $subTotal = 0;
    $baseSubTotal = 0;
    $subTotalInclTax = 0;
    $baseSubTotalInclTax = 0;
    $discount = 0;
    $baseDiscount = 0;
    $tax = 0;
    $baseTax = 0;

    foreach ($orderItems as $orderItem) {
        if ($orderItem->getParentItemId()) {
            $parentOrderItem = $this->orderItemRepository->get($orderItem->getParentItemId());
            $totalQty += $parentOrderItem->getQtyOrdered();
            $subTotal += $this->priceCurrency->round(
                $parentOrderItem->getQtyOrdered() * $parentOrderItem->getPrice()
            );
            $baseSubTotal += $this->priceCurrency->round(
                $parentOrderItem->getQtyOrdered() * $parentOrderItem->getBasePrice()
            );
            $subTotalInclTax += $this->priceCurrency->round(
                $parentOrderItem->getQtyOrdered() * $parentOrderItem->getPriceInclTax()
            );
            $baseSubTotalInclTax += $this->priceCurrency->round(
                $parentOrderItem->getQtyOrdered() * $parentOrderItem->getBasePriceInclTax()
            );
            if ($parentOrderItem->getDiscountPercent()) {
                $discount += $this->priceCurrency->round(
                    $subTotal * ($parentOrderItem->getDiscountPercent() / 100)
                );
                $baseDiscount += $this->priceCurrency->round(
                    $baseSubTotal * ($parentOrderItem->getDiscountPercent() / 100)
                );
            }
            if ($parentOrderItem->getTaxPercent()) {
                $tax += $this->priceCurrency->round(
                    $subTotal * ($parentOrderItem->getTaxPercent() / 100)
                );
                $baseTax += $this->priceCurrency->round(
                    $baseSubTotal * ($parentOrderItem->getTaxPercent() / 100)
                );
            }
        } else {
            if ($orderItem->getPrice() > 0) {
                $totalQty += $orderItem->getQtyOrdered();
                $subTotal += $this->priceCurrency->round(
                    $orderItem->getQtyOrdered() * $orderItem->getPrice()
                );
                $baseSubTotal += $this->priceCurrency->round(
                    $orderItem->getQtyOrdered() * $orderItem->getBasePrice()
                );
                $subTotalInclTax += $this->priceCurrency->round(
                    $orderItem->getQtyOrdered() * $orderItem->getPriceInclTax()
                );
                $baseSubTotalInclTax += $this->priceCurrency->round(
                    $orderItem->getQtyOrdered() * $orderItem->getBasePriceInclTax()
                );
                if ($orderItem->getDiscountPercent()) {
                    $discount += $this->priceCurrency->round(
                        $subTotal * ($orderItem->getDiscountPercent() / 100)
                    );
                    $baseDiscount += $this->priceCurrency->round(
                        $baseSubTotal * ($orderItem->getDiscountPercent() / 100)
                    );
                }
                if ($orderItem->getTaxPercent()) {
                    $tax += $this->priceCurrency->round(
                        $subTotal * ($orderItem->getTaxPercent() / 100)
                    );
                    $baseTax += $this->priceCurrency->round(
                        $baseSubTotal * ($orderItem->getTaxPercent() / 100)
                    );
                }
            }
        }
    }
    $amountDiscount = $discount;
    $baseAmountDiscount = $baseDiscount;
    if ($discount > 0) {
        $amountDiscount = -$discount;
        $baseAmountDiscount = -$baseDiscount;
    }
    $shippingAmount = $this->priceCurrency->convert($this->priceCurrency->round($baseShippingAmnt));

    $order->setBaseDiscountAmount($baseAmountDiscount);
    $order->setDiscountAmount($amountDiscount);
    $order->setBaseTaxAmount($baseTax);
    $order->setTaxAmount($tax);
    $order->setBaseGrandTotal($baseSubTotal - $baseDiscount + $baseTax + $baseShippingAmnt);
    $order->setGrandTotal($subTotal - $discount + $tax + $shippingAmount);
    $order->setBaseSubtotal($baseSubTotal);
    $order->setSubtotal($subTotal);
    $order->setTotalQtyOrdered($totalQty);
    $order->setBaseSubtotalInclTax($baseSubTotalInclTax);
    $order->setSubtotalInclTax($subTotalInclTax);
    $order->setBaseTotalDue($baseSubTotal - $baseDiscount);
    $order->setTotalDue($subTotal - $discount);
    $order->setBaseShippingAmount($baseShippingAmnt);
    $order->setBaseShippingInclTax($baseShippingAmnt);
    $order->setShippingAmount($shippingAmount);
    $order->setShippingInclTax($shippingAmount);
    $this->orderRepository->save($order);

    return $order;
}

The QuoteManagement Plugin I've separated the quote items based on different vendors. Note- This is only required if one product can be associated to multiple vendors. In my case the vendor was decided by priority.

public function aroundSubmit(
    \Magento\Quote\Model\QuoteManagement $subject,
    \Closure $proceed,
    \Magento\Quote\Model\Quote $quote,
    $orderData = []
) {
    /*@var \Magento\Checkout\Model\Cart $cart */
    $this->cart->setQuote($quote);
    $this->separateItemsVendorBasis();
    $this->save();

    return $proceed($quote, $orderData);
}

//separate items based on your requirement, like vendor wise product
private function separateItemsVendorBasis()
{
    $count = [];
    foreach ($this->cart->getQuote()->getAllItems() as $quoteItem) {
        $itemId = $quoteItem->getId();
        //i get all vendors for that product
        $allVItems = $this->getVendorsProduct($quoteItem->getProduct()->getId());
        if( !isset($count[$itemId]) )
            $count[$itemId] = 1;

        foreach ($allVItems as $item) {
            if ($count[$itemId] == 1) {
                //for existing quote item
                $quoteItem->setVendorId($item->getVendorId());
                if ($quoteItem->getParentItem()) {
                    $quoteItem->getParentItem()->setQty($item->getQty());
                    $quoteItem->getParentItem()->setVendorId($item->getVendorId());
                } else {
                    $quoteItem->setQty($item->getQty());
                }
            } else {
                //creating new quote item
                $info = $quoteItem->getBuyRequest();
                $product = $quoteItem->getProduct();
                if ($quoteItem->getParentItem()) {
                    $product = $quoteItem->getParentItem()->getProduct();
                    $info = $quoteItem->getParentItem()->getBuyRequest();
                }
                $info->setQty($item->getQty());
                $info->setOriginalQty($item->getQty());
                $info->unsetData('uenc');
                $newQuoteItem = $this->cart->addProduct($product, $info);
                $newQuoteItem->setProduct($product);
                $newQuoteItem->setVendorId($item->getVendorId());
                $newQuoteItem->save();
                if ($newQuoteItem->getHasChildren()) {
                    foreach ($newQuoteItem->getChildren() as $newQuoteChildrens) {
                        $newQuoteChildrens->setVendorId($item->getVendorId());
                        $newQuoteChildrens->save();
                    }
                }
            }
        }
    }
}
6
  • So I have to create a observer? CheckoutAllSubmitAfterObserver Can you please mention the event
    – Ramesh KR
    May 30, 2020 at 7:46
  • Yes. this observer is called after the order is placed. May 30, 2020 at 7:47
  • checkout_submit_all_after this event right?
    – Ramesh KR
    May 30, 2020 at 7:47
  • yes. "checkout_submit_all_after" May 30, 2020 at 7:49
  • Thanks for your answer. My code in the question splits the answer but the authorisation error occurs. Have any idea on it?
    – Ramesh KR
    May 30, 2020 at 7:56
0

Please make the below code change and try if it helps!

public function aroundPlaceOrder(QuoteManagement $subject, callable $proceed, $cartId, $payment = null)
    { 

        $quote = $this->quoteRepository->getActive($cartId);
        $paymentMethod = $quote->getPayment()->getMethod();
        $billingAddress = $quote->getBillingAddress()->getData();
        $shippingAddress = $quote->getShippingAddress()->getData();

        ......
        ......

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

            // Split Quote
            $split = $this->quoteFactory->create();
            $split->setCustomer($quote->getCustomer());

            // Save split to get a quote id for each item
            $this->quoteRepository->save($split);

            // Add item
            $item->setId(null);
            $split->addItem($item);

            // Set addresses
            $split->getBillingAddress()->setData($billingAddress);
            $split->getShippingAddress()->setData($shippingAddress);

            // Set payment menthod
            $split->getPayment()->setMethod($paymentMethod);

            ......
            ......

        }

    }

OR

Please set the Allow Anonymous Guest Access setting to "Yes and check if it works! Admin > Stores > Settings > Configuration > Services > Magento Web API > Web API Security.

More, check for user role and authentication token. Or maybe the session (auth token) expires. https://devdocs.magento.com/guides/v2.3/get-started/authentication/gs-authentication-token.html

Also, it could be an issue with the PHP version as well. Please visit the post here.

9

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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