gloomybear Posted November 23, 2014 Share Posted November 23, 2014 (edited) Hello, I want to adjust default product quantity discounts so that discount is granted only for specific number of items in cart. This is because I have products nicely packed by manufacturer by 5 in a box. It is cheaper for me to sell whole box (no cost of packaging) so I can offer some reduction to my clients when they buy exactly 5 items (as originally packed by manufacturer). By default you can set a quantity discount for a product (on Price tab in Product Catalog). This applies with a condition ">=", so when a condition is set for quantity = 5, then customer will get discount when he adds 5 items or more to cart (i.e. discount is granted also for 6 items). I want this condition to be "==" so that discount is applied for 5 items (but not 6 items). Any ideas how to approach this? All help greatly appreciated!! Edited November 23, 2014 by gloomybear (see edit history) Link to comment Share on other sites More sharing options...
PascalVG Posted November 23, 2014 Share Posted November 23, 2014 Probably have to edit/( or better, override) file: classes/SpecificPrice.php: Find function: public static function getSpecificPrice($id_product, $id_shop, $id_currency, $id_country, $id_group, $quantity, $id_product_attribute = null, $id_customer = 0, $id_cart = 0, $real_quantity = 0) { if (!SpecificPrice::isFeatureActive()) return array(); /* ** The date is not taken into account for the cache, but this is for the better because it keeps the consistency for the whole script. ** The price must not change between the top and the bottom of the page */ $key = ((int)$id_product.'-'.(int)$id_shop.'-'.(int)$id_currency.'-'.(int)$id_country.'-'.(int)$id_group.'-'.(int)$quantity.'-'.(int)$id_product_attribute.'-'.(int)$id_cart.'-'.(int)$id_customer.'-'.(int)$real_quantity); if (!array_key_exists($key, SpecificPrice::$_specificPriceCache)) { $now = date('Y-m-d H:i:s'); $query = ' SELECT *, '.SpecificPrice::_getScoreQuery($id_product, $id_shop, $id_currency, $id_country, $id_group, $id_customer).' FROM `'._DB_PREFIX_.'specific_price` WHERE `id_product` IN (0, '.(int)$id_product.') AND `id_product_attribute` IN (0, '.(int)$id_product_attribute.') AND `id_shop` IN (0, '.(int)$id_shop.') AND `id_currency` IN (0, '.(int)$id_currency.') AND `id_country` IN (0, '.(int)$id_country.') AND `id_group` IN (0, '.(int)$id_group.') AND `id_customer` IN (0, '.(int)$id_customer.') AND ( (`from` = \'0000-00-00 00:00:00\' OR \''.$now.'\' >= `from`) AND (`to` = \'0000-00-00 00:00:00\' OR \''.$now.'\' <= `to`) ) AND id_cart IN (0, '.(int)$id_cart.') AND IF(`from_quantity` > 1, `from_quantity`, 0) <= '; // <-- HERE WE WILL CHANGE SOMETHING $query .= (Configuration::get('PS_QTY_DISCOUNT_ON_COMBINATION') || !$id_cart || !$real_quantity) ? (int)$quantity : max(1, (int)$real_quantity); $query .= ' ORDER BY `id_product_attribute` DESC, `from_quantity` DESC, `id_specific_price_rule` ASC, `score` DESC'; SpecificPrice::$_specificPriceCache[$key] = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($query); } return SpecificPrice::$_specificPriceCache[$key]; } About 10 lines from the end of the code above, you see line: AND IF(`from_quantity` > 1, `from_quantity`, 0) <= '; change into: AND IF(`from_quantity` > 1, `from_quantity`, 0) = '; Result: Example: discount of 5$ on a normal price of 16.51 when buying exactly 3 products: (Sorry, didn't change "From (quantity)" to "When buying exactly (quantity)" text, I'm confident you can remember that :-) ) 2 products, no discount 3 products gives discount 4 products, no discount any more again... Hope this helps, pascal (P.S. Was considering to make it like quantity MOD x, (so that you would get discount at 3,6,9 etc) but then thought you may give a different discount when buying 2 boxes, 3 boxes anyway, so did't bother in the end, as it was more work. If you do want that, play with the line below the one we changed. I leave that as an exercise for you :-) ) 1 Link to comment Share on other sites More sharing options...
gloomybear Posted November 23, 2014 Author Share Posted November 23, 2014 Thank you Pascal!! This is what I was looking for! Link to comment Share on other sites More sharing options...
Eolia Posted November 23, 2014 Share Posted November 23, 2014 +1 Pascal But... for 4 products, a discount shouldn't be applied on the 3 firsts and keep the last product at the normal price, no ? Link to comment Share on other sites More sharing options...
gloomybear Posted November 23, 2014 Author Share Posted November 23, 2014 +1 Pascal But... for 4 products, a discount shouldn't be applied on the 3 firsts and keep the last product at the normal price, no ? This is exactly what I wanted. Discount on 3, 4-th on the normal price. Link to comment Share on other sites More sharing options...
Eolia Posted November 23, 2014 Share Posted November 23, 2014 you must use the %modulo operator: $nb_products = 8 // for example $pack_value = 3; $nb_packs = $nb_products % $pack_value; $nb_specific_price = $nb_packs * $pack_value; $nb_normal_price = $nb_products - $nb_specific_price; $nb_packs = 2.So you have 2 packs of 3 (6 products) with a specific price and $nb_normal_price (2 products) with a normal price But you must make more changes in the SpecificPrice.php class... 1 Link to comment Share on other sites More sharing options...
gloomybear Posted November 24, 2014 Author Share Posted November 24, 2014 You are correct Eolia. Now I see that simple change would have some flaws (i.e. when customer orders 4 products while 3 are in pack then no discount would be granted at all). Link to comment Share on other sites More sharing options...
PascalVG Posted November 24, 2014 Share Posted November 24, 2014 Hmm, Interpreted the "but not 6 items" differently. Thought you meant, then "no discount at all" (as you still have extra cost of packaging which was the reason to give no discount), but you probably meant only for 5 (a box full) a discount, but for the 6th one not... Am I correct Ok, so then the wish is: - every "whole box" quantity (e.g. 5, 10, 15) within the total ordered quantity of that product (e.g 6,7, 13 etc) will get the discounted price, the left over ones (6-5 =1, or 7-5=2, 13-5-5=3) will be charged the normal price, right? Only problem is then: If you want to give an extra discount when the customer buys 10 bottles (because he buys more), in the first case, you could add a specific price for quantity=10. When using mod, that goes wrong then. You're then stuck with only one discount... Do you want to give only one discount, independent of how many (full) boxes he/she orders, or indeed maybe more discount for many (full) boxes? May need some rethinking... Please describe your exact wish, and we may be able to create something. Pascal Link to comment Share on other sites More sharing options...
gloomybear Posted November 25, 2014 Author Share Posted November 25, 2014 Thank you guys for all the input!! Let's put one example in place. I sell teacups. One teacup is $10. Manufacturer puts them 4 in a box. It is hard, durable box - enough to protect cups during shipping. I want to give my customers discount of 10% if they buy multiplicity of 4 teacups because this way I do not have spend money on wrapping (both materials & time). So the exact cases: 1. 3 teacups in cart = 3 x $10 = $30 2. 4 teacups in cart = 4 x $9 (10% discount applied for buying a full box) = $36 3. 5 teacups in cart = 4 x $9 + 1 x $10 = $46 This would be perfect solution Link to comment Share on other sites More sharing options...
PascalVG Posted November 26, 2014 Share Posted November 26, 2014 Hi GBear, Just to make sure: - Do you want the SAME discount for ALL multiples of 4: 4,8,12,16 (all 9$) or a DIFFERENT one for higher multiples of 4, like for example 4 and 8 (9$), but 12,16 (say maybe 8.50$)? This does make it more complicated indeed.... Needs some thinking time... pascal Link to comment Share on other sites More sharing options...
gloomybear Posted November 26, 2014 Author Share Posted November 26, 2014 Hi Pascal, I wanted flat discount rate, so regardless whether client orders 1 box (4 teacups) or 4 boxes (16 teacups) client is always granted 10% for full box. Thank you and Eolia for all hints. I will implement simplest solution (based on modulo operator) to give discount when client buys full boxes (i.e. 5 teacups = no discount) as fully fledged solution would be quite complicated. Link to comment Share on other sites More sharing options...
PascalVG Posted November 29, 2014 Share Posted November 29, 2014 Hi Gloomy, If you just want a discount for when they order 1 or more full boxes, do this: Restore the classes/SpecificPrice.php to it's old glory: AND IF(`from_quantity` > 1, `from_quantity`, 0) <= '; and save. Then edit file classes/Product.php (backup!!!) and edit function: public static function priceCalculation() In this function, scroll down to find this piece of code: // Add Tax if ($use_tax) $price = $product_tax_calculator->addTaxes($price); // Reduction $specific_price_reduction = 0; if (($only_reduc || $use_reduc) && $specific_price) { if ($specific_price['reduction_type'] == 'amount') { $reduction_amount = $specific_price['reduction']; if (!$specific_price['id_currency']) $reduction_amount = Tools::convertPrice($reduction_amount, $id_currency); $specific_price_reduction = !$use_tax ? $product_tax_calculator->removeTaxes($reduction_amount) : $reduction_amount; } else $specific_price_reduction = $price * $specific_price['reduction']; change this into: // Reduction $specific_price_reduction = 0; if (($only_reduc || $use_reduc) && $specific_price) { if ($specific_price['reduction_type'] == 'amount') { $reduction_amount = $specific_price['reduction']; if (!$specific_price['id_currency']) $reduction_amount = Tools::convertPrice($reduction_amount, $id_currency); $specific_price_reduction = !$use_tax ? $product_tax_calculator->removeTaxes($reduction_amount) : $reduction_amount; } else { if ($real_quantity > 0 ) $specific_price['reduction'] = ($real_quantity % $specific_price['from_quantity'] == 0 ? $specific_price['reduction'] : 0); $specific_price_reduction = $price * $specific_price['reduction']; } You see, at almost the end, I added: else { if ($real_quantity > 0 ) $specific_price['reduction'] = ($real_quantity % $specific_price['from_quantity'] == 0 ? $specific_price['reduction'] : 0); $specific_price_reduction = $price * $specific_price['reduction']; } (N.B. Don' forget the {..} brackets ) What it does: If there are any products to give a discount to: if the amount of products is a multiple of from_quantity, then give the discount, otherwise make the discount 0%. ( The from_quantity is the amount you specify in the specific price: from XX products, give YY% discount) I also created a rue where the amount works as you originally wanted it: 1-3 no discount 4 10% discount <-- from_quantity 5 (first 4 10% discount, 5th no discount) 6 (first 4 10% discount, 5th+6th no discount) 7 (first 4 10% discount, 5th+6th+7th no discount) 8 (2x from_quantity-> All 10% discount 9 (first 8 10% discount, 9th no discount) etc. every multiple of 4 gets 10% discount, rest none: If you want this change above to: else { if ($real_quantity > 0 ) $specific_price['reduction'] = (float)((float)($specific_price['from_quantity']*(int)($real_quantity / $specific_price['from_quantity']))/$real_quantity)*$specific_price['reduction']; $specific_price_reduction = $price * $specific_price['reduction']; } What it does: it calculates the "average discount per bottle" if only multiples of from_quantity are discunted If there are products to give a discount to: new discount% = (From_quantity*(real_quantity DIV From_quantity))/real_quantity) * discount% example: product price 50$ discount of 10% for multiples of 4 bottles (1 box = 4 bottles) total products: 10 (normal price total = 500$) (4*(10 DIV 4)/10) * 10% = (4*2)/10 * 10% = 8/10 * 10% > New 'average discount' per bottle = 8% = 460$ check: first 2 boxes = 8 bottles: 50$*(4*2) =400$ -> 10% discount = 360$ left over 2 bottles normal price = 50%*2 = 100$ total 360$ + 100$ = 460$ This sounds perfect. BUT... One little problem, the discounted price is calculated per bottle, so for example : when 7 bottles the discount = (4*1/7)*10% = (4/7)*10 = 5.714% discounted price per bottle: 47.142857 = 47.14$ You see the problem now, the ROUNDED price is 47.14. AFTER ROUNDING, it multiplies with 7 for 7 bottles = 329.98$ NOT 330.00$, there is a small round off difference. To fix that, you should fix the whole way of calculating the total price in PrestaShop, which goes to far for me. So, it's up to you if you can live with the small round off difference (method 2), or just use the 10% if EXACTLY multiples of box-quantity and else no discount (method 1). Hope this helps, pascal 1 Link to comment Share on other sites More sharing options...
gloomybear Posted November 29, 2014 Author Share Posted November 29, 2014 BIG kudos PacalVG! You are the man! Link to comment Share on other sites More sharing options...
PascalVG Posted November 29, 2014 Share Posted November 29, 2014 (edited) Let me know which one you chose :-) Thanks for letting me puzzle a little! I'll mark this topic as solved. (*edit, you did already) pascal Edited November 29, 2014 by PascalVG (see edit history) Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now