Background
Virtuemart is an excellent Open Source e-Commerce system that integrates with the Joomla content management system. I use it on a number of sites – some with more challenging eCommerce requirements than others.
While Virtuemart offers bulk purchase discounts (£10 each, or 3 or £27 style offers) – it unfortunately only implements these on a per-product basis. This is great if you want to order 3 of the exact same pair of baby shoes.
However, a common request is to implement category-based discounts, so for example you would be able to offer a discount where people buy multiple products from a particular category.
We do just this on SnugBaby, where we periodically run category based discounts on our baby shoes, where the requirement is that you get a discount if you choose to buy multiple products from a particular category.
Caveats
There are some caveats to the approach we’re going to describe below:
- It involves patching the Virtuemart source files – this is not a Plugin, Component, or module. You should attempt this only if you’re comfortable editing PHP code
- Further to the above – if you upgrade Virtuemart this will need re-applying, possibly with some tweaks and tickles
- This assumes you’re running Virtuemart 1.1.3 (Although the theory of the approach works with VM 1.0.x as well)
Summary
This approach uses the Virtuemart bulk pricing model to store the bulk prices. This means you need to go through the products that are on special offer and add in the bulk prices. In the example I’m going to use here we are selling baby shoes. The standard price is £16.50 per pair, but if you buy more than 1 pair (Of any design), then you get them for £15.00 per pair.
We market this as “2 for £30” – however the reality (Probably should be caveat number 4!) is that it becomes £15 per pair for any number of shoes 2 and over (E.g. you could get 3 pairs for £45, 4 pairs for £60 and so on …).
So. First things first. Add the pricing to Virtuemart. If you’ve never done bulk pricing in Virtuemart here’s a quick guide. For each of your products in turn, edit the product, and choose “List Prices”:
Then you’ll need to specify your pricing:
Once this is done then your customers can get the special price, but only if they buy 2 or more of the same product.
The fun part
We can make all the changes we need to in just one file, administrator/components/com_virtuemart/classes/ps_product.php. This file is responsible for calculating product pricing, taking into account quantity discounts. The function in question is get_price().
This function out-of-the-box is given a product ID and is asked to calculate the price. It has a section relating to quantity pricing which roughly follows the following logic:
– Cycle through all cart contents
– If the product_id of the cart item matches the product_id we’re trying to cost then sum up the quantity of this item
– Retrieve the product price based on total volume of a product in the cart
We’re going to amend the second step such that it sums up the quantities of all products in the same category as the product we’re trying to cost.
The full patch (For the impatient) is here.
The patch is split into 2 main sections. The first gets the category that the product we’re costing belongs to (Caveat #5 – I have no idea what happens if your bulk purchase products are in multiple categories!). It also checks if that category is eligible for bulk discounts. You’ll need to change the 28 below to match your category ID.
$product_parent_id = $this->get_field($product_id, “product_parent_id”);
// LW PATCH – Get the category of this product – used later to drive category-based multiple purchases
$sesq = “SELECT category_id FROM #__{vm}_product_category_xref WHERE product_id=’$product_id'”;
$db->setQuery($sesq); $db->query();
$db->next_record();
$sesprodcat = $db->f(“category_id”);
if ($sesprodcat == 28) {
$ses_cat_discount = TRUE;
} else {
$ses_cat_discount = FALSE;
}
// END LW PATCH
The next section changes the calculation so that instead of comparing the product IDs, we comapre category IDs:
if ($ses_cat_discount) {
$sesq = “SELECT category_id FROM #__{vm}_product_category_xref WHERE product_id='”.$cart[$i][“product_id”].”‘”;
$db->setQuery($sesq); $db->query();
$db->next_record();
$sesprodchildcat = $db->f(“category_id”);
if ($sesprodchildcat == $sesprodcat) {
$quantity += $cart[$i][“quantity”];
}
} else {
if ($cart[$i][“product_id”] == $product_id) {
if ($parent) {
$parent_id = $cart[$i][“parent_id”];
}
}
}
// END LW PATCH
And that’s that. Give it a try, let me know what you think!
July 15, 2009 at 3:16 pm
Hi Lee,
Thanks for your patch, this is what I was looking for! I know a little php editing but it’s unclear to me which parts of the original code I need to delete. To avoid errors, could you send me the adjusted PHP file so that I just need to change the category id’s. Thank you for your trouble.
Regards,
Willem-hein
July 15, 2009 at 8:18 pm
Hi – you can find a fully patched version of ps_product.php from Virtuemart 1.1.3 here –
http://www.leewillis.co.uk/patches/vm_category_bulk_discounts/ps_product.txt
(If you’re using a different version of Virtuemart then you’ll be better off downloading the patch file from the original post and applying that to your version)
December 21, 2010 at 12:22 pm
Hi
It seems great.
I have a question.
The dicounted prices are appear the same in the cart and the checkout proccess?
Sakis
December 21, 2010 at 12:26 pm
Not quite sure I understand the question, but maybe this will help.
Say you have a product that normally sells for £10 each. You might run an offer to “buy two for £15”. In this case the user will see a unit price of £7.50 in the cart and the checkout if they buy two or more of the product.
July 16, 2009 at 11:42 am
Thanks Lee, works like a charm!
Can I also use it for more than category? Or is that wishfull thinking?
Is it ok if I post a link to this page on the forum?
Regards Willem-Hein
July 16, 2009 at 11:55 am
>> Can I also use it for more than category? Or is that wishfull thinking?
There’s lots of variations of how that might work, so I’m loathe to say “yes” – it is a simple hack after all! That said, I have used a version of this to offer it across categories.
>> Is it ok if I post a link to this page on the forum?
Absolutely 🙂
Let me know the link and we can perhaps have a discussion there about what multi-category scenario you’d like to use it for and how it might work …
July 16, 2009 at 12:15 pm
The first forum I posted on is Dutch:
http://forum.dutchjoomla.org/showthread.php?p=371132#post371132
English should not be a problem though.
The other forum is the general vm forum:
http://forum.virtuemart.net/index.php?topic=57997.0
We can continue our discussion on the last one I will link to it from the dutch one.
July 17, 2009 at 6:34 pm
I made some changes to allow bulk discounts across multiple categories. Details in the thread at the vm forums here: http://forum.virtuemart.net/index.php?topic=57997.msg190359#msg190359
July 24, 2009 at 2:45 pm
What are the correct line numbers for a VM 1.0.x file? And do I have to replace or add the patch?
July 30, 2009 at 12:34 pm
You can either read the file, and apply the changes by hand – otherwise use the UNIX “patch” utility to apply them. I know that hack works in principle on VM 1.0.x (I used to use it before I upgraded to VM 1.1.x) but I don’t know off the top of my head if the code is exactly the same, or what the line numbers would be. I’ll try and grab a copy of 1.0.x over the next few days and post a patched version ….
August 6, 2009 at 1:58 am
nice hack!!
can this mod into .. percentage % discount.
Let say same category, order different products any mix and match… if you order 5 or more you can get the 10% discount at the check out and if you ordered 10 or more you have 20 % off.
will that be possible.
sl
August 6, 2009 at 1:18 pm
Hi sam – thanks for the feedback.
As is this hack gets the product pricing from the bulk purchase prices configured in Virtuemart already. The code we’re editing here isn’t the right place to start calculating % discounts.
However – if you simply set up bulk pricing at the required rates within Virtuemart then you’d get the same effect – albeit you’ll have the overhead of setting up the prices manually.
So, image you have two products:
– Product A – £10
– Product B – £20
If you set these up so that they have bulk pricing, e.g.
Product A is £10 for 0-4, £9 for 5-9, and £8 for 10-999
Product B is £20 for 0-4, £18 for 5-9, and £16 for 10-999
Then a user buying 3 of product A, and 3 of product B would be charged (3x£9 + 3x£18 = £81 (10% off £90)).
A user buying 5 of product A, and 5 of product B would be charged (5x£8 + 5x£16 = £120 (20% off £150).
So – you can acheive the same end-user experience as long as you set up the bulk pricing.
August 6, 2009 at 1:35 pm
Hi Sam,
I set up a scheme with quantity discount per category with lee’s hack. Works good! You can see it working on vespaghetti.eu.
With CSV improved I uploaded the different prices for different quantities (£10 for 0-4, £9 for 5-9, and £8 for 10-999). You can import them from a csv file edited in Excel with the csv improved component. This saved me quite a lot of time for giving price ranges for 110 products.
Regards,
Willem-Hein
November 13, 2009 at 5:53 pm
This is a great hack and (almost) what I’m looking for! I’m building a shop that has products with different sizes (attributes) at different costs so to use this would mean creating individual products for each size. A version whereby the percentage discount is used would be top class so that I could keep the sizing choices within the one product. Great work though! This should be an option in the shipped VM.
January 8, 2010 at 7:35 pm
This hack doesn’t sound like the solution for my problem but it’s as close as I’ve seen. I’m thinking mine should be already included and if not maybe easier.
My problem is:
Originally I set up my store so that each product was the artwork that can be printed on a shirt. Under each product I set up attributes for color, style, size. I gave the product a quantity discount and it did pretty much everything I wanted it to do. It took each attribute style(ex. t-shirt, hoodie,etc.) and gave them all the product discount so that 12 t-shirts and 12 hoodies = 24 pieces for the discount.
The problem is that hoodie’s weigh more than t-shirts and come in different available colors. So I tried setting them up as seperate items under each product so that I can give each it’s own attributes but this also gives each item it’s own quantity discount.
What I need is a way to make all items under the same product share the same discount. Either by using it’s own discount scheme or being able to add an add on value(+10.00 more for a hoodie, like it does as an attribute) to the already existing price for the product and then use the same discount.
I don’t have this site active yet but you can get an idea of what I need to do by visiting my other site http://www.studio-md.com
I used cubecart on that site but I want to get away from it.
I would appreciate any ideas on how to approach this.
thanks, mark
January 8, 2010 at 10:25 pm
Forget about my previous problem with the quantities. Don’t ask me why but It’s doing exactly what I wanted it to. I must be going nuts, I could swear it was treating them all separate before. Oh well…..
Consider me fixed!
January 8, 2010 at 10:55 pm
Great – glad you got it sorted 🙂
February 1, 2010 at 1:52 am
cannot believe it..it works so well on my site..thanks for this mod….you are Ace~!!!…
February 14, 2010 at 11:00 pm
Great modification. i googled all day to find somthing that do the trick.
works awsome !!!
Lee man realy nice work thx
i’m working with “virteuMart 1.1.4 stable” copy the http://www.leewillis.co.uk/patches/vm_category_bulk_discounts/ps_product.txt changed the category ID and it works here
Best regards wimm
February 15, 2010 at 10:45 pm
Woah there Wimm – that file is modified from the Virtuemart 1.1.3 tree and I can’t make any guarantees about it working properly with 1.1.4!
Your best bet is to manually apply the changes to the original ps_product.php from 1.1.4 ….
Failing that – drop me a copy of the original 1.1.4 file and I’ll apply the changes and post a new version …
Cheers
February 16, 2010 at 11:05 pm
I’ve posted a copy of the 1.1.4 ps_product.php here:
http://www.leewillis.co.uk/patches/vm_category_bulk_discounts/ps_product.vm114.php
February 17, 2010 at 12:53 am
Thx it works fine now.
You are great man.
Best regards wimm
June 7, 2010 at 5:09 pm
You don’t have to hack core VM files anymore, you can include them in your theme as a class override. It makes updating VM allot easier.
Here’s how to do it:
http://www.vm-expert.com/virtuemart-expert-blog/80-extending-virtuemart-114
Please note that I had to include the full function I was extending, but even so it’s v. handy!
June 7, 2010 at 5:21 pm
Hey – that’s pretty neat. As you say, in this case I don’t think it brings us much benefit (Since we’d have to include the whole function I’m not sure I’d be comfortable upgrading VM, and leaving an “old” copy of the function in place) – but it’s a neat trick to know – Thanks!
August 6, 2010 at 5:04 am
Lee, heres my situation. Maybe you can enlighten me. I have a website that is selling shoes. We are wanting to sell shoes at a specail of buy any one pair and get a second pair 50% off. I have applied your hack and everyting is work. What I did was setup the bulk pricing to be 0-1 pair to be full price and 2-2 pairs to be 75% of full price. Works great when you buy 2 of the same pair. But when you mix and match different shoes at different prices my totals are calculating to be less than the B1G1 50% off. Any ideas on what I can do to fix this?
Thanks, Glen
August 6, 2010 at 8:44 am
The price quoted will be based on 75% of the first pair + 75% of the second pair. That’s how this hack works I’m afraid. If you want a different model you’d have to check out one of the commercial discount plugins, or modify this to suit your needs.
August 6, 2010 at 5:42 am
The next problem I just noticed is, If I have Shoe-A for sale at $10. With a B1G1 50% off, if I buy 2 pairs they are priced at $7.50 each (total $15). But if I buy 3 pairs they are priced at $7.50 (total $22.50) With a B1G1 50% off the total should end up being $25.00.
Any thoughts?
Glen
August 6, 2010 at 8:45 am
See my previous note 🙂
That’s exactly how this mod works.
August 23, 2010 at 2:57 am
Whats up Lee, great advice, awesome advice so far!!! Here is my deal, hopefully you can help me!! Im working on a site that sells suits. Each suit has a product price of $250.00, but has a discounted price of $69.99, so costumers save $180.01 on each suit. I’ve added a category discount to select suits in hopes of getting a 2 suits for $100.00 deal. It works fine when I take of the discounted price but when I leave the discounted price of 69.99 in, the total for 2 ends up being -$260.02. Its extremely important that the original price & the discounted price been seen, but is it possible for the 2 for 100 category discount to work at the same time.
August 26, 2010 at 8:58 pm
Hi. The trick to getting this right, is realising that the discount (£180.01) is fixed, and doesn’t change. So – to get 2 suits for $100 your instinct is probably to put the quantity based prices in as $50 – however the system will apply a $180.01 discount to that, taking each suit down to -$130.01 – hence the total of $260.02.
So – you need to set your prices $180.01 higher than you’d expect, so your quantity pricing for 2 suits should be $50 + $180.01 = $230.01
If you set it up as that it should all work for you …
August 29, 2010 at 5:11 am
That makes sense, thanks a lot Lee great advice!! I’ve just upgraded from 1.1.4 to 1.1.5 and I tried to use the patch with this version, but it doesn’t seem to be working. Is there any way for the category discount to work with version 1.1.5?
August 29, 2010 at 7:43 am
Hi – the code’s not too dissimilar – so there’s no reason the same principle couldn’t be applied. However I haven’t moved most of my sites so I haven’t really had chance to re-work it. If you need it urgently I’d be happy to give you a quote to port it over.
August 29, 2010 at 6:08 am
Hi Lee,
Have you upgraded this patch to work across multiple categories?
At my site, http://www.ikhlasgreetings.com , I have bulk pricing for different categories. I’d like to be able to use your patch, but it only allows for one category.
Thanks for your help.
David
August 29, 2010 at 7:47 am
Hi David,
It depends what you mean really. Do you mean that bulk discounts apply within multiple categories, e.g. 2 products from category A are reduced, and 2 products from category B are also reduced, but 1 product from category A together with 1 product from category B are at the standard prices.
OR, do you mean that buying 1 product from category A and 1 product from category B are charged at the reduced prices.
August 29, 2010 at 9:47 pm
I was wondering the same thing, the first scenario in my case though. And a quote would be fine, I do need it to work urgently.
August 30, 2010 at 9:14 pm
Actually, I just needed all within one category so I’m fine. I thought about doing it all within all subcategories of a main category, but found later that I don’t need to.
I also used a hack to add images to my mini cart and cart.
It’s kinda fun, but very detailed.
Do you know where I can find resources on how to format the cart header?
Thanks for your prompt reply.
August 30, 2010 at 9:18 pm
No idea – sorry …
September 18, 2010 at 8:06 pm
Thanks for the post! Already working great for me! Quick question, I’m working on a site that sells courses. So if you purchase 1 course it is $350, if you purchase 2 courses it is $500 and if you purchase 3 or more courses (there are only 5 total) your total bill is $600 – notice the important shift there since courses 4 and 5 are technically free under this plan.
Is there any way to accomplish this here?
Thanks!
September 19, 2010 at 12:54 pm
You just set the item price break figures to “total amount chargeable to customer” divided by “number of items”. So:
0-1 = $350
2-2 = $250
3-3 = $200
4-4 = $150
5-5 = $120
Does that do what you’re after?
November 5, 2010 at 9:47 pm
Hello,
Is this patch compatible with vm 1.1.5?
I want to add it to my website but i’m not sure it will work.
November 6, 2010 at 10:42 pm
As it stands not directly no. You’d have to adapt it to fit – although I believe the approach is still sound.
February 21, 2011 at 9:04 am
Thanks for the code. It works as a gem 🙂
February 21, 2011 at 12:30 pm
I ran in to a problem in this code an i have fixed that.
What happened is I added many child products to the cart from the same categorey same prodcut. But the query returned zero. Eventually my cart contains only child products of the same cat and I could not get the discount. So I worte the condition to get the parent id and called in the query.
$product_parent_id = $this->get_field($product_id, “product_parent_id”);
// LW PATCH – Get the category of this product – used later to drive category-based multiple purchases
$pid=0;
if($product_parent_id>1 ){
$pid=$product_parent_id;
}
else{$pid=$product_id;}
$sesq = “SELECT category_id FROM #__{vm}_product_category_xref WHERE product_id=’$pid’”;
After doing so i got the discounts for the child product too.
March 22, 2011 at 5:11 pm
Ramachandran, I have almost the same problem:
I could have some Children from different Parents. All belonging to the same Category and eligible for the quantity discount.
With your code, I get the first part of the Lee Willis hack working and I get : $ses_cat_discount = TRUE
for those children products
BUT the second part of the hack doesn’t work for me because :
$sesprodchildcat is NULL
So the if ($sesprodchildcat == $sesprodcat) condition is not fulfilled in case of children products…
Did you complete your code ?
March 31, 2011 at 2:48 pm
OK I made it. So I’ve added another query if $sesprodchildcat is NULL
Here is my version of the second part of the Lee code
(first part should be with Ramachandran fix)
// LW PATCH – If any product in the cart is in the same category then treat as volume purchase
if ($ses_cat_discount) {
$sesq = “SELECT category_id FROM #__{vm}_product_category_xref WHERE product_id='”.$cart[$i][“product_id”].”‘”;
$db->setQuery($sesq); $db->query();
$db->next_record();
$sesprodchildcat = $db->f(“category_id”);
// PS – A child product would not be detected by previous query. So we look for the parent category
if ($sesprodchildcat == NULL){
$sesq = “SELECT category_id FROM #__{vm}_product_category_xref C INNER JOIN #__{vm}_product P ON C.product_id=P.product_parent_id WHERE P.product_id='”.$cart[$i][“product_id”].”‘”;
$db->setQuery($sesq); $db->query();
$db->next_record();
$sesprodchildcat = $db->f(“category_id”);
}
if ($sesprodchildcat == $sesprodcat) {
$quantity += $cart[$i][“quantity”];
}
}
else {
if ($cart[$i][“product_id”] == $product_id) {
if ($parent) {
$parent_id = $cart[$i][“parent_id”];
}
else {
$quantity += $cart[$i][“quantity”];
}
}
}
} // End LW PATCH
August 9, 2011 at 1:42 am
guys, im not getting the discounted price on the products form same category. Using virtuemart 1.1.9
Thanks.
September 1, 2011 at 7:02 pm
Hi… this is perfect but i have a problem. I change de category ID but it makes the discount for products from different category.
Ex:
Cat 1 – >6 products 10€ discount
Cat 2 – no discount
I buy 5 from cat 1 and 1 from cat 2 and it makes de discount. It can’t be.
Can you tell me why?
THANX
September 1, 2011 at 7:46 pm
No idea I’m afraid. Sounds like something’s gone wrong somewhere.
September 1, 2011 at 7:56 pm
It can be that i make something wrong? or this code doesn’t do that? I don’t know if the mistake is mine or its because this code downs’t do that type of discount?
tnx anywhere
October 4, 2011 at 1:32 am
Is there a stable patch for VM 1.1.6? I’m having trouble getting it to work.
October 4, 2011 at 8:04 pm
I don’t I’m afraid. I’m no longer using Virtuemart I’m afraid.
October 4, 2011 at 8:09 pm
I got it working. Thanks for the response though!
October 26, 2011 at 12:32 pm
Hi Mike, Lee
Have either of you managed to patch the 1.1.8 version of ps_product.php – I’ve been hacking around but everything I’ve done causes the site to fall over.
Could you post your code for the 1.1.6 version so I can take a look?
Your help would be greatly appreciated
Darcy
December 23, 2011 at 8:03 am
Hi, Thank you for the information.
I tried it and it is not working for me.
I have a customized virtuemart and somehow it is not working..any suggestions?
thxs
December 23, 2011 at 8:17 am
I am using 1.1.5 though..can anybody help me..
anybody has a copy of PS_product.php that works on 1.1.5?
Thxs
December 23, 2011 at 11:47 am
Hi
I finally worked out a solution using a combination of AWO Coupons Pro (http://dev.awofadeju.com/product/awocoupon-pro) and Bonus Products for VM (www.daycounts.com/en/shop/joomla-15/bonus-items-for-virtuemart).
Admittedly, these cost Euro44 and $45, but rather than hours and hours of faffing about changing code on a client website, I got the job done and went to the pub instead!
Both websites offer great tech support – took a while to get my system running – http://www.safetyposters365.com – which offers 10% discounts on quantities of 5-9 posters or 25% on 10 or more posters within the same SIZE category (eg ‘A3 posters’ or ‘A2 posters’), which proved to be a bit tricky to configure.
The customer is happy
February 20, 2012 at 2:18 am
Perfect solution for category discount in VM. I was searching for two days now a workaround … Thank you!
April 12, 2012 at 9:06 pm
Is there any way to limit purchase by category?
EX:
Same category
Product A
Product B
6 is the limite
Buy 3 Prod A and Buy 3 Prod B
or
Buy 4 Prod A and Buy 2 Prod B
If we add to cart more than 6 from same category (4 Prod A and 3 Prod B) it shows an error message or it says it can’t be…
Tnx a lot people…
June 7, 2012 at 6:20 am
Can you tell me weather this work on virtumart 2.06?
i am not able to find that product file….. Help plz
February 6, 2014 at 5:45 pm
Hello,
the same question as previous….Does it work with Virtuemart 2.0.26 + Joomla 2.5.17?
Thanks
February 20, 2014 at 9:33 pm
No idea I’m afraid – I don’t really work with Joomla! any more so can’t really comment.