4

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];
    }
}
  • For better understanding: The code you are using saves the correct calculated weight value into quote_item table and in a later step in the checkout some other process is overwriting the value? – HelgeB Mar 14 '19 at 17:10
  • @HelgeB Correct! – brackfost Mar 14 '19 at 17:11
  • Can you identify the step where it happens? That would make it easier – HelgeB Mar 14 '19 at 17:13
  • I'm not sure exactly. But looking at the database, I see the entry being created (with the new weight) in the table, and then it gets updated with the initial weight shortly afterwards. – brackfost Mar 14 '19 at 17:15
  • Well I guess you will have to debug that to see where that change comes from. I'll give you a hint in the chat chat.stackexchange.com/rooms/90428/help – HelgeB Mar 14 '19 at 17:22
2

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];
}
| improve this answer | |
  • this works perfectly. Thank you so much your help on this! At the moment, if used on a product with multiple customizable options, this only grabs the weight from the last option. Do you think a good solution would be to make $selectedWeight into an array, and then sum it before calculating newWeight? Anyway, can't thank you enough. – brackfost Mar 15 '19 at 14:48
  • (Went the array route, worked out well) – brackfost Mar 15 '19 at 14:55
  • 1
    You're welcome. I guess for summing up over multiple options you just have to change $selectedWeight = $optionValue->getCustomWeight(); to $selectedWeight += $optionValue->getCustomWeight(); and don't forget to set $selectedWeight = 0 before the foreach loop otherwise you will get an php error with "undefined variable" because of the "+=" operator. – HelgeB Mar 15 '19 at 14:56
  • I actually just set $selectedWeights = array() earlier in the code, then $selectedWeights[] = $optionValue->getCustomWeight() in the loop, and finally at the end: if(!empty($selectedWeights)){ $selectedWeight = array_sum($selectedWeights); } if(isset($selectedWeight)){ $newWeight = $weight + $selectedWeight; } – brackfost Mar 15 '19 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.