Magento – Magento2 – duplicate quote item to new row and new price

addtocartmagento2quoteitem

Is it possible to add the same product (with same configurable options) with different price based on some custom values present in the product page to the shopping cart, but have it appear as different line items?

Steps I want to follow :

  1. Add product A with configurable option C, Qty=1
  2. Add product A with configurable option C, Custom Value = 20, Qty=1

Both above mentioned quote item should be in different row. Currently it is updating Qty to 2 and adding my Custom Value = 20 to same quote item.

I am using event checkout_cart_product_add_after And below is the code of my observer. Please suggest what should i do to achieve required result.

namespace Ids\SizeCalculator\Observer;

use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\App\ObjectManager;

class CheckoutCartAddObserver implements ObserverInterface {

protected $_layout;
protected $_storeManager;
protected $_request;
private $serializer;
public function __construct(
\Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\View\LayoutInterface $layout, \Magento\Framework\App\RequestInterface $request, Json $serializer = null
) {
    $this->_layout = $layout;
    $this->_storeManager = $storeManager;
    $this->_request = $request;
    $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
}

public function execute(EventObserver $observer) {
    $item = $observer->getQuoteItem();
    $additionalOptions = array();

    if ($additionalOption = $item->getOptionByCode('additional_options')) {
        $additionalOptions = (array) $this->serializer->unserialize($additionalOption->getValue());
    }

    $post = $this->_request->getPost();

    if (isset($post['sc_custom_length']) && $post['sc_custom_length'] > 0) {
        $additionalOptions[] = [
            'label' => 'Length',
            'value' => $post['sc_custom_length'] . " " . $post['sc_custom_unit']
        ];

        if (isset($post['sc_custom_width']) && $post['sc_custom_width'] > 0) {
            $additionalOptions[] = [
                'label' => 'Width',
                'value' => $post['sc_custom_width'] . " " . $post['sc_custom_unit']
            ];
        }

        if (count($additionalOptions) > 0) {
            $item->addOption(array(
                'product_id' => $item->getProductId(),
                'code' => 'additional_options',
                'value' => $this->serializer->serialize($additionalOptions)
            ));
        }
    }

    /* Set custom price */
    if (isset($post['sc_custom_price']) && $post['sc_custom_price'] > 0) {
        $item->setCustomPrice($post['sc_custom_price']);
        $item->setOriginalCustomPrice($post['sc_custom_price']);
        $item->getProduct()->setIsSuperMode(true);
    }
}
}

Best Answer

You can add an around plugin on the method Magento\Quote\Model\Quote\Item\Processor::prepare() that adds your value as a custom option.
2 cart items with similar configurations that have at least one different custom option will be added as separate lines in the cart.

your plugin can look like this (this is just an idea, you might need to change this):

public function aroundPrepare(
    \Magento\Quote\Model\Quote\Item\Processor $subject,
    callable $proceed,
    \Magento\Quote\Model\Quote\Item $item,
    \Magento\Framework\DataObject $request,
    \Magento\Catalog\Model\Product $candidate
) {
    $proceed($item, $request, $candidate);
    $customLength = $request->getData('sc_custom_length');
    $customWidth = $request->getData('sc_custom_width');
    if ($customLength) {
        $option = new \Magento\Framework\DataObject([
        'code' => 'sc_custom_length',
        'product' => $product,
        'value' => json_encode([
            'name' => 'Lenght',
            'length' => $customLength,
            //can add as many values as you want here
            ]),
        ]);
         $item->addOption($option);
    }
    if ($customWidth) {
        $option = new \Magento\Framework\DataObject([
        'code' => 'sc_custom_width',
        'product' => $product,
        'value' => json_encode([
            'name' => 'Width',
            'length' => $customWidth,
            //can add as many values as you want here
            ]),
        ]);
         $item->addOption($option);
    }
    $quoteItem->saveItemOptions(); //this might not be needed, not sure.
}