Jump to content

[SOLVED] Discount for exact product quantity


gloomybear

Recommended Posts

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 by gloomybear (see edit history)
Link to comment
Share on other sites

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:

post-455771-0-41188300-1416755982_thumb.png

(Sorry, didn't change "From (quantity)" to "When buying exactly (quantity)" text, I'm confident you can remember that :-)  )

 

 

post-455771-0-49439100-1416755953_thumb.png

2 products, no discount

 

 

post-455771-0-43295000-1416756146_thumb.png

3 products gives discount

 

 

post-455771-0-15644600-1416756173_thumb.png

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 :-)  )

  • Like 1
Link to comment
Share on other sites

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...

  • Like 1
Link to comment
Share on other sites

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

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

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

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

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

  • Like 1
Link to comment
Share on other sites

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...