Question

I am trying to create order programmatically product with customizable options, and it's working but it's created order with one product even I passed two products for me for these I tried below code.

Controller.php Code is below

<?php

namespace Learning\COP\Controller\Index;

use \Magento\Framework\App\Action\Action;
use \Magento\Framework\View\Result\PageFactory;
use \Magento\Framework\App\Action\Context;
use \Magento\Framework\Controller\ResultInterface;
use \Magento\Framework\App\ResponseInterface;
use \Learning\COP\Helper\Data;

class Index extends Action
{

    /**
     * @var PageFactory
     */
    protected $resultPageFactory;

    /**
     * @var Data
     */
    protected $helper;

    /**
     * Index constructor.
     *
     * @param Context $context
     * @param PageFactory $pageFactory
     * @param Data $helper
     */
    public function __construct(
        Context $context,
        PageFactory $pageFactory,
        Data $helper
    )
    {
        $this->helper = $helper;
        $this->resultPageFactory = $pageFactory;
        parent::__construct($context);
    }

    /**
     * @return ResponseInterface|ResultInterface|void
     */
    public function execute()
    {
        $order = [
            'currency_id' => 'USD',
            'email' => 'bojjaiah@gmail.com',
            'shipping_address' => ['firstname' => 'John',
                'lastname' => 'Doe',
                'street' => '123 Street',
                'city' => 'New York',
                'country_id' => 'US',
                'region' => 'New York',
                'postcode' => '10004',
                'telephone' => '52556542',
                'fax' => '3242322556',
                'region_id' => 43,
                'save_in_address_book' => 1],
            'items' => [
                ['product_id' => '2', 'qty' => 1, 'name' => 'Test Product 1', 'sku' => 'ts1', 'price' => 250 ],
                ['product_id' => '2', 'qty' => 2, 'name' => 'Test Product 2', 'sku' => 'ts2','price' => 15 ]
            ]
        ];

        $result = $this->helper->createOrder($order);
        var_dump($result);
    }

}

Helper class

 <?php

namespace Learning\COP\Helper;

use \Magento\Framework\App\Helper\AbstractHelper;
use \Magento\Framework\App\Helper\Context;
use \Magento\Store\Model\StoreManagerInterface;
use \Magento\Catalog\Model\Product;
use \Magento\Framework\Data\Form\FormKey;
use \Magento\Quote\Model\QuoteFactory;
use \Magento\Quote\Model\QuoteManagement;
use \Magento\Customer\Model\CustomerFactory;
use \Magento\Customer\Api\CustomerRepositoryInterface;
use \Magento\Sales\Model\Service\OrderService;
use \Magento\Catalog\Api\Data\CustomOptionInterface;
use Magento\Catalog\Api\Data\ProductCustomOptionInterface;
use \Magento\Catalog\Model\Product\Option;
use Magento\Quote\Api\Data\ProductOptionInterface;
use \Magento\Quote\Api\CartItemRepositoryInterface;
use Magento\Quote\Api\Data\CartItemInterface;

class Data extends AbstractHelper{

    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var Product
     */
    protected $product;

    /**
     * @var FormKey
     */
    protected $formkey;

    /**
     * @var QuoteFactory
     */
    protected $quote;

    /**
     * @var QuoteManagement
     */
    protected $quoteManagement;

    /**
     * @var CustomerFactory
     */
    protected $customerFactory;

    /**
     * @var CustomerRepositoryInterface
     */
    protected $customerRepository;

    /**
     * @var OrderService
     */
    protected $orderService;

    /**
     * @var CustomOptionInterface
     */
    protected $_pOptions;

    /**
     * @var ProductCustomOptionInterface
     */
    protected $_productWithCustomOptions;

    /**
     * @var Option
     */
    protected $_options;

    /**
     * @var ProductOptionInterface
     */
    protected $_productOptionInterface;

    /**
     * @var CartItemRepositoryInterface
     */
    protected $_cartItemRep;

    /**
     * @var CartItemInterface
     */
    protected $_cartItem;

    /**
     * Data constructor.
     *
     * @param Context $context
     * @param StoreManagerInterface $storeManager
     * @param Product $product
     * @param FormKey $formKey
     * @param QuoteFactory $quote
     * @param QuoteManagement $quoteManagement
     * @param CustomerFactory $customerFactory
     * @param CustomerRepositoryInterface $customerRepository
     * @param OrderService $orderService
     * @param CustomOptionInterface $customOption
     * @param ProductCustomOptionInterface $productCustomOption
     * @param Option $productOptions
     * @param ProductOptionInterface $_productOptionInterface
     * @param CartItemRepositoryInterface $cartItemRepository
     * @param CartItemInterface $cartItem
     */
    public function __construct(
        Context $context,
        StoreManagerInterface $storeManager,
        Product $product,
        FormKey $formKey,
        QuoteFactory $quote,
        QuoteManagement $quoteManagement,
        CustomerFactory $customerFactory,
        CustomerRepositoryInterface $customerRepository,
        OrderService $orderService,
        CustomOptionInterface $customOption,
        ProductCustomOptionInterface $productCustomOption,
        Option $productOptions,
        ProductOptionInterface $_productOptionInterface,
        CartItemRepositoryInterface $cartItemRepository,
        CartItemInterface $cartItem
    )
    {
        $this->_pOptions = $customOption;
        $this->storeManager = $storeManager;
        $this->product = $product;
        $this->formkey = $formKey;
        $this->quote = $quote;
        $this->quoteManagement = $quoteManagement;
        $this->customerFactory = $customerFactory;
        $this->customerRepository = $customerRepository;
        $this->orderService = $orderService;
        $this->_productWithCustomOptions = $productCustomOption;
        $this->_options = $productOptions;
        $this->_productOptionInterface = $_productOptionInterface;
        $this->_cartItemRep = $cartItemRepository;
        $this->_cartItem = $cartItem;
        parent::__construct($context);
    }

    public function createOrder($order)
    {
        $store = $this->storeManager->getStore();
        $websiteId = $this->storeManager->getStore()->getWebsiteId();
        $customer = $this->customerFactory->create();
        $customer->setWebsiteId($websiteId);
        $customer->loadByEmail($order['email']);
        if (!$customer->getEntityId()) {
            $customer->setWebsiteId($websiteId)->setStore($store)->setFirstname($order['shipping_address']['firstname'])->setLastname($order['shipping_address']['lastname'])->setEmail($order['email'])->setPassword($order['email']);
            $customer->save();
        }
        $quote = $this->quote->create();
        $quote->setStore($store);
        $customer = $this->customerRepository->getById($customer->getEntityId());
        $quote->setCurrency();
        $quote->assignCustomer($customer);


        $i = 1;
        $optionData = [];
        $optionsArray = [];

        foreach ($order['items'] as $item) {
            $product = $this->product->load($item['product_id']);
            $product->setPrice($item['price']);
            $product->setQty(intval($item['qty']));
            /*foreach ($product->getProductOptionsCollection()->getItems() as $options){
                $this->_productWithCustomOptions->setSku($item['sku']);
                $this->_productWithCustomOptions->setTitle($item['name']);
                $this->_productWithCustomOptions->setPrice($item['price']);
                $this->_productWithCustomOptions->setOptionId($options->getData('option_id'));
                $this->_productWithCustomOptions->setIsRequire(true);
                $this->_productWithCustomOptions->setProductSku($product->getSku());
            }
            $optionsArray[] = array(
                'sku' => $item['sku'],
                'name' => $item['name'],
                'price' => $item['price'],
                'option_id' => $i,
                'product_id' => $product->getId()
            );*/
            $params = array(
                'product' => $product->getId(),
                'qty' => intval($item['qty']),
                'options' => array(
                    '1' => $item['name'],
                    '2' => $item['sku'],
                    '3' => $item['price']
                )
            );
            $requestParams = new \Magento\Framework\DataObject($params);
            //$options = $this->_productWithCustomOptions->getData();
            //$product->setCustomOptions($options);
            $quote->addProduct($product, $requestParams);
            $i++;
        }

       /* $customOptionInterface = \Magento\Framework\App\ObjectManager::getInstance()->create(\Magento\Catalog\Api\Data\CustomOptionInterface::class);
        foreach ($optionsArray as $option) {
            $optionData[] = $customOptionInterface->setdata($option);
        }

        $productOption = $this->_productOptionInterface; // instance of Magento\Quote\Api\Data\ProductOptionInterface
        $extAttribute = $productOption->getExtensionAttributes();
        $extAttribute->setCustomOptions($optionData[0]);
        $productOption->setExtensionAttributes($extAttribute);
        $cartItem = $this->_cartItem;
        $cartItem->setProductOption($productOption);
        $this->_cartItemRep->save($cartItem);*/

        $quote->getBillingAddress()->addData($order['shipping_address']);
        $quote->getShippingAddress()->addData($order['shipping_address']);

        $shippingAddress = $quote->getShippingAddress();
        $shippingAddress->setCollectShippingRates(true)->collectShippingRates()->setShippingMethod('freeshipping_freeshipping');
        $quote->setPaymentMethod('checkmo');
        $quote->setInventoryProcessed(false);
        $quote->save();

        $quote->getPayment()->importData(['method' => 'checkmo']);
        $quote->collectTotals()->save();
        $orderdata = $this->quoteManagement->submit($quote);
        if(isset($orderdata)){
            if ($orderdata->getEntityId()) {
                $result['order_id'] = $orderdata->getRealOrderId();
            } else {
                $result = ['error' => 1, 'msg' => 'Order not created..'];
            }
        }else {
            $result = ['error' => 1, 'msg' => 'Order not created..'];
        } 
        return $result;
    } 
}

Customizable options image here it is.

enter image description here

And created order with only one product see below screenshot.

enter image description here

Could you please suggest me where I went wrong on this process.

Was it helpful?

Solution 2

Finally achieved by following steps.

1) One of the reasons that passing the same product Id as said @KKR, @CoderGeek, and @AdarshM.

2) Product is configured/created with Qty = 0, Price = 0, Stock Maintenance = No and Search = Notvisible.

When I am trying to add the product programmatically it's not added because of Saleable is returning false and not the stock of the product.

for this, I have set the $product->setSkipSaleableCheck(true) and $quote->setIsSuperMode(true) properties for both product and quote and tried the product is created.

and my code is:

$order = [
            'currency_id' => 'USD',
            'email' => 'bojjaiah@gmail.com',
            'shipping_address' => ['firstname' => 'John',
                'lastname' => 'Doe',
                'street' => '123 Street',
                'city' => 'New York',
                'country_id' => 'US',
                'region' => 'New York',
                'postcode' => '10004',
                'telephone' => '52556542',
                'fax' => '3242322556',
                'region_id' => 43,
                'save_in_address_book' => 1],
            'items' => [
                ['product_id' => '3', 'qty' => 5, 'name' => 'Test Product 1', 'sku' => 'testp1', 'price' => '50' ]
            ]
        ];
$store = $this->storeManager->getStore();
        $websiteId = $this->storeManager->getStore()->getWebsiteId();
        $customer = $this->customerFactory->create();
        $customer->setWebsiteId($websiteId);
        $customer->loadByEmail($order['email']);
        if (!$customer->getEntityId()) {
            $customer->setWebsiteId($websiteId)->setStore($store)->setFirstname($order['shipping_address']['firstname'])->setLastname($order['shipping_address']['lastname'])->setEmail($order['email'])->setPassword($order['email']);
            $customer->save();
        }
        $quote = $this->quote->create();
        $quote->setStore($store);
        $customer = $this->customerRepository->getById($customer->getEntityId());
        $quote->setCurrency();
        $quote->assignCustomer($customer);
        $i = 1;
        foreach ($order['items'] as $item) {
            $product = $this->product->load($item['product_id']);
            $product->setPrice($item['price']);
            $product->setQty(intval($item['qty']));
            $product->setSkipSaleableCheck(true);
            $params = array(
                'product' => $product->getId(),
                'qty' => intval($item['qty']),
                'options' => array(
                    7 => $item['name'],
                    8 => $item['price'],
                    9 => $item['sku']
                )
            );

            $requestParams = new \Magento\Framework\DataObject($params);
            $quote->setIsSuperMode(true);
            $quote->addProduct($product, $requestParams);
            $i++;
        }

        $quote->getBillingAddress()->addData($order['shipping_address']);
        $quote->getShippingAddress()->addData($order['shipping_address']);

        $shippingAddress = $quote->getShippingAddress();
        $shippingAddress->setCollectShippingRates(true)->collectShippingRates()->setShippingMethod('freeshipping_freeshipping');
        $quote->setPaymentMethod('checkmo');
        $quote->setInventoryProcessed(false);
        $quote->save();

        $quote->getPayment()->importData(['method' => 'checkmo']);
        $quote->collectTotals()->save();
        $order_data = $this->quoteManagement->submit($quote);
        if(isset($order_data)){
            if ($order_data->getEntityId()) {
                $result['order_id'] = $order_data->getRealOrderId();
            } else {
                $result = ['error' => 1, 'msg' => 'Order not created..'];
            }
        }else {
            $result = ['error' => 1, 'msg' => 'Order not created..'];
        }
        return $result;

3) If you want to add custom products multiple then try to add one product initially and then save again add one more product until n number of product one by one it will work.

Thanks for all if this is not working feel free to ping me or else if better code please update my thread.

OTHER TIPS

It seems there are two product passed with the same product_id. It should be different product_id. Please try same code but with different Product Id. e.g.

                ['product_id' => '2', 'qty' => 1, 'name' => 'Test Product 1', 'sku' => 'ts1', 'price' => 250 ],
                ['product_id' => '2', 'qty' => 2, 'name' => 'Test Product 2', 'sku' => 'ts2','price' => 15 ]
            ]

Replace it with

'items' => [
                ['product_id' => '2', 'qty' => 1, 'name' => 'Test Product 1', 'sku' => 'ts1', 'price' => 250 ],
                ['product_id' => '3', 'qty' => 2, 'name' => 'Test Product 2', 'sku' => 'ts2','price' => 15 ]
            ]

Better use REST API's, I have to give the magento devdoc url: https://devdocs.magento.com/guides/v2.3/rest/tutorials/orders/order-create-quote.html

You are passing the same product_id, try changing that and it should add both the products to the order. It is also adding the qty ordered and overwriting the price.

You have two products with the same product_id, so the quantity gets added up and the price is overwritten from the final duplicate product.

Your code is too verbose. Why don't use Magento\Sales\Model\Order?

This is my example:

$order = $this->_objectManager->create('Magento\Sales\Model\Order')
                ->setStoreId($store_id)
                ->setQuoteId(0)
                ->addData($orderData);
                ->setBillingAddress($billingAddress)
                ->setShippingAddress($shippingAddress)
                ->setShippingMethod('flatrate_flatrate')
                ->setPayment('checkmo');
foreach ($items as $item) {
    $order->addItem($item);
}
$order->save();

You don't need to declare a Helper class to do it.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top