Question

My goal is to alter the weight of a product based on a chosen custom option. I have the custom option part working, but am unable to alter the weight of the item is such a way that affects the shipping estimates.

The below code partly works, in that it initially sets the correctly updated weight in the quote_item table. However, this gets overwritten, and is always overwritten before shipping is calculated. Is there a better way to do this? Or should I be plugging in later, perhaps? Any thoughts would be greatly appreciated!

<?php

namespace Ibex\Options\Model\Plugin;

use Magento\Quote\Model\Quote;

class Weight
{
    protected $configurationHelper;

    public function __construct(
        \Magento\Catalog\Helper\Product\Configuration $configurationHelper
    )
    {
        $this->configurationHelper = $configurationHelper;
    }

    public function beforeSetProduct(\Magento\Quote\Model\Quote\Item $subject, $product)
    {
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');

        $customOptions = $product->getCustomOptions();

        if (isset($customOptions['option_ids'])){
            $optionIds = $customOptions['option_ids'];

            foreach (explode(',', $optionIds->getValue()) as $optionId) {
                $option = $product->getOptionById($optionId);
                if (isset($customOptions['option_'.$optionId])){
                    $itemOption = $subject->getOptionByCode('option_'.$optionId);

                    $group = $option->groupFactory($option->getType())
                    ->setOption($option)
                    ->setConfigurationItem($subject)
                    ->setConfigurationItemOption($itemOption);

                    if($group->getOption()->getValueById($itemOption->getValue())){
                        $optionValue = $group->getOption()->getValueById($itemOption->getValue());
                        $selectedWeight = $optionValue->getCustomWeight();
                    }
                }
            }
        }
        $weight = $product->getWeight();
        if(isset($selectedWeight)){
          $newWeight = $weight + $selectedWeight;
        }
        if(isset($newWeight)){
          $product->setWeight($newWeight);
        }
        return [$product];
    }
}
Was it helpful?

Solution

I found the error in the approach: The identification of custom options based on $product->getCustomOptions(). This are only set when a product is added to the cart. After that they are stored in the quote items option. Therfore we have to check only for options of the quote item. So instead of looking at $product->getCustomOptions() we just have to use Magento\Quote\Model\Quote\Item::getOptionByCode() and check if those are set.

While testing the code I have found an additional side effect: If you have the same product with different configurations in the same cart, adding the custom weight to each product would sum up for all products except the first. I have added a commented solution for that situation too.

This code should work and keep the correct weight through the complete checkout - I have tested it in my environment:

public function beforeSetProduct(\Magento\Quote\Model\Quote\Item $subject, $product)
{
    $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');

    $optionIds = $subject->getOptionByCode('option_ids');
    if ($optionIds !== null){

        foreach (explode(',', $optionIds->getValue()) as $optionId) {
            $option = $product->getOptionById($optionId);

            $itemOption = $subject->getOptionByCode('option_'.$optionId);

            if ($itemOption !== null){

                $group = $option->groupFactory($option->getType())
                ->setOption($option)
                ->setConfigurationItem($subject)
                ->setConfigurationItemOption($itemOption);

                if($group->getOption()->getValueById($itemOption->getValue())){
                    $optionValue = $group->getOption()->getValueById($itemOption->getValue());
                    $selectedWeight = $optionValue->getCustomWeight();
                }
            }
        }
    }

    //If we have more than one product with different configurations 
    //in the same cart we would sum all weights
    //therefore we should keep the original product weight as base 
    //for custom weight calculation
    $weight = $product->getOriginalWeight() != null ? $product->getOriginalWeight() : $product->getWeight();
    if(isset($selectedWeight)){
        $newWeight = $weight + $selectedWeight;
    }
    if(isset($newWeight)){
        $product->setOriginalWeight($weight);
        $product->setWeight($newWeight);
    }
    return [$product];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top