1

We have several clients that keep forgetting to add a weight to their simple products and thus creating virtual products.

This creates problems later on in the checkout where there is no option to enter a shipping address nor to select a shipping method.

We keep telling them how to change them to simple products using the bulk editing, and how to add weight in the first place, but it is a recurring thing. Most of our clients have shops where they will never need virtual products anyway.

So my idea is to "disable" virtual products all together (I didn't find a way to do that yet).

Or maybe write an observer at "product save" that sets weight to an arbitrary value and has_weight to 1, if there isn't anything set.

How would you approach this?

2 Answers 2

1

To remove Virtual Product from the Add Product options in the admin panel (assuming your clients create products through the admin panel)

Override the Adminhtml Block in your module by adding a preference in your di.xml

\Magento\Catalog\Block\Adminhtml\Product

and modify the method _getAddProductButtonOptions and add these lines to skip the Virutal Product to options

    /**
     * Retrieve options for 'Add Product' split button
     *
     * @return array
    */
    protected function _getAddProductButtonOptions()
    {
        $splitButtonOptions = [];
        $types = $this->_typeFactory->create()->getTypes();
        uasort(
            $types,
            function ($elementOne, $elementTwo) {
            return ($elementOne['sort_order'] < $elementTwo['sort_order']) ? -1 : 1;
        }
        );

        foreach ($types as $typeId => $type) {

            // add these lines to skip adding virtual products to the 'Add Product' button options 
            if (stripos($typeId,'virtual') !== FALSE)  {
                continue;
            }

            $splitButtonOptions[$typeId] = [
                'label' => __($type['label']),
                'onclick' => "setLocation('" . $this->_getProductCreateUrl($typeId) . "')",
                'default' => \Magento\Catalog\Model\Product\Type::DEFAULT_TYPE == $typeId,
            ];
        }

             return $splitButtonOptions;
    }

This is the recommended way to disable (not actually) the virtual product and not giving an option to your customers to add virutal products from the admin panel. This is much simpler and easier way than the other options available to get rid of Virtual Products in your magento2 store.

make sure to compile your code after this bin/magento s:d:c

Remember the Vitual Product is not completely disabled and still its possible to create Virtual Products through REST API / GraphQL routes.

In case, if you want to get rid of the virtual products all together then override the product_types.xml in your module and remove the Virtual Product <type>.

Need to remove these lines

<type name="virtual" label="Virtual Product" modelInstance="Magento\Catalog\Model\Product\Type\Virtual" indexPriority="20" sortOrder="40">
        <indexerModel instance="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\VirtualProductPrice" />
        <customAttributes>
            <attribute name="is_real_product" value="false"/>
            <attribute name="refundable" value="false"/>
        </customAttributes>
    </type>
1

Because disabling the Virtual product type feature completely might pose a risk, I suggest setting the "product has weight" attribute to "yes" to force it to be a simple product.

Let's go! When you save a product or update product attributes it will call the processProduct method in Magento\Catalog\Model\Product\TypeTransitionManager to set the product type. Since this is a public method, you can add a before plugin to force set product_has_weight to 1.

Finally, your plugin class looks like the following:
app/code/VendorName/ModuleName/Plugin/ForceSetProductHasWeight.php

<?php declare(strict_types=1);
namespace VendorName\ModuleName\Plugin;

use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\TypeTransitionManager;

class ForceSetProductHasWeight
{
    /**
     * @param TypeTransitionManager $subject
     * @param Product $product
     * @return array
     */
    public function beforeProcessProduct(TypeTransitionManager $subject, Product $product): array
    {
       // Customization: Force set product has weight in case product has no weight
        // to prevent it set product type to virtual product
        if (!$product->getData('product_has_weight')) {
            $product->setData('product_has_weight', 1);
        }

        return [$product];
    }
}

The di.xml config should look like this:
app/code/VendorName/ModuleName/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Model\Product\TypeTransitionManager">
        <plugin name="ForceSetProductHasWeight" type="VendorName\ModuleName\Plugin\ForceSetProductHasWeight"/>
    </type>
</config>

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.