2

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);
    }
}
}

2 Answers 2

1

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.
}
4
  • Unfortunately, Its not working.. Commented Jun 14, 2018 at 8:48
  • I made a mistake. I should have used json_encode instead of serialize. Most probably this is not the reason it doesn't work, but you can give it another try. I edited the answer.
    – Marius
    Commented Jun 14, 2018 at 9:38
  • Nope, This will not work. Do you have any other idea? Can I add configurable product in cart using the same event I am using checkout_cart_product_add_after ? Commented Jun 14, 2018 at 10:51
  • I got a solution for the problem, I used 2 events, "catalog_product_load_after" and "checkout_cart_product_add_after" , I added custom field by using "catalog_product_load_after" and the calculated their price in "checkout_cart_product_add_after". In this way my problem sorted out. Commented Jun 25, 2018 at 7:42
1

I had the same problem in Magento 2.1. If you check this method Magento\Quote\Model\Quote\Item::representProduct() you'll see that Magento compares quote item options vs product options

/**
 * Check product representation in item
 *
 * @param   \Magento\Catalog\Model\Product $product
 * @return  bool
 */
public function representProduct($product)
{
    ...
    ...
    // Check options
    $itemOptions = $this->getOptionsByCode();
    $productOptions = $product->getCustomOptions();

    if (!$this->compareOptions($itemOptions, $productOptions)) {
        return false;
    }
    if (!$this->compareOptions($productOptions, $itemOptions)) {
        return false;
    }
    return true;
}

If not equals then return False and create new Item in \Magento\Quote\Model\Quote::AddProduct() line $item = $this->getItemByProduct($candidate);

foreach ($cartCandidates as $candidate) {
        // Child items can be sticked together only within their parent
        $stickWithinParent = $candidate->getParentProductId() ? $parentItem : null;
        $candidate->setStickWithinParent($stickWithinParent);
        // Here problem, return false when item options and product options are different
        $item = $this->getItemByProduct($candidate);
        if (!$item) {
            $item = $this->itemProcessor->init($candidate, $request);
            // Add only item that is not in quote already
            $this->addItem($item);
        }
        $items[] = $item;
}

The solution for me was create a observer for catalog_product_load_after and add the same option to my product before Add to this cart.

<?php

namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\RequestInterface;
class SetOptionsToProducts implements ObserverInterface {
protected $_request;

public function __construct(
    RequestInterface $request
) {
    $this->_request = $request;
}

public function execute(\Magento\Framework\Event\Observer $observer)
{ 
    if ($this->_request->getFullActionName() == 'checkout_cart_add') { 

        $product = $observer->getProduct(); 
        $observer->getProduct()->addCustomOption('the_option_code_like_quote_item_option', '0');
    }
}
}

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.