Fixing Magento's catalog rules (1.8.0.0)

While working on a new version of a Magento website using v1.8.0.0, I came accross a bug which affects the way catalog rules are evaluated, and makes the resulting price index wrong. At first, I thought the problem lied with the conditions engine, but since there wasn’t any significant code change between 1.7.0.2 and 1.8.0.0 in Mage_Rule, I started to search elsewhere.

A few changes were made to Mage_CatalogRule in Magento’s latest stable iteration.

Here what the release notes from 1.8.0.0 have to say about promotional price rules:

  • Catalog price rules are applied properly to customer groups.
  • The scope of a product attribute is now honored by a catalog price rule.

Sounds good, except the latter breaks everything.

The scope of attributes is honored only for website-scoped attributes. As of 1.8.0.0, you can’t use (or I’m completely misguided) any globally-scoped attribute in a condition of a catalog rule without breaking it.

The fix is quite simple and should be applied to Mage_CatalogRule_Model_Rule_Condition_Product, so I made a simple module to override this class. It’s available here on Bitbucket.

Here’s the fix, explained in details.

<?php
class MyNamespace_CatalogRuleFix_Model_CatalogRule_Rule_Condition_Product extends Mage_CatalogRule_Model_Rule_Condition_Product
{
    /**
     * Get attribute value
     *
     * Bugfix: When we are dealing with a global scope attribute, _entityAttributeValues is not set
     *         That's expected, see the parent class comments) but we don't want to use array()
     *         when dealing with a global attribute, because this replaces every 
     *         globally-scoped attribute value with an empty one!
     *         Instead, we use the current attribute value (which is already scoped).
     *
     *         This seems redundant but it's the less intrusive way to fix what seems to be a shitty bug.
     *
     * @param Varien_Object $object
     * @return mixed
     */
    protected function _getAttributeValue($object)
    {
        $storeId = $object->getStoreId();
        $defaultStoreId = Mage_Core_Model_App::ADMIN_STORE_ID;
        $productValues = isset($this->_entityAttributeValues[$object->getId()]) ? $this->_entityAttributeValues[$object->getId()] : array($defaultStoreId => $object->getData($this->getAttribute()));
        $defaultValue = isset($productValues[$defaultStoreId]) ? $productValues[$defaultStoreId] : null;
        $value = isset($productValues[$storeId]) ? $productValues[$storeId] : $defaultValue;

        $value = $this->_prepareDatetimeValue($value, $object);
        $value = $this->_prepareMultiselectValue($value, $object);
        return $value;
    }
}

I don’t really know if it’s the right fix to apply, but it doesn’t seem to be intrusive, and it works well after that, so for now, I’ll use that way!

I have the feeling there might be some other issues with the changes introduced in Mage_CatalogRule, but it seems to be mostly because my project is not running from a fresh install of Magento.

Enjoy your next Magento debugging session! :thumbsup:


Gabriel Féron

Yet-to-be successful software developer, crazy Android lover and open-source enthusiast