Restrict Cart Rules to specific categories in PrestaShop

You might want to use discount vouchers on your site, but restrict the usage for certain categories or products. Let’s see how to do it in PrestaShop.

Two cases

In this tutorial, we will see how we can restrict the usage of vouchers for different categories in PrestaShop. There are two ways we can do this, depending on our needs. The first consist of simply restricting the rule to some categories, and apply discounts in the cart to products belonging to them, only.
However, if you really want to exclude the usage of specific vouchers on certain products, there is a more complex procedure to use, which involves coding.

The easy way

If you are happy with having the rule be available on all products, but only affect the ones you want, from a specific category, then the easy way will be enough.
We are assuming you already know How to use Discounts in PrestaShop, so let’s proceed from the Cart Rule configuration page:

PrestaShop Cart Rule configuration

Give the rule some generic conditions and a code (so we can test it); then, under restrictions, tick Product selection.
In the “Add a rule concerning” select box, choose categories, then hit add. A new box will appear below, where you can choose the category you want to restrict this voucher to

PrestaShop cart rules category restriction configuration

Then, in the action tab, if you select a discount percentage, you must make sure to check the “selected Products” box, under “Apply a discount to”. This will make sure other products are not affected by the price reduction.
This, however, limits us in the case we want to use another rule (cheapest product?) or free shipping on specific categories only. Therefore, if you have more specific needs, read on.

The advanced way

If you are not happy with the above solution, then you might want to code some extra checks in the CartRule class, to make the rule invalid if your cart contains extra products.
Open up CartRule.php, located in the classes folder, or create an override for it.
Locate the checkValidity method, then the following snippet:

...
  // gonna slow the thing down a bit

        /* This loop checks:
            - if the voucher is already in the cart
            - if a non compatible voucher is in the cart
            - if there are products in the cart (gifts excluded)
            Important note: this MUST be the last check, because if the tested cart rule has priority over a non combinable one in the cart, we will switch them
        */
        $nb_products = Cart::getNbProducts($context->cart->id);
        $otherCartRules = array();
        if ($check_carrier) {
            $otherCartRules = $context->cart->getCartRules();
        }

        ...

Right before it, we need to get products, in case the variable is not set yet, and check for our conditions

        if(!isset($products))
            $products = $context->cart->getProducts();

        if($products)
        {
            // first, let's get our current cart rule ID
            if($this->id == 1) // not thought for extensive usage
            {
                // having them hardcoded here, can be expanded
                $valid_categories = array(7); // Blouses category
            }
            
        }

This part is not thought for an extensive usage, so if you want to use more than a couple of special price rules, you might need to find another way. We are hardcoding the cart rule id, so that this condition is only applied when we are adding it. This will also make sure this rule is removed from the cart, in case other invalid products are added afterwards.
Let’s now check if all products are at least belonging to the Blouses category

        if(!isset($products))
            $products = $context->cart->getProducts();

        if($products)
        {
            // first, let's get our current cart rule ID
            if($this->id == 1) // not thought for extensive usage
            {
                // having them hardcoded here, can be expanded
                $valid_categories = array(7); // Blouses category
                foreach ($products as $prod) {
                    $prod_categories = Product::getProductCategories($prod['id_product']);
                    foreach ($valid_categories as $val_cat) {
                        if(!in_array($val_cat, $prod_categories)) // if the blouse category is not among product categories
                           return (!$display_error) ? false : Tools::displayError('One or more products in your cart are not compatible with this discount'); // we deny the voucher's usage
                    }
                }    
            }
            
        }

If the product is not in any of the valid categories (in this case, just Blouses), then we cannot add the rule. Save and refresh, then clear cache if you used an override.
You can now try adding the rule to the cart. First, with a valid product only. Then remove the rule, add an invalid product, and add the rule back again. You should see a message popping up:

PrestaShop Cart Rule restricted by category

If not, the condition is not triggering, and you have to go back and print a debug of the rule/categories.
If you see the error, you can erase the invalid product, add the voucher again (it should be valid this time), then add the product once more. The rule should be automatically removed from your cart, since it becomes invalid. Unfortunately, there is no way to trigger an error message when this happens.

You like the tuts and want to say "thank you"? Well, you can always feel free to donate:

You like the tuts and want to say "thank you"? Well, you can always feel free to donate: