2

Out the box is it possible to apply catalog price rules to grouped products on magento ce or ee? I've searched but cant find anything related to my question.

2 Answers 2

3

If the product added to the cart is child of a grouped, we have to look for the category of the grouped and not of the simple.

For this, I have done the next:

  1. Create one file

Vendor/Module/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">
    <preference for="Magento\SalesRule\Model\Rule\Condition\Product"
                type="Vendor\Module\Model\Rule\Condition\Product"/>
    <preference for="Magento\Catalog\Model\ResourceModel\Product"
                type="Vendor\Module\Model\ResourceModel\Product"/>
</config>
  1. I have overrided the function validate to call a custom function of the resource model.

Vendor\Module\Model\Rule\Condition\Product

<?php
namespace Vendor\Module\Model\Rule\Condition;

use Magento\SalesRule\Model\Rule\Condition\Product as magentoSalesRuleProduct;
use Magento\Framework\Model\AbstractModel;
use Magento\Rule\Model\Condition\AbstractCondition;

/**
 * Class Product
 * @package Vendor\Module\Model\Rule\Condition
 */
class Product extends magentoSalesRuleProduct
{

    /**
     * Validate Product Rule Condition
     *
     * @param \Magento\Framework\Model\AbstractModel $model
     * @return bool
     */
    public function validate(\Magento\Framework\Model\AbstractModel $model)
    {
        /** @var \Magento\Catalog\Model\Product $product */
        $product = $model->getProduct();
        if (!$product instanceof \Magento\Catalog\Model\Product) {
            $product = $this->productRepository->getById($model->getProductId());
        }

        $product->setQuoteItemQty(
            $model->getQty()
        )->setQuoteItemPrice(
            $model->getPrice() // possible bug: need to use $model->getBasePrice()
        )->setQuoteItemRowTotal(
            $model->getBaseRowTotal()
        );

        return $this->validateRuleProduct($product, $model);
    }


    /**
     * Validate by condition
     * @param AbstractModel $model
     * @param AbstractModel $modelQuoteItem
     * @return bool
     */
    public function validateRuleProduct(AbstractModel $model, AbstractModel $modelQuoteItem)
    {
        $attrCode = $this->getAttribute();

        if ('category_ids' == $attrCode) {
            //Validate in categories with params product and quote_item to know info for grouped products
            return $this->validateAttribute(
                $model->getResource()->getAvailableInCategoriesInQuote($model, $modelQuoteItem)
            );
        } elseif (!isset($this->_entityAttributeValues[$model->getId()])) {
            if (!$model->getResource()) {
                return false;
            }
            $attr = $model->getResource()->getAttribute($attrCode);

            if ($attr && $attr->getBackendType() == 'datetime' && !is_int($this->getValue())) {
                $this->setValue(strtotime($this->getValue()));
                $value = strtotime($model->getData($attrCode));
                return $this->validateAttribute($value);
            }

            if ($attr && $attr->getFrontendInput() == 'multiselect') {
                $value = $model->getData($attrCode);
                $value = strlen($value) ? explode(',', $value) : [];
                return $this->validateAttribute($value);
            }

            return AbstractCondition::validate($model);
        } else {
            $result = false;
            // any valid value will set it to TRUE
            // remember old attribute state
            $oldAttrValue = $model->hasData($attrCode) ? $model->getData($attrCode) : null;

            foreach ($this->_entityAttributeValues[$model->getId()] as $value) {
                $attr = $model->getResource()->getAttribute($attrCode);
                if ($attr && $attr->getBackendType() == 'datetime') {
                    $value = strtotime($value);
                } elseif ($attr && $attr->getFrontendInput() == 'multiselect') {
                    $value = strlen($value) ? explode(',', $value) : [];
                }

                $model->setData($attrCode, $value);
                $result |= AbstractCondition::validate($model);

                if ($result) {
                    break;
                }
            }

            if ($oldAttrValue === null) {
                $model->unsetData($attrCode);
            } else {
                $model->setData($attrCode, $oldAttrValue);
            }

            return (bool)$result;
        }
    }
}
  1. I have created a custom function getAvailableInCategoriesInQuote similiar to getAvailableInCategories where if the product added to the cart is child of a grouped product I have look for the category of the grouped and not of the simple.

Vendor\Module\Model\ResourceModel\Product.php

<?php
namespace Vendor\Module\Model\ResourceModel;

use Magento\Catalog\Model\ResourceModel\Product as magentoResourceModelProduct;
use Magento\Catalog\Model\Product\Visibility;

/**
 * Class Product
 * @package Vendor\Module\Model\ResourceModel
 */
class Product extends magentoResourceModelProduct
{

    /**
     * @param \Magento\Catalog\Model\Product $object
     * @param \Magento\Quote\Model\Quote\Item $quoteItem
     * @return mixed
     */
    public function getAvailableInCategoriesInQuote($object, $quoteItem)
    {
        // is_parent=1 ensures that we'll get only category IDs those are direct parents of the product, instead of
        // fetching all parent IDs, including those are higher on the tree
        $entityId = (int)$object->getEntityId();
        if($quoteItem->getProductType() == 'grouped'){
            $buyRequest = $quoteItem->getBuyRequest()->getData('super_product_config');
            $entityId = $buyRequest['product_id'];
        }
        if (!isset($this->availableCategoryIdsCache[$entityId])) {
            $this->availableCategoryIdsCache[$entityId] = $this->getConnection()->fetchCol(
                $this->getConnection()->select()->distinct()->from(
                    $this->getTable('catalog_category_product_index'),
                    ['category_id']
                )->where(
                    'product_id = ? AND is_parent = 1',
                    $entityId
                )->where(
                    'visibility != ?',
                    Visibility::VISIBILITY_NOT_VISIBLE
                )
            );
        }
        return $this->availableCategoryIdsCache[$entityId];
    }
}
2
  • Thanks, it works!!
    – surbhi agr
    Mar 22 at 8:29
  • Hello @raumatbel, only issue is when i am adding grouped product along with simple product to the cart, its applies to both the product. I want it to be applicable for just grouped product
    – surbhi agr
    Mar 27 at 12:21
1

It's not possible out of the box - there are a few extensions that can do it however. I normally run a script to add all of the sub-products to a hidden category and apply the rules to that category.

4
  • Thx Mark, if you could elaborate more on how you do that? Nov 25, 2015 at 20:24
  • Or any extension that can help with this feature? Nov 25, 2015 at 20:38
  • I build a table in a separate database with the product ID, then use the API to add products to the catalog. Basic code: // Connect to Magento $client= new SoapClient('API URL', array('trace' => 1, "connection_timeout" => 120)); $sess_id= $client->login('USER', 'PASS'); // Load into category by ID while($row=mysql_fetch_array($query)) { echo $row['ProductID']."\n"; $doit=$client->call($sess_id, 'category.assignProduct', array($row['Category'], $row['ProductID'])); }
    – Mark
    Nov 25, 2015 at 20:54
  • Do you know of any extension that can do this? Nov 25, 2015 at 20:56

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.