Jump to content

Vocuher that should exclude products already on sale, doesn't work correctly. Prestashop 1.7


Recommended Posts

I created a voucher that should exclude products on sale.

When I only have discounted products in my cart it works as expected.

If I there are not-discounted products in cart, then the Voucher is applied to all cart, including products already on sale.

I tried to implement code changes from this thread, but it doesn't seem to work for me: https://www.prestashop.com/forums/topic/290274-disabling-discounts-cart-rules-to-items-already-on-sale/

Maybe in 1.7 there is another way to solve this?

 

 

 

Link to comment
Share on other sites

  • 7 months later...

I am having the same issue. If only the product on sale is in the cart then the voucher won't work. However, as soon as I add another product to the cart then the discount is applied and should only be applied to the product that is not on sale. Also, the option "Exclude discounted products" in the Actions page of the cart rule is set to yes.

I did find this thread on Github: https://github.com/PrestaShop/PrestaShop/issues/10300. This is the specific issue in question, unfortunately it looks like the fix got pushed back to the 1.7.7.0 milestone, which has still not been released. @mantas393were you able to find a fix for the time being? Does anyone know a fix that will work currently while we're waiting for the update?

Link to comment
Share on other sites

  • 3 months later...
On 4/27/2020 at 5:28 PM, prus said:

No it doesn't work on 1.7.6.x... it's slated for the 1.7.7.0 release, which shows is 96% complete as of right now. It shows this release is due by June 1 but most releases are delayed several days, if not weeks.

image.png.d26a60c177362e697c1da8be52e2a015.png

 

I was able to take the code from the merge on git that fixes this issue and replace the file with my 'cart/src/Core/Cart/CartRuleCalculator.php' file and that is currently working on 1.7.6.5. Here's the entire code from my CarRuleCalculator.php file that works for me:

 @@ -0,0 +1,308 @@
<?php
/**
 * 2007-2019 PrestaShop and Contributors
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/OSL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to [email protected] so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to https://www.prestashop.com for more information.
 *
 * @author    PrestaShop SA <[email protected]>
 * @copyright 2007-2019 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
 * International Registered Trademark & Property of PrestaShop SA
 */

namespace PrestaShop\PrestaShop\Core\Cart;

use Cart;

class CartRuleCalculator
{
    /**
     * @var Calculator
     */
    protected $calculator;

    /**
     * @var CartRowCollection
     */
    protected $cartRows;

    /**
     * @var CartRuleCollection
     */
    protected $cartRules;

    /**
     * @var Fees
     */
    protected $fees;

    /**
     * process cartrules calculation
     */
    public function applyCartRules()
    {
        foreach ($this->cartRules as $cartRule) {
            $this->applyCartRule($cartRule);
        }
    }

    /**
     * process cartrules calculation, excluding free-shipping processing
     */
    public function applyCartRulesWithoutFreeShipping()
    {
        foreach ($this->cartRules as $cartRule) {
            $this->applyCartRule($cartRule, false);
        }
    }

    /**
     * @param \PrestaShop\PrestaShop\Core\Cart\CartRuleCollection $cartRules
     *
     * @return CartRuleCalculator
     */
    public function setCartRules($cartRules)
    {
        $this->cartRules = $cartRules;

        return $this;
    }

    /**
     * @param CartRuleData $cartRuleData
     * @param bool $withFreeShipping used to calculate free shipping discount (avoid loop on shipping calculation)
     *
     * @throws \PrestaShopDatabaseException
     */
    protected function applyCartRule(CartRuleData $cartRuleData, $withFreeShipping = true)
    {
        $cartRule = $cartRuleData->getCartRule();
        $cart = $this->calculator->getCart();

        if (!\CartRule::isFeatureActive()) {
            return;
        }

        // Free shipping on selected carriers
        if ($cartRule->free_shipping && $withFreeShipping) {
            $initialShippingFees = new AmountImmutable(
                $cart->getOrderTotal(true, Cart::ONLY_SHIPPING),
                $cart->getOrderTotal(false, Cart::ONLY_SHIPPING)
            );
            $this->calculator->getFees()->subDiscountValueShipping($initialShippingFees);
            $cartRuleData->addDiscountApplied($initialShippingFees);
        }

        // Free gift
        if ((int) $cartRule->gift_product) {
            foreach ($this->cartRows as $cartRow) {
                $product = $cartRow->getRowData();
                if ($product['id_product'] == $cartRule->gift_product
                    && ($product['id_product_attribute'] == $cartRule->gift_product_attribute
                        || !(int) $cartRule->gift_product_attribute)
                ) {
                    $cartRuleData->addDiscountApplied($cartRow->getFinalUnitPrice());
                    $cartRow->applyFlatDiscount($cartRow->getFinalUnitPrice());
                }
            }
        }

        // Discount (%) on the whole order
        if ($cartRule->reduction_percent && $cartRule->reduction_product == 0) {
            foreach ($this->cartRows as $cartRow) {
              $product = $cartRow->getRowData();
              if ((($cartRule->reduction_exclude_special && !$product['reduction_applies'])
                    || !$cartRule->reduction_exclude_special)) {
                $amount = $cartRow->applyPercentageDiscount($cartRule->reduction_percent);
                $cartRuleData->addDiscountApplied($amount);
              }
            }
        }

        // Discount (%) on a specific product
        if ($cartRule->reduction_percent && $cartRule->reduction_product > 0) {
            foreach ($this->cartRows as $cartRow) {
                if ($cartRow->getRowData()['id_product'] == $cartRule->reduction_product) {
                    $amount = $cartRow->applyPercentageDiscount($cartRule->reduction_percent);
                    $cartRuleData->addDiscountApplied($amount);
                }
            }
        }

        // Discount (%) on the cheapest product
        if ($cartRule->reduction_percent && $cartRule->reduction_product == -1) {
            /** @var CartRow|null $cartRowCheapest */
            $cartRowCheapest = null;
            foreach ($this->cartRows as $cartRow) {
                $product = $cartRow->getRowData();
                if (((($cartRule->reduction_exclude_special && !$product['reduction_applies'])
                        || !$cartRule->reduction_exclude_special)) && ($cartRowCheapest === null
                        || $cartRowCheapest->getInitialUnitPrice()->getTaxIncluded() > $cartRow->getInitialUnitPrice()
                            ->getTaxIncluded())
                ) {
                    $cartRowCheapest = $cartRow;
                }
            }
            if ($cartRowCheapest !== null) {
                // apply only on one product of the cheapest row
                $discountTaxIncluded = $cartRowCheapest->getInitialUnitPrice()->getTaxIncluded()
                                       * $cartRule->reduction_percent / 100;
                $discountTaxExcluded = $cartRowCheapest->getInitialUnitPrice()->getTaxExcluded()
                                       * $cartRule->reduction_percent / 100;
                $amount = new AmountImmutable($discountTaxIncluded, $discountTaxExcluded);
                $cartRowCheapest->applyFlatDiscount($amount);
                $cartRuleData->addDiscountApplied($amount);
            }
        }

        // Discount (%) on the selection of products
        if ($cartRule->reduction_percent && $cartRule->reduction_product == -2) {
            $selected_products = $cartRule->checkProductRestrictionsFromCart($cart, true);
            if (is_array($selected_products)) {
                foreach ($this->cartRows as $cartRow) {
                    $product = $cartRow->getRowData();
                    if (in_array($product['id_product'] . '-' . $product['id_product_attribute'], $selected_products)
                        || in_array($product['id_product'] . '-0', $selected_products)
                           && (($cartRule->reduction_exclude_special && !$product['reduction_applies'])
                               || !$cartRule->reduction_exclude_special)) {
                        $amount = $cartRow->applyPercentageDiscount($cartRule->reduction_percent);
                        $cartRuleData->addDiscountApplied($amount);
                    }
                }
            }
        }

        // Discount (¤) : weighted calculation on all concerned rows
        //                weight factor got from price with same tax (incl/excl) as voucher
        if ((float) $cartRule->reduction_amount > 0) {
            $concernedRows = new CartRowCollection();
            if ($cartRule->reduction_product > 0) {
                // discount on single product
                foreach ($this->cartRows as $cartRow) {
                    if ($cartRow->getRowData()['id_product'] == $cartRule->reduction_product) {
                        $concernedRows->addCartRow($cartRow);
                    }
                }
            } elseif ($cartRule->reduction_product == 0) {
                // Discount (¤) on the whole order
                $concernedRows = $this->cartRows;
            }
            /*
             * Reduction on the cheapest or on the selection is not really meaningful and has been disabled in the backend
             * Please keep this code, so it won't be considered as a bug
             * elseif ($this->reduction_product == -1)
             * elseif ($this->reduction_product == -2)
             */

            // currency conversion
            $discountConverted = $this->convertAmountBetweenCurrencies(
                $cartRule->reduction_amount,
                new \Currency($cartRule->reduction_currency),
                new \Currency($cart->id_currency)
            );

            // get total of concerned rows
            $totalTaxIncl = $totalTaxExcl = 0;
            foreach ($concernedRows as $concernedRow) {
                $totalTaxIncl += $concernedRow->getFinalTotalPrice()->getTaxIncluded();
                $totalTaxExcl += $concernedRow->getFinalTotalPrice()->getTaxExcluded();
            }

            // The reduction cannot exceed the products total, except when we do not want it to be limited (for the partial use calculation)
            $discountConverted = min($discountConverted, $cartRule->reduction_tax ? $totalTaxIncl : $totalTaxExcl);

            // apply weighted discount :
            // on each line we apply a part of the discount corresponding to discount*rowWeight/total
            foreach ($concernedRows as $concernedRow) {
                // get current line tax rate
                $taxRate = 0;
                if ($concernedRow->getFinalTotalPrice()->getTaxExcluded() != 0) {
                    $taxRate = ($concernedRow->getFinalTotalPrice()->getTaxIncluded()
                                - $concernedRow->getFinalTotalPrice()->getTaxExcluded())
                               / $concernedRow->getFinalTotalPrice()->getTaxExcluded();
                }
                $weightFactor = 0;
                if ($cartRule->reduction_tax) {
                    // if cart rule amount is set tax included : calculate weight tax included
                    if ($totalTaxIncl != 0) {
                        $weightFactor = $concernedRow->getFinalTotalPrice()->getTaxIncluded() / $totalTaxIncl;
                    }
                    $discountAmountTaxIncl = $discountConverted * $weightFactor;
                    // recalculate tax included
                    $discountAmountTaxExcl = $discountAmountTaxIncl / (1 + $taxRate);
                } else {
                    // if cart rule amount is set tax excluded : calculate weight tax excluded
                    if ($totalTaxExcl != 0) {
                        $weightFactor = $concernedRow->getFinalTotalPrice()->getTaxExcluded() / $totalTaxExcl;
                    }
                    $discountAmountTaxExcl = $discountConverted * $weightFactor;
                    // recalculate tax excluded
                    $discountAmountTaxIncl = $discountAmountTaxExcl * (1 + $taxRate);
                }
                $amount = new AmountImmutable($discountAmountTaxIncl, $discountAmountTaxExcl);
                $concernedRow->applyFlatDiscount($amount);
                $cartRuleData->addDiscountApplied($amount);
            }
        }
    }

    /**
     * @param \PrestaShop\PrestaShop\Core\Cart\Calculator $calculator
     *
     * @return CartRuleCalculator
     */
    public function setCalculator($calculator)
    {
        $this->calculator = $calculator;

        return $this;
    }

    protected function convertAmountBetweenCurrencies($amount, \Currency $currencyFrom, \Currency $currencyTo)
    {
        if ($amount == 0 || $currencyFrom->conversion_rate == 0) {
            return 0;
        }

        // convert to default currency
        $amount /= $currencyFrom->conversion_rate;
        // convert to destination currency
        $amount *= $currencyTo->conversion_rate;

        return $amount;
    }

    /**
     * @param \PrestaShop\PrestaShop\Core\Cart\CartRowCollection $cartRows
     *
     * @return CartRuleCalculator
     */
    public function setCartRows($cartRows)
    {
        $this->cartRows = $cartRows;

        return $this;
    }

    /**
     * @return CartRuleCollection
     */
    public function getCartRulesData()
    {
        return $this->cartRules;
    }
}

 

  • Thanks 1
Link to comment
Share on other sites

  • 2 months later...
On 4/29/2020 at 7:26 PM, Idyllic said:

No it doesn't work on 1.7.6.x... it's slated for the 1.7.7.0 release, which shows is 96% complete as of right now. It shows this release is due by June 1 but most releases are delayed several days, if not weeks.

image.png.d26a60c177362e697c1da8be52e2a015.png

 

I was able to take the code from the merge on git that fixes this issue and replace the file with my 'cart/src/Core/Cart/CartRuleCalculator.php' file and that is currently working on 1.7.6.5. Here's the entire code from my CarRuleCalculator.php file that works for me:


 @@ -0,0 +1,308 @@
<?php
/**
 * 2007-2019 PrestaShop and Contributors
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/OSL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to [email protected] so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to https://www.prestashop.com for more information.
 *
 * @author    PrestaShop SA <[email protected]>
 * @copyright 2007-2019 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
 * International Registered Trademark & Property of PrestaShop SA
 */

namespace PrestaShop\PrestaShop\Core\Cart;

use Cart;

class CartRuleCalculator
{
    /**
     * @var Calculator
     */
    protected $calculator;

    /**
     * @var CartRowCollection
     */
    protected $cartRows;

    /**
     * @var CartRuleCollection
     */
    protected $cartRules;

    /**
     * @var Fees
     */
    protected $fees;

    /**
     * process cartrules calculation
     */
    public function applyCartRules()
    {
        foreach ($this->cartRules as $cartRule) {
            $this->applyCartRule($cartRule);
        }
    }

    /**
     * process cartrules calculation, excluding free-shipping processing
     */
    public function applyCartRulesWithoutFreeShipping()
    {
        foreach ($this->cartRules as $cartRule) {
            $this->applyCartRule($cartRule, false);
        }
    }

    /**
     * @param \PrestaShop\PrestaShop\Core\Cart\CartRuleCollection $cartRules
     *
     * @return CartRuleCalculator
     */
    public function setCartRules($cartRules)
    {
        $this->cartRules = $cartRules;

        return $this;
    }

    /**
     * @param CartRuleData $cartRuleData
     * @param bool $withFreeShipping used to calculate free shipping discount (avoid loop on shipping calculation)
     *
     * @throws \PrestaShopDatabaseException
     */
    protected function applyCartRule(CartRuleData $cartRuleData, $withFreeShipping = true)
    {
        $cartRule = $cartRuleData->getCartRule();
        $cart = $this->calculator->getCart();

        if (!\CartRule::isFeatureActive()) {
            return;
        }

        // Free shipping on selected carriers
        if ($cartRule->free_shipping && $withFreeShipping) {
            $initialShippingFees = new AmountImmutable(
                $cart->getOrderTotal(true, Cart::ONLY_SHIPPING),
                $cart->getOrderTotal(false, Cart::ONLY_SHIPPING)
            );
            $this->calculator->getFees()->subDiscountValueShipping($initialShippingFees);
            $cartRuleData->addDiscountApplied($initialShippingFees);
        }

        // Free gift
        if ((int) $cartRule->gift_product) {
            foreach ($this->cartRows as $cartRow) {
                $product = $cartRow->getRowData();
                if ($product['id_product'] == $cartRule->gift_product
                    && ($product['id_product_attribute'] == $cartRule->gift_product_attribute
                        || !(int) $cartRule->gift_product_attribute)
                ) {
                    $cartRuleData->addDiscountApplied($cartRow->getFinalUnitPrice());
                    $cartRow->applyFlatDiscount($cartRow->getFinalUnitPrice());
                }
            }
        }

        // Discount (%) on the whole order
        if ($cartRule->reduction_percent && $cartRule->reduction_product == 0) {
            foreach ($this->cartRows as $cartRow) {
              $product = $cartRow->getRowData();
              if ((($cartRule->reduction_exclude_special && !$product['reduction_applies'])
                    || !$cartRule->reduction_exclude_special)) {
                $amount = $cartRow->applyPercentageDiscount($cartRule->reduction_percent);
                $cartRuleData->addDiscountApplied($amount);
              }
            }
        }

        // Discount (%) on a specific product
        if ($cartRule->reduction_percent && $cartRule->reduction_product > 0) {
            foreach ($this->cartRows as $cartRow) {
                if ($cartRow->getRowData()['id_product'] == $cartRule->reduction_product) {
                    $amount = $cartRow->applyPercentageDiscount($cartRule->reduction_percent);
                    $cartRuleData->addDiscountApplied($amount);
                }
            }
        }

        // Discount (%) on the cheapest product
        if ($cartRule->reduction_percent && $cartRule->reduction_product == -1) {
            /** @var CartRow|null $cartRowCheapest */
            $cartRowCheapest = null;
            foreach ($this->cartRows as $cartRow) {
                $product = $cartRow->getRowData();
                if (((($cartRule->reduction_exclude_special && !$product['reduction_applies'])
                        || !$cartRule->reduction_exclude_special)) && ($cartRowCheapest === null
                        || $cartRowCheapest->getInitialUnitPrice()->getTaxIncluded() > $cartRow->getInitialUnitPrice()
                            ->getTaxIncluded())
                ) {
                    $cartRowCheapest = $cartRow;
                }
            }
            if ($cartRowCheapest !== null) {
                // apply only on one product of the cheapest row
                $discountTaxIncluded = $cartRowCheapest->getInitialUnitPrice()->getTaxIncluded()
                                       * $cartRule->reduction_percent / 100;
                $discountTaxExcluded = $cartRowCheapest->getInitialUnitPrice()->getTaxExcluded()
                                       * $cartRule->reduction_percent / 100;
                $amount = new AmountImmutable($discountTaxIncluded, $discountTaxExcluded);
                $cartRowCheapest->applyFlatDiscount($amount);
                $cartRuleData->addDiscountApplied($amount);
            }
        }

        // Discount (%) on the selection of products
        if ($cartRule->reduction_percent && $cartRule->reduction_product == -2) {
            $selected_products = $cartRule->checkProductRestrictionsFromCart($cart, true);
            if (is_array($selected_products)) {
                foreach ($this->cartRows as $cartRow) {
                    $product = $cartRow->getRowData();
                    if (in_array($product['id_product'] . '-' . $product['id_product_attribute'], $selected_products)
                        || in_array($product['id_product'] . '-0', $selected_products)
                           && (($cartRule->reduction_exclude_special && !$product['reduction_applies'])
                               || !$cartRule->reduction_exclude_special)) {
                        $amount = $cartRow->applyPercentageDiscount($cartRule->reduction_percent);
                        $cartRuleData->addDiscountApplied($amount);
                    }
                }
            }
        }

        // Discount (¤) : weighted calculation on all concerned rows
        //                weight factor got from price with same tax (incl/excl) as voucher
        if ((float) $cartRule->reduction_amount > 0) {
            $concernedRows = new CartRowCollection();
            if ($cartRule->reduction_product > 0) {
                // discount on single product
                foreach ($this->cartRows as $cartRow) {
                    if ($cartRow->getRowData()['id_product'] == $cartRule->reduction_product) {
                        $concernedRows->addCartRow($cartRow);
                    }
                }
            } elseif ($cartRule->reduction_product == 0) {
                // Discount (¤) on the whole order
                $concernedRows = $this->cartRows;
            }
            /*
             * Reduction on the cheapest or on the selection is not really meaningful and has been disabled in the backend
             * Please keep this code, so it won't be considered as a bug
             * elseif ($this->reduction_product == -1)
             * elseif ($this->reduction_product == -2)
             */

            // currency conversion
            $discountConverted = $this->convertAmountBetweenCurrencies(
                $cartRule->reduction_amount,
                new \Currency($cartRule->reduction_currency),
                new \Currency($cart->id_currency)
            );

            // get total of concerned rows
            $totalTaxIncl = $totalTaxExcl = 0;
            foreach ($concernedRows as $concernedRow) {
                $totalTaxIncl += $concernedRow->getFinalTotalPrice()->getTaxIncluded();
                $totalTaxExcl += $concernedRow->getFinalTotalPrice()->getTaxExcluded();
            }

            // The reduction cannot exceed the products total, except when we do not want it to be limited (for the partial use calculation)
            $discountConverted = min($discountConverted, $cartRule->reduction_tax ? $totalTaxIncl : $totalTaxExcl);

            // apply weighted discount :
            // on each line we apply a part of the discount corresponding to discount*rowWeight/total
            foreach ($concernedRows as $concernedRow) {
                // get current line tax rate
                $taxRate = 0;
                if ($concernedRow->getFinalTotalPrice()->getTaxExcluded() != 0) {
                    $taxRate = ($concernedRow->getFinalTotalPrice()->getTaxIncluded()
                                - $concernedRow->getFinalTotalPrice()->getTaxExcluded())
                               / $concernedRow->getFinalTotalPrice()->getTaxExcluded();
                }
                $weightFactor = 0;
                if ($cartRule->reduction_tax) {
                    // if cart rule amount is set tax included : calculate weight tax included
                    if ($totalTaxIncl != 0) {
                        $weightFactor = $concernedRow->getFinalTotalPrice()->getTaxIncluded() / $totalTaxIncl;
                    }
                    $discountAmountTaxIncl = $discountConverted * $weightFactor;
                    // recalculate tax included
                    $discountAmountTaxExcl = $discountAmountTaxIncl / (1 + $taxRate);
                } else {
                    // if cart rule amount is set tax excluded : calculate weight tax excluded
                    if ($totalTaxExcl != 0) {
                        $weightFactor = $concernedRow->getFinalTotalPrice()->getTaxExcluded() / $totalTaxExcl;
                    }
                    $discountAmountTaxExcl = $discountConverted * $weightFactor;
                    // recalculate tax excluded
                    $discountAmountTaxIncl = $discountAmountTaxExcl * (1 + $taxRate);
                }
                $amount = new AmountImmutable($discountAmountTaxIncl, $discountAmountTaxExcl);
                $concernedRow->applyFlatDiscount($amount);
                $cartRuleData->addDiscountApplied($amount);
            }
        }
    }

    /**
     * @param \PrestaShop\PrestaShop\Core\Cart\Calculator $calculator
     *
     * @return CartRuleCalculator
     */
    public function setCalculator($calculator)
    {
        $this->calculator = $calculator;

        return $this;
    }

    protected function convertAmountBetweenCurrencies($amount, \Currency $currencyFrom, \Currency $currencyTo)
    {
        if ($amount == 0 || $currencyFrom->conversion_rate == 0) {
            return 0;
        }

        // convert to default currency
        $amount /= $currencyFrom->conversion_rate;
        // convert to destination currency
        $amount *= $currencyTo->conversion_rate;

        return $amount;
    }

    /**
     * @param \PrestaShop\PrestaShop\Core\Cart\CartRowCollection $cartRows
     *
     * @return CartRuleCalculator
     */
    public function setCartRows($cartRows)
    {
        $this->cartRows = $cartRows;

        return $this;
    }

    /**
     * @return CartRuleCollection
     */
    public function getCartRulesData()
    {
        return $this->cartRules;
    }
}

 

great fix!
also for me it solves ... with prestashop 1.7.5 .

too many problems with discounts on ps 1.7.x?!
.... there is also the problem with free shipping ... mah

 

Link to comment
Share on other sites

  • 1 year later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...