Has anyone figured out how to programatically create an order in Magento 2.1 yet ?

There are a few tutorials kicking around for 2.0 which work nicely but every since I upgraded it no longer works. The error I most often see is:

Exception #0 (Magento\Framework\Exception\NoSuchEntityException): Cart 88 does not contain item 91

Obviously the cart ID and item ID change with every attempt.

I have been looking at admin order create and the only difference is when we hit the file:

vendor/magento/module-quote/Model/QuoteRepository/SaveHandler.php

In the save() function when processing an order in the admin $items = $quote->getItems(); is null whereas when programmatically creating an order we have an item still.

Anyone know why this is happening? Or just know how to create an order?

My class can be found here:
https://gist.github.com/TheFrankman/21fdf70883d4e2d717333ac67f1e0f2d

But there is quite a lot going on. Most logic happens in function createOrder().

If you use the code that's in this question : How to create order programmatically in Magento 2?

You will get the same error. It might be easier to follow than my example


EDIT:

Big thanks to Jaimin Parikh for helping me out with this.

I thought it might be useful to put together a quick blog post about the differences between 2.0 and 2.1 now that I've solved my problem with a more simplified example. Here is the link:

http://frankclark.xyz/magento2-1-programatically-create-order

==============================

Another side note to this if anyone still has problems after the above. You need to us the environment emulation. Various checks in magento core get the current store view, which, if you are running this from a cron for example, will return as store id 0 which may not match the quote that you are creating.

\Magento\Store\Model\App\Emulation $emulation

  • Show your code here. – Sohel Rana Jul 19 '16 at 16:03
  • I have same problem occur in 2.1 . This create an issue in my script because I was saving $quote object ,, after remove that line it works fine for me. But still I am posting script for create order – Keyur Shah Jul 19 '16 at 16:15
  • I have added a gist for my code as there is a lot of it. – Frank Jul 19 '16 at 16:16
  • @KeyurShah could you perhaps paste your code ? – Frank Jul 19 '16 at 16:21
  • 1
up vote 4 down vote accepted

I faced same error while placing order programatically.

I have made some changes in your createOrder() function.

public function createOrder(Subscriptions $subscription, $originalOrder, $originalOrderItemId) {
        /**
         * @var $originalOrder \Magento\Sales\Model\Order
         * @var $quote         \Magento\Quote\Model\Quote
         * @var $customer
         */
        // Firstly make sure we can load the customer
        $customerId = $originalOrder->getCustomerId();
        //$customer = $this->_customerFactory->create()->load($customerId);
        $customer = $this->_customerInterface->getById($customerId);
        if ($customer->getEmail()) {
            // Get some data from the original order that we are going to need
            // @todo : load isn't deprecated it might be later
            $originalStoreId = $originalOrder->getStoreId();
            $store = $this->_store->load($originalStoreId);
            // Create a blank quote
            $quote = $this->_quoteFactory->create()->setStoreId($originalStoreId);

            /**
             * Added by Jaimin
             */
            $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
            $quoteRepository = $objectManager->create(\Magento\Quote\Api\CartRepositoryInterface::class);
            $quoteRepository->save($quote);
            /**
             * End
             */
            // Assign the customer to the quote
            $quote->assignCustomer($customer);
            //@todo : fix error with below - must implement interface Magento\Quote\Api\Data\CurrencyInterface
            //$currency = $originalOrder->getBaseCurrencyCode();
            //$quote->setCurrency($currency);
            //temporary - currency needs to be the purchased currency
            $quote->setCurrency();
            // Build Billing Address
            $billingAddress = clone $originalOrder->getBillingAddress();
            $billingAddress->unsetData('entity_id')->unsetData('parent_id')
                    ->unsetData('customer_address_id')->unsetData('customer_id')
                    ->unsetData('quote_address_id');
            // Build Shipping Address
            // @todo : Customers are going to need the ability to change their shipping address come back and fix this once that's done
            $shippingAddress = clone $originalOrder->getShippingAddress();
            $shippingAddress->unsetData('entity_id');
            // Insert the address details
            $quote->getBillingAddress()->addData($billingAddress->getData());
            $quote->getShippingAddress()->addData($shippingAddress->getData());
            /**
             * @var $subscriptionOrderItem \Magento\Sales\Model\Order\Item
             */
            // Load the subscription product from original order
            $subscriptionOrderItem = $originalOrder->getItemById(
                    $originalOrderItemId
            );
            // Make sure we have a product
            if ($subscriptionOrderItem instanceof SalesOrderItem) {
                // Get params required to add product
                $params = $this->getProductParams($subscriptionOrderItem);
                // Load the product
                $product = $this->getSubscriptionProduct(
                        $subscriptionOrderItem, $originalStoreId
                );
                // Product may have been deleted
                if (!$product) {
                    $message = sprintf(
                            __('Could not load product id %s for subscription %s'), $subscription->getProductId(), $subscription->getId()
                    );
                    $this->_logger->critical($message);
                    throw new LocalizedException($message);
                }
                //$quote->addProduct($product, $params);
                $this->_quoteInitializer->init($quote, $product, $params);
                // Set the product price based on original order incase product price changed
                try {
                    $this->setProductPrices(
                            $quote, $product, $subscription->getProductCost()
                    );
                } catch (LocalizedException $e) {
                    $message = __(
                            'Unable set custom price - cannot release subscription'
                    );
                    $this->_logger->critical($message);
                    throw new LocalizedException($message, $e);
                }
                $quote->setTotalsCollectedFlag(false)->collectTotals();
            } else {
                $message = __(
                                'Could not load original order item for subscription : '
                        ) . $subscription->getId();
                $this->_logger->critical($message);
                throw new LocalizedException(__($message));
            }
            // Collect shipping rates
            $quote->getShippingAddress()->setCollectShippingRates(true)
                    ->collectShippingRates();
            // Apply the correct shipping rate to this order
            $this->getShippingRate($quote, $originalOrder);
            // Set payment method - work still to be done here
            $quote->setPaymentMethod($this->_getPaymentMethod());
            // Create the quote

            /**
             * Change by Jaimin
             */
            //$quote->save();

            /**
             * End
             */
            // Import payment data - Don't understand this yet
            $quote->getPayment()->importData(
                    array('method' => $this->_getPaymentMethod())
            );
            // Collect quote totals
            $quote->collectTotals()->save();
            /**
             * @var $order \Magento\Sales\Model\Order
             */
            // Convert the quote to an order
            //$order = $this->_quoteManagement->submit($quote);

            /**
             * Added by Jaimin
             */
            $quoteManagement = $objectManager->create(\Magento\Quote\Api\CartManagementInterface::class);
            $quote = $quoteRepository->get($quote->getId());
            $order = $quoteManagement->submit($quote);
            /**
             * End
             */
            // Prevent default order confirmation, we will make our own
            $order->setEmailSent(0);
            // Make sure the order has been created properly
            if (!$order->getRealOrderId()) {
                $order = false;
            }
            $this->_logger->info(
                    sprintf(
                            __('Order %s succesfully created for subscription %s'), $order->getRealOrderId(), $subscription->getId()
                    )
            );
            return $order;
        } else {
            $message = __('The customer no longer exists for subscription: ')
                    . $subscription->getId();
            $this->_logger->critical($message);
            throw new LocalizedException(__($message));
        }
    }
  • Thanks for the massive effort Jaimin, I'll check this out and let you know, – Frank Jul 20 '16 at 7:46
  • You're a hero! I'll accept your answer and post the changes into my question (but without using the object manager) shortly. Thanks Jaimin! – Frank Jul 20 '16 at 8:04
  • Will the code work for configurable product? – TS Guhan Feb 5 at 9:41
  • Yes, it will work for configurable product – Jaimin Parikh Feb 13 at 9:11

...

public function __construct(
        \Magento\Framework\App\Helper\Context $context,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Catalog\Model\Product $product,
        \Magento\Quote\Model\QuoteFactory $quote,
        \Magento\Quote\Model\QuoteManagement $quoteManagement,
        \Magento\Customer\Model\CustomerFactory $customerFactory,
        \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
        \Magento\Sales\Model\Service\OrderService $orderService,
        \Magento\Quote\Api\CartRepositoryInterface $cartRepositoryInterface,
        \Magento\Quote\Api\CartManagementInterface $cartManagementInterface
    ) {
        $this->_storeManager = $storeManager;
        $this->_product = $product;
        $this->quote = $quote;
        $this->quoteManagement = $quoteManagement;
        $this->customerFactory = $customerFactory;
        $this->customerRepository = $customerRepository;
        $this->orderService = $orderService;
        $this->cartRepositoryInterface = $cartRepositoryInterface;
        $this->cartManagementInterface = $cartManagementInterface;
    }

    /**
     * @return int
     */
    public function test()
    {
        return 1;
    }

    /**
     * @return array
     */
    public function createOrder()
    {
        $store = $this->_storeManager->getStore();
        $websiteId = $this->_storeManager->getStore()->getWebsiteId();

        $customer = $this->customerFactory->create();
        $customer->setWebsiteId($websiteId);
        $customer->loadByEmail('bradf@home.net');

        if(!$customer->getEntityId()){
            $customer->setWebsiteId($websiteId)
                ->setStore($store)
                ->setFirstname('Customer')
                ->setLastname('Name')
                ->setEmail('bradf@home.net')
                ->setPassword('1234567');
            $customer->save();
        }

        $quote = $this->quote->create();
        $quote->setStore($store);

        $customer = $this->customerRepository->getById($customer->getEntityId());
        $quote->setCurrency();

        $quote->save();

        $quote->assignCustomer($customer);

        $product = $this->_product->load(1);
        $product->setPrice(12);
        $quote->addProduct($product,1);

        $address = [
            'firstname' => 'First',
            'lastname' => 'Last',
            'street' => '12345 1234 st',
            'city' => 'cityville',
            'country_id' => 'US',
            'region' => 'WA',
            'postcode' => '98765',
            'telephone' => '4254254254',
            'fax' => '4254254255',
            'save_in_address_book' => 1
        ];

        $quote->getBillingAddress()->addData($address);
        $quote->getShippingAddress()->addData($address);

        $shippingAddress = $quote->getShippingAddress();

        $shippingAddress->setCollectShippingRates(true)
            ->collectShippingRates()
            ->setShippingMethod('flatrate_flatrate');
        $quote->setPaymentMethod('checkmo');

        $quote->setInventoryProcessed(false);

        $quote->getPayment()->importData(['method'=>'checkmo']);

        $quote->collectTotals()->save();

        $order_id = $this->quoteManagement->placeOrder($quote->getId());

        if($order_id){
            $retval['order_id'] = $order_id;
        } else {
            $retval['error'] = 'order not created';
        }
        return $retval;
    }

...

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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