Lee Willis

Virtuemart Category Discounts

| 61 Comments

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:

  1. 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
  2. Further to the above – if you upgrade Virtuemart this will need re-applying, possibly with some tweaks and tickles
  3. 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”:

listprices

Then you’ll need to specify your pricing:

productprices

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.

Line 1608:
// Get the product_parent_id for this product/item
$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:

Line 1622:
// 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”);
  if ($sesprodchildcat == $sesprodcat) {
    $quantity += $cart[$i]["quantity"];
  }
} else {

  if ($cart[$i]["product_id"] == $product_id) {
    if ($parent) {
      $parent_id = $cart[$i]["parent_id"];
Line 1631:
       }
     }
   }
}
// END LW PATCH

And that’s that. Give it a try, let me know what you think!

61 Comments

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

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

    • Hi

      It seems great.
      I have a question.

      The dicounted prices are appear the same in the cart and the checkout proccess?

      Sakis

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

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

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

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

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

  7. What are the correct line numbers for a VM 1.0.x file? And do I have to replace or add the patch?

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

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

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

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

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

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

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

  15. Great – glad you got it sorted :)

  16. cannot believe it..it works so well on my site..thanks for this mod….you are Ace~!!!…

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

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

  19. Thx it works fine now.
    You are great man.
    Best regards wimm

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

    • 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!

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

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

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

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

    • 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 …

      • 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?

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

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

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

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

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

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

    • 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?

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

  27. Thanks for the code. It works as a gem :)

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

    • 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 ?

      • 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

  29. guys, im not getting the discounted price on the products form same category. Using virtuemart 1.1.9

    Thanks.

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

    • No idea I’m afraid. Sounds like something’s gone wrong somewhere.

      • 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

  31. Is there a stable patch for VM 1.1.6? I’m having trouble getting it to work.

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

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

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

  35. Perfect solution for category discount in VM. I was searching for two days now a workaround … Thank you!

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

  37. Can you tell me weather this work on virtumart 2.06?
    i am not able to find that product file….. Help plz

  38. Hello,

    the same question as previous….Does it work with Virtuemart 2.0.26 + Joomla 2.5.17?
    Thanks

Leave a Reply

Required fields are marked *.