6

I have created one virtual product in my Magento2 website. Now, I want to add it programmatically to the existing customer orders on some occasions with its custom price and name.

Can someone please guide step by step to achieve this?

Thanks,

2 Answers 2

7

After some goggling and with the help of some core modules I came up with following solution :

/**
* @var \Magento\Sales\Api\OrderRepositoryInterface
*/
protected $orderRepository;

/**
* @var \Magento\Catalog\Api\ProductRepositoryInterface
*/
protected $productRepository;

/**
* @var \Magento\Quote\Api\Data\CartItemInterfaceFactory
*/
protected $cartItemFactory;

/**
* @var \Magento\Quote\Api\CartRepositoryInterface
*/
protected $quoteRepository;

/**
* @var \Magento\Sales\Model\Order\ItemFactory
*/
protected $orderItemFactory;

 public function __construct(
    \Magento\Backend\App\Action\Context $context,        
    \Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
    \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
    \Magento\Quote\Api\Data\CartItemInterfaceFactory $cartItemFactory,
    \Magento\Quote\Api\CartRepositoryInterface $quoteRepository,
    \Magento\Sales\Model\Order\ItemFactory $orderItemFactory
) {
    parent::__construct($context);       
    $this->productRepository    = $productRepository;
    $this->orderRepository      = $orderRepository;
    $this->cartItemFactory      = $cartItemFactory;
    $this->quoteRepository      = $quoteRepository;
    $this->orderItemFactory     = $orderItemFactory;
}

public function execute()
{
    $postData       = $this->getRequest()->getPostValue();
    $productSku     = 'your-product-sku'; // Static Virtual Product created. You may also use static ID.
    $product        = $this->productRepository->get($productSku);
    $order          = $this->orderRepository->get($postData['order_id']);
    $quote          = $this->quoteRepository->get($order->getQuoteId());

    try {
    /* Add Quote Item Start */
    $quoteItem = $this->cartItemFactory->create();
    $quoteItem->setProduct($product);
    $quoteItem->setCustomPrice($postData['custom_price']);
    $quoteItem->setOriginalCustomPrice($postData['custom_price']);
    $quoteItem->getProduct()->setIsSuperMode(true);
    $quote->addItem($quoteItem);
    $quote->collectTotals()->save();
    /* Add Quote Item End */

    /* Add Order Item Start */
    $orderItem = $this->orderItemFactory->create();
    $orderItem
            ->setStoreId($order->getStoreId())
            ->setQuoteItemId($quoteItem->getId())
            ->setProductId($product->getId())
            ->setProductType($product->getTypeId())
            ->setName($product->getName())
            ->setSku($product->getSku())
            ->setQtyOrdered(1)
            ->setPrice($postData['custom_price'])
            ->setBasePrice($postData['custom_price'])
            ->setOriginalPrice($postData['custom_price'])
            ->setBaseOriginalPrice($postData['custom_price'])
            ->setPriceInclTax($postData['custom_price'])
            ->setBasePriceInclTax($postData['custom_price'])
            ->setRowTotal($postData['custom_price'])
            ->setBaseRowTotal($postData['custom_price'])
            ->setRowTotalInclTax($postData['custom_price'])
            ->setBaseRowTotalInclTax($postData['custom_price'])
            ->setWeight(1)
            ->setIsVirtual(1);
    $order->addItem($orderItem);
    /* Add Order Item End */

    /* Update relavant order totals Start */
    $order->setBaseGrandTotal($order->getBaseGrandTotal() + $postData['custom_price']);
    $order->setGrandTotal($order->getGrandTotal() + $postData['custom_price']);
    $order->setBaseSubtotal($order->getBaseSubtotal() + $postData['custom_price']);
    $order->setSubtotal($order->getSubtotal() + $postData['custom_price']);
    $order->setBaseSubtotalInclTax($order->getBaseSubtotalInclTax() + $postData['custom_price']);
    $order->setSubtotalInclTax($order->getSubtotalInclTax() + $postData['custom_price']);
    $order->setTotalItemCount($order->getTotalItemCount() + 1);
    $order->setTotalQtyOrdered($order->getTotalQtyOrdered() + 1);
    $this->orderRepository->save($order);
    /* Update relavant order totals End */
  }
  catch (\Exception $e) {
     $this->messageManager->addError($e->getMessage());
  } 
}
3
  • This seems to be working fine. However, I stumbled on an error when trying to renew an order with an item added through this process. The error occurs in Magento\Checkout\Model\Cart.php l:280. Our programmatically added item does not have the option 'info_buyRequest'. I managed to fix it by setting $info to an empty array when the option is missing, in a preference.
    – Badong
    Oct 30, 2020 at 12:14
  • @Badong I face the same issue. could you please provide a solution for this? Also in my case, Newly added item hasn't included in orderFetch API via rest Jan 4 at 12:45
  • It seems the order status has been changed automatically from pending to processing. Anyone knows the solution? Jan 5 at 10:30
0

As per as, Magento you cannot add a new item to an order after that order is placed before. You must have to add your item before it cart has been placed.

2
  • Hello @Amit, Many a times you will get these type of requirements in which you need to add virtual product to get some kind of additional fee into your order with custom price. I think this can be possible with custom programming. Oct 8, 2018 at 9:48
  • every platform has it own limitation,.You have to build your logic of requirement according to it
    – Amit Bera
    Oct 8, 2018 at 9:58

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.