Jump to content
Sign in to follow this  
inveostore.com

[Fix] Quantity Discounts do not work properly (BUGS 6792 & 6827)

Recommended Posts

The aim of this post is to explain the fix for Quantity Discounts to PS developers (in Bugtracker under 6792 & 6827) and show how to fix it.

Description: Quantity Discounts do not work properly -> currently they only work correctly for percentage discounts and with a Tax assigned to product which "correct" the final price so everything looks almost fine (sorry for description - it is too much time ago we debug this problem and I do not really remember more details).

Reason: The reason why it does not work is basically due to the design of code located in classes/PaymentModule.php:


$reduc = QuantityDiscount::getValue($price_wt, $qtyD->id_discount_type, $qtyD->value, new Currency(intval($order->id_currency)));



This code INCORRECTLY computes quantity discount amount for product_quantity_discount field in order_details table, because it computes the discount from price ($price_wt variable) to which was Quantity Discount already applied (and also Group Reduction applied what is wrong as well).

The problem with "Quantity Discount already applied" can easily be solved by getting the price of product by calling Product::getPriceStatic() method with 1 quantity of product (currently it uses variable with price computed for multiple products).
The problem with "Group Reduction already applied" has to be solved by adding new argument to Product::getPriceStatic() (there is no other way).

Here is a fix:

Open classes/PaymentModule.php

This fixes how product_quantity_discount field is computed (need also changes in Product::getPriceStatic() - described below):

Find:

$reduc = QuantityDiscount::getValue($price_wt, $qtyD->id_discount_type, $qtyD->value, new Currency(intval($order->id_currency)));



Replace with:

$price_wt_no_qtyD = Product::getPriceStatic(intval($product['id_product']), true, ($product['id_product_attribute'] ? intval($product['id_product_attribute']) : NULL), 6, NULL, false, true, 1, false, NULL, NULL, NULL, false);
$reduc = QuantityDiscount::getValue($price_wt_no_qtyD, $qtyD->id_discount_type, $qtyD->value, ($tax) ? true : false, $tax, $currency);



This fixes the price inserted to product_price field in order_details table which needs to be pure price without any reductions applied:

Find:

'.floatval(Product::getPriceStatic(intval($product['id_product']), false, ($product['id_product_attribute'] ? intval($product['id_product_attribute']) : NULL), (Product::getTaxCalculationMethod(intval($order->id_customer)) == PS_TAX_EXC ? 2 : 6), NULL, false, false, $product['cart_quantity'], false, intval($order->id_customer), intval($order->id_cart), intval($order->id_address_delivery))).',



Replace with:

'.floatval(Product::getPriceStatic(intval($product['id_product']), false, ($product['id_product_attribute'] ? intval($product['id_product_attribute']) : NULL), (Product::getTaxCalculationMethod(intval($order->id_customer)) == PS_TAX_EXC ? 2 : 6), NULL, false, false, 1, false, intval($order->id_customer), intval($order->id_cart), intval($order->id_address_delivery))).',



This fixes strange warning but not required I think:

Find:

'.(int)QuantityDiscount::getDiscountFromQuantity(intval($product['id_product']), intval($product['cart_quantity'])).',



Replace with:

'.((!QuantityDiscount::getDiscountFromQuantity(intval($product['id_product']), intval($product['cart_quantity']))) ? '0' : '1').',




Now we need to add new argument to Product::getPriceStatic() method in classes/Product.php file:
(to be able to get price without Group Reduction discount applied - this fix does not change the behavior of this method so any old code will work exactly same as before)

Find:

public static function getPriceStatic($id_product, $usetax = true, $id_product_attribute = NULL, $decimals = 6, $divisor = NULL, $only_reduc = false, $usereduc = true, $quantity = 1, $forceAssociatedTax = false, $id_customer = NULL, $id_cart = NULL, $id_address_delivery = NULL)



Replace with:

public static function getPriceStatic($id_product, $usetax = true, $id_product_attribute = NULL, $decimals = 6, $divisor = NULL, $only_reduc = false, $usereduc = true, $quantity = 1, $forceAssociatedTax = false, $id_customer = NULL, $id_cart = NULL, $id_address_delivery = NULL, $group_reduc = true)



Find:

if ($usereduc)
   $price -= Tools::ps_round($price * Group::getReduction(((isset($id_customer) AND $id_customer) ? $id_customer : 0)) / 100, 2);



Replace with:

if ($usereduc && $group_reduc)
   $price -= Tools::ps_round($price * Group::getReduction(((isset($id_customer) AND $id_customer) ? $id_customer : 0)) / 100, 2);



Find:

$cacheId = $id_product.'-'.($usetax?'1':'0').'-'.$id_product_attribute.'-'.$decimals.'-'.$divisor.'-'.($only_reduc?'1':'0').'-'.($usereduc?'1':'0').'-'.$quantity.'-'.($id_customer ? $id_customer : '0');



Replace with:

$cacheId = $id_product.'-'.($usetax?'1':'0').'-'.$id_product_attribute.'-'.$decimals.'-'.$divisor.'-'.($only_reduc?'1':'0').'-'.($usereduc?'1':'0').'-'.($group_reduc?'1':'0').'-'.$quantity.'-'.($id_customer ? $id_customer : '0');



All is now FINALLY correctly inserted to database and only last two issues needs to be corrected:
Order::_deleteProduct() and Order::setProductPrices() method in classes/Order.php. This fix will be posted in next post because there is a limit of post size.

Share this post


Link to post
Share on other sites

Open classes/Order.php file and replace complete Order::setProductPrices() method with following code:

    public function setProductPrices(&$row)
   {
       if ($this->_taxCalculationMethod == PS_TAX_EXC)
           $row['product_price'] = Tools::ps_round($row['product_price'], 2);
       else
           $row['product_price_wt'] = Tools::ps_round($row['product_price'] * (1 + $row['tax_rate'] / 100), 2);
       if ($row['reduction_percent'])
       {
           if ($this->_taxCalculationMethod == PS_TAX_EXC)
               $row['product_price'] = $row['product_price'] - $row['product_price'] * ($row['reduction_percent'] * 0.01);
           else
               $row['product_price_wt'] = Tools::ps_round($row['product_price_wt'] - $row['product_price_wt'] * ($row['reduction_percent'] * 0.01), 2);
       }
       if ($row['reduction_amount'])
       {
           if ($this->_taxCalculationMethod == PS_TAX_EXC)
               $row['product_price'] = $row['product_price'] - $row['reduction_amount'];
           else
               $row['product_price_wt'] = Tools::ps_round($row['product_price_wt'] - $row['reduction_amount'] * (1 + ($row['tax_rate'] * 0.01)), 2);
       }

       // Added - makes Qty discounts work properly - BEGIN
       if ($row['product_quantity_discount'])
       {
           if ($this->_taxCalculationMethod == PS_TAX_EXC)
               $row['product_price'] = Tools::ps_round($row['product_price'] - $row['product_quantity_discount'] / (1 + ($row['tax_rate'] * 0.01)), 2);
           else
               $row['product_price_wt'] = $row['product_price_wt'] - $row['product_quantity_discount'];
       }
       // Added - makes Qty discounts work properly - END

       if ($row['group_reduction'])
       {
           if ($this->_taxCalculationMethod == PS_TAX_EXC)
               $row['product_price'] = $row['product_price'] - $row['product_price'] * ($row['group_reduction'] * 0.01);
           else
               $row['product_price_wt'] = Tools::ps_round($row['product_price_wt'] - $row['product_price_wt'] * ($row['group_reduction'] * 0.01), 2);
       }
       if (($row['reduction_percent'] OR $row['reduction_amount'] OR $row['group_reduction']) AND $this->_taxCalculationMethod == PS_TAX_EXC)
           $row['product_price'] = Tools::ps_round($row['product_price'], 2);
       // Added - makes Qty discounts work properly - BEGIN
       if($row['product_quantity_discount'] AND $this->_taxCalculationMethod == PS_TAX_INC)
           $row['product_price'] = Tools::ps_round($row['product_price'], 2);
       // Added - makes Qty discounts work properly - END
       if ($this->_taxCalculationMethod == PS_TAX_EXC)
           $row['product_price_wt'] = Tools::ps_round($row['product_price'] * (1 + ($row['tax_rate'] * 0.01)), 2) + Tools::ps_round($row['ecotax'] * (1 + $row['ecotax_tax_rate'] / 100), 2);
       else
       {
           $row['product_price_wt_but_ecotax'] = $row['product_price_wt'];
           $row['product_price_wt'] = Tools::ps_round($row['product_price_wt'] + $row['ecotax'] * (1 + $row['ecotax_tax_rate'] / 100), 2);
       }
       $row['total_wt'] = $row['product_quantity'] * $row['product_price_wt'];
       $row['total_price'] = $row['product_quantity'] * $row['product_price_wt'];
   }

Share this post


Link to post
Share on other sites

Open classes/Order.php file and replace complete Order::_deleteProduct() method with following code:

    private function _deleteProduct($orderDetail, $quantity)
   {
       $row = Db::getInstance()->getRow('
                       SELECT *
                       FROM `'._DB_PREFIX_.'order_detail` od
                       WHERE od.`id_order_detail` = '.intval($orderDetail->id));
       $this->setProductPrices($row);

       /* Update cart */
       $cart = new Cart($this->id_cart);
       $cart->updateQty($quantity, $orderDetail->product_id, $orderDetail->product_attribute_id, false, 'down'); // customization are deleted in deleteCustomization
       $cart->update();

       /* Update order */
       $shippingDiff = $this->total_shipping - $cart->getOrderShippingCost();
       $price = $row['product_price_wt'] * $quantity;
       $this->total_products -= $row['product_price'] * $quantity;
       $this->total_products_wt -= $row['product_price_wt'] * $quantity;
       $this->total_shipping = $cart->getOrderShippingCost();
       $this->total_paid -= ($price + $shippingDiff);
       $this->total_paid_real -= ($price + $shippingDiff);

       /* Prevent from floating precision issues (total_products has only 2 decimals) */
       if ($this->total_products < 0)
           $this->total_products = 0;

       /* Prevent from floating precision issues */
       $this->total_paid = number_format($this->total_paid, 2, '.', '');
       $this->total_paid_real = number_format($this->total_paid_real, 2, '.', '');
       $this->total_products = number_format($this->total_products, 2, '.', '');
       $this->total_products_wt = number_format($this->total_products_wt, 2, '.', '');

       /* Update order detail */
       $orderDetail->product_quantity -= intval($quantity);

       if (!$orderDetail->product_quantity)
       {
           if (!$orderDetail->delete())
               return false;
           if (count($this->getProductsDetail()) == 0)
           {
               global $cookie;
               $history = new OrderHistory();
               $history->id_order = intval($this->id);
               $history->changeIdOrderState(_PS_OS_CANCELED_, intval($this->id));
               if (!$history->addWithemail())
                   return false;
           }
           return $this->update();
       }
       return $orderDetail->update() AND $this->update();
   }



Thats all. Now Quantity Discounts start to work properly. According to what we see in forums, integrating this fix to PS help a LOT of people. It was very common problem which reached many PS users.

If anything is not clear, feel free to ask us. I will reply ASAP.

BTW we made about 200 test orders during debugging this bug... :-)

Share this post


Link to post
Share on other sites

Hello! will these fixes work with prestashop version 1.3.2x or are they necessary for that version. Thanks for the advice!

Share this post


Link to post
Share on other sites

i tried the fix as instructed. I keep getting a parse error T_Publicon on line 348. I cannot imagine at this point what the error could be...

Share this post


Link to post
Share on other sites

can any one has the complete files that already customize / fixed these problems , and upload to here please

something like this i don't know how to change

Thank you very much!

Share this post


Link to post
Share on other sites

Since 1.3.0.7 is the last official PS version in 1.3 branch we plan in March to re-release this version with our additional fixes which were not included (for people who want to stay with 1.3).

Share this post


Link to post
Share on other sites

Because I have error on total calculation using discount per quantity without taxe, I could be happy with your corrected 1.3 PS version, Team will not update anymore 1.3 version.


I made all these modifications but this doesn't resolve miscalculation on total with percent quantity discount when taxe is not activated.

product A, price 2.50, discount 5% from 2

I added 2 on cart, price for each is 2.38, for 2 4.76 but total is 4.75 ! WRONG !

This issue cause a Paypal error on paiement (Paypal calculate right price 4.76)

Could it be possible to have a fix for it too?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...

Important Information

Cookies ensure the smooth running of our services. Using these, you accept the use of cookies. Learn More