Zen Cart custom software development, Zen Cart modules, Zen Cart Expert eCommerce with Zen Cart!

osCommerce Quantity Discounts

Quantity Discounts for osCommerce

The Quantity Discounts contribution permits you to offer discounts that are structured many different ways:
  • By total number of items purchased
  • By number of items purchased, counting per item
  • By number of items purchased, counting per category
  • Counting by items or by number of dollars spent, in any of the ways above
  • Specifying the discount as a percentage, a flat dollar figure or dollars per item

Important Version Information: This product is for osCommerce Phoenix only. If you are looking for osCommerce 2.2, 2.3 or 3.0, please see the link in the Relevance section below.

Donate: This is free software. Show your appreciation by supporting my efforts. Donate

Background: See the osCommerce Matrix-o-discounts

Example Discounts: I have provided several examples of osCommerce Quantity Discount discounts.

Relevance: osCommerce™ Phoenix only.

If you are looking for osCommerce 2.2, 2.3 or 3.0, please see the Legacy Quantity Discounts page.

Cost: Free, but donation appreciated

Location: osCommerce Community Add-Ons page, under Order Total Modules

Download: Quantity Discounts for osCommerce Phoenix

Installation: click here

Payment Gateway Integration Note that some payment modules, including PayPal, do not natively work with Order Total discounts (mine or anyone else's). See Payment Modules below.

Installed Cost: $90.00 Buy Professional Installation by That Software Guy. Note that this does not include payment gateway integration - see above.

Current Version: 1.3 Occasionally, new features are documented prior to being publicly available; please check the version history to ensure the feature you want is available in your version.

osCommerce Support Thread: Quantity Discounts for osCommerce Support Thread

FAQ: click here

Marketing Text: click here

Add-Ons:
Like Quantity Discounts? Take a look at Discount Preview.


Overview:

This mod permits a shop to define up to five admin specified quantity discount levels on a per-product, per category or total number of items purchased basis. The discounts may be expressed in percentage or currency units. Various user exits are provided to make it easy to extend the mod to meet shop requirements.

The limit of five levels can be exceeded using a simple interface in the code to define additional levels.

Detailed Description:

  1. Configuration
    osCommerce Configuration of Quantity Discounts
    Quantity Discounts is configured through the Admin interface (Admin > Modules > Order Total > Quantity Discount).

    The image to the left shows a shop which gives 10% off items bought in quantities of 5 or more.

    Discount Level 1..5 and Discount Amount 1..5 are fields which contain a set of five discount levels (min. quantity required to get to this level, discount amount) may be specified. The Discount Basis radio buttons determine if quantity required to get to a discount level is counted as bulk purchases of a single item (Total By Item), by purchases within a category (Total By Category) or by the total number of items in the cart (Total Items in Cart). The Discount Units radio buttons allow the units of discount levels to be specified as percent off, currency units off (i.e. dollars off the total), or currency units per item off (i.e. dollars off per item).

    Categories

    Note that the category used is the parent category. This means that when using Total By Category for products in subcategories, the "category" will be the immediate parent category.

    Men's Clothing 
         |
         ---->  Dress Shirts
         ... 
         ---->  Shirts
                |
                -------> shirt A     
                         shirt B
                         shirt C 
    
    In this example, the parent category of "shirt A" is "Shirts," not "Men's Clothing."

    If linked products are used, the parent category may still be explicitly specified using the is_linked_category() function. Simply add category ids which represent linked categories to this function, and that category ID will not be used if another non-linked category id is also available for that product. For instance, using the example above, if shirt A is product 20, and is under both Dress Shirts (category 8) and Shirts (category 12), the osCommerce products_to_categories table will have the following entries:
    products_idcategories_id
    208
    2012

    If 8 is actually a linked category, then add 8 to the list in is_linked_category, and product 20 will be counted as being in category 12.

    Although a product can be in multiple osCommerce categories, it can only be in one category for Quantity Discounts discounting purposes.

    Specifying "include tax = true" will gross the discount up by the amount of tax that would have been paid on the goods prediscount. This is appropriate for shops under a price inclusive sales tax system, such as the UK VAT. Specifying Re-calculate tax = Standard will recalculate the entire tax based on the original subtotal minus the quantity discount. Even more information about tax is provided at the bottom of this file.

    Counting Method allows discounting by dollars spent rather than units purchased as is normally done. The default value for this field is "items."

  2. User Exits
    Some shops will want to further customize their discounting policy. Additional user exits are provided to permit the following customizations:
    • Include or exclude certain categories from discounting.
    • Include or exclude certain products from discounting.
    • Modify the discounts for particular categories if discounting by category.
    • Modify the discounts for particular items if discounting by item.


    Specifically:

    • The function exclude_category() shows you how to add categories to a list of categories to not be discounted. This may be used with any Discount Basis.
    • The function exclude_product() shows you how to add products to a list of products to not be discounted. This may be used with any Discount Basis.
    • If you are using the Discount Basis "Total By Category," you may customize the discount for a specific category using the function apply_special_category_discount().
    • If you are using the Discount Basis "Total By Item," you may customize the discount for a specific item using the function apply_special_item_discount().

    Each of these functions contains a simple example of how to use it using item and category numbers like 99999, 99998, etc. See the FAQ below for more examples

    The user exits are within the Quantity Discounts contribution, in catalog/includes/modules/order_total/ot_quantity_discount.php

    Quantity Discounts discounts off the *gross* price of the goods. This can result in a larger than expected discount if other discounts are added (such as coupons or group discounts), so bear this in mind when creating your discount strategy.

  3. User Interface
    Quantity Discounts is an "Order Total" module, which means that by default, the discount is not shown until the Order Confirmation page. However, using the Discount Preview module will allow you to show the discount in the shopping cart.

    Depending on your configuration, the Order Confirmation page will look something like this:

    osCommerce Order Confirmation Page displaying Quantity Discount

    osCommerce Order Confirmation Page with Quantity Discounts

  4. Breaking the five level barrier
    Suppose your discounting strategy is as follows:

    Level Amount
    55% off
    1010% off
    1515% off
    2020% off
    2525% off
    10050% off


    How do you create that last discount?

    In the file catalog/includes/modules/order_total/ot_quantity_discount.php, there is a function called setup(). From there, you may add a call to a function to add additional discounting levels and discounts in the following manner:
        function  setup() {
           // Add extra levels and discounts here in this manner: 
           // "Buy more than 100 items, get 50 (% or $, as per admin) off" 
           $this->add_extra_level_discount(100, 50); 
        }
    

    These extra {level, discount} pairs will show up in your marketing text just like those you added in the admin panel.

Installation Instructions

  1. Back up everything! Try this in a test environment prior to installing it on a live shop.
  2. If you already have the Quantity Discounts module installed, please deinstall your old copy by going to Admin > Modules > Order Total, selecting "Quantity Discount" and pressing the "Remove" button. Make a note of your settings so you can apply them to the new version.
  3. Copy the contents of the unzipped folder to the root directory of your shop.
  4. Go to Admin > Modules > Order Total. Click on the "Install Module" button on the upper right. Select the row that says "Quantity Discount." Press the "Install Module" button on the right hand side of the screen.
  5. Ensure that the sort order that you have selected for Quantity Discounts is not already used by another module. If necessary, change the sort orders of other modules to ensure that Quantity Discounts displays where you want it, with a unique sort order. Also, if you are using Re-calculate Tax = Standard, be sure the Tax sort order is larger than the Quantity Discounts sort order.
  6. Decide on the parameters you wish to use. The easiest way to do this is to open a shopping cart in another window, and just try different discounting models. The discounts are shown on the Order Confirmation page under "Quantity Discount," which is itself a link that explains the calculation.
  7. Customization: If you have a single discounting policy for your shop, you're all set. If you wish to tailor the policy, you will have to add code to the user exits as described above.
  8. If you wish, follow the guidelines in marketing to advertise your discounts.
  9. Test a transaction and ensure the discount goes all the way through. For PayPal and some other payment methods, you may need to make additional changes, as outlined below in Payment Modules in osCommerce.
  10. Donate! Show your appreciation by supporting my efforts.
If you are having trouble installing this module, you should also refer to my Guide to Mod Installation on osCommerce. I'm also happy to install most of my mods for a fee.

Optional Installation Instructions:

  1. I highly recommend Discount Preview with all my discounting modules. Without Discount Preview, your customers cannot see the price reductions they are entitled to until the second page of checkout (checkout confirmation). Obviously this is a disadvantage, particularly for new customers who need to go through the additional step of creating an account before they can see this information. Sometimes seeing is believing, so here's a video showing Discount Preview in action.



Notes on Taxes for osCommerce Phoenix


If you don't use embedded taxes, and don't have a mix of taxable and tax-free products, and don't have a different rate of tax for shipping, please skip this section.
However, if you any of the above apply to you, please read my Notes on Taxes.



Payment Modules in osCommerce

Some contributed payment modules in osCommerce Phoenix are not naturally aware of the existence of discounts (mine or any other contribution or extension). For this reason, you must ensure any payment module you use takes discounts into account.

For some carts, PayPal Website Payments Standard has been changed to accommodate these osC discounts, but in a bizarre way. If you have
Subtotal: $30
Postage: $7.69
Discount: -$12.00
Tax (7%): $1.26
-----------------------------
Total:   $26.95
PayPal turns this into
Subtotal: $17.16
Tax (7%): $2.10
Shipping: $7.69
-----------------------------
Total:   $26.95
The final number is correct (and your actual order total figures as shown in osC admin will be correct per the checkout confirmation page).

I have seen this work on some carts and not on others depending on mods and settings; please test your own cart carefully.

PayPal Express in osCommerce Phoenix still has problems with osCommerce discounts. Here is one way to fix it:

In ./ext/modules/payment/paypal/express.php, right above the block on line 544-545 that sets the item and tax amounts:
      $params['ITEMAMT'] = $items_total;
      $params['TAXAMT'] = $tax_total;
insert the discount computation. Here's an example for Quantity Discounts. Pull in the module and its language file. Then compute the discount and add it in as a line item.
      // Add OT
      require('includes/modules/order_total/ot_quantity_discount.php');
      include(DIR_WS_LANGUAGES . $language . '/modules/order_total/ot_quantity_discount.php');
      $discount = new ot_quantity_discount();
      if ($discount->check()) {
         $qd = $discount->calculate_deductions();
         if ($qd['total'] > 0) {
            $amt = $qd['total'] * -1;
            $params['L_NAME' . $line_item_no] = 'Discount';
            $params['L_AMT' . $line_item_no] = $amt;
            $params['L_NUMBER' . $line_item_no] = 0;
            $params['L_QTY' . $line_item_no] = 1;
    
            $product_tax = 0; 
            $params['L_TAXAMT' . $line_item_no] = $paypal_express->format_raw($product_tax);
            $items_total += $paypal_express->format_raw($amt);
            $line_item_no++;
         }
      }

For other modules, here are the filenames. The object to be created is the filename without the .php suffix.
ModuleFile Name
Quantity Discountsot_quantity_discount.php
Better Togetherot_better_together.php
Discount Chooserot_freegift_chooser.php




Quantity Discounts Marketing Text

Donate! Show your appreciation by supporting my efforts.

These marketing text examples are from osCommerce Phoenix.

To install the marketing text on the product info page, do the following: In Admin > Modules > Content, press the Install Module button. Then select Quantity Discounts Marketing Text, and press Install Module.

There are four styles of Marketing Text you can choose from. They look like this:

Choosing style 1 will create output that will look something like this:

Quantity Discounts Marketing Style 1 Output

Store Quantity Discount Policy


Buy 2 or more of any item, get 5% off
Buy 5 or more of any item, get 10% off
Buy 10 or more of any item, get 15% off
Buy 50 or more of any item, get 50% off


At the current time, style 1 and style 2 are identical; this may change in the future.

What if we want to do something that shows the levels as a range? Style 3 will show the start and end of a discount range.

Quantity Discounts Marketing Style 3 Output

QuantityDiscount
10 - 1915%
20 - 9920%
100 + 50%


Some of the examples above do not work well when using Counting by Currency. Style 4 is built for shops that use this configuration.

Quantity Discounts Marketing Style 4 Output

Amount SpentDiscount
$100.00 - $399.99$10.00
$400.00 + $20.00



Marketing Text and User Exits

If you use the user exits, you must adjust how the marketing text is called.

If you have used the user exits exclude_category() or exclude_product() or exclude_category, you should also customize get_marketing_info() to be aware of your changes. For example, if you are using exclude_category, then change get_marketing_info() from:

if ($discount->check() > 0) { 
   $body = $discount->get_marketing_body((int)$_GET['products_id'], MODULE_CONTENT_PI_SWGUY_QD_MARKETING_CONTENT_STYLE);
   $header = $discount->get_marketing_header(MODULE_CONTENT_PI_SWGUY_QD_MARKETING_CONTENT_STYLE);
}
to:
if ($discount->check() > 0) { 
   $cat = $discount->get_master_category((int)$_GET['products_id']);
   if (!$discount->exclude_category($cat)) { 
      $body = $discount->get_marketing_body((int)$_GET['products_id'], MODULE_CONTENT_PI_SWGUY_QD_MARKETING_CONTENT_STYLE);
      $header = $discount->get_marketing_header(MODULE_CONTENT_PI_SWGUY_QD_MARKETING_CONTENT_STYLE);
   }
}
Similar changes would be required if you have used the user exits apply_special_category_discount() or apply_special_item_discount(). In such cases, rather than calling the user exits again and attempting to deduce whether a change has been made to the discount amount, it would be easier to simply express the logic in human language. For example, change get_marketing_info() from:

return array('header' => $header, 'body' => $body); 
to:
return array('header' => $header, 'body' => $body, 'footer'=>TEXT_SPECIAL_QUANTITY_DISCOUNT_DETAILS);
Then display the footer in includes/modules/content/product_info/templates/tpl_cm_pi_swguy_qd_marketing.php.


Major Versions

  • 1.3 05/19/2020 - Updates for Marketing Text
  • 1.2 08/17/2019 - Support for Marketing Text
  • 1.1 08/03/2019 - First release for Phoenix

FAQ

Q: How do I install this software?
A: Installation instructions for osCommerce 2.2/2.3 are here. If you've never installed an osCommerce mod before, please read my Guide to Mod Installation on osCommerce.

Q: I can't seem to get Quantity Discounts for osCommerce 2.2/2.3 to work. What am I doing wrong?
A: Please check the following things:
  • Ensure that the sort order that you have selected for Quantity Discounts is not already used by another module. If necessary, change the sort orders of other modules to ensure that Quantity Discounts displays where you want it, with a unique sort order. Also, if you are using Re-calculate Tax = Standard, be sure the Tax sort order is larger than the Quantity Discounts sort order.
  • Go to Admin > Modules > Order Total. Do you see Quantity Discounts? If not, then you haven't installed it. Follow the README.
  • Click on Quantity Discounts. The Level and Amount fields are all numeric. Do not use dollar or percent signs in these fields.
  • If you've used any of the exits, make a backup of your ot_quantity_discount.php file and re-install from scratch; then apply your changes one at a time to see where it breaks. The most common error I have seen in exits is using the assignment operator ("=") where an equivalence test ("==") was intended.
  • Remember that the "levels" (Discount Level 1, Discount Level 2, etc.) are numbers of items, not number of dollars spent (unless you configure counting method to be by currency).
  • If you are using 2.2-MS2, follow these instructions.
  • If you are using the CCGV (Coupon Code/Gift Voucher) Contribution, then follow these instructions.


Q: I am also using the CCGV (Coupon Code/Gift Voucher) Contribution. On the Checkout Payment Page, I get the error:
"Fatal error: Call to undefined method ot_quantity_discount::use_credit_amount() in 
  ......../httpdocs/includes/classes/order_total.php on line nnn"
A: Make the following fix to the file includes/classes/order_total.php
Change
          if ($GLOBALS[$class]->enabled && $GLOBALS[$class]->credit_class) 
to
          if ($GLOBALS[$class]->enabled && $GLOBALS[$class]->credit_class 
            && (method_exists($GLOBALS[$class], 'use_credit_amount'))) 
The location of this line will depend on how many changes you've made to the file order_total.php.

Q: How do I tell if my stock is organized into subcategories?
A: In the Admin page, go to Catalog > Categories/Products, and click on the category you're not sure about. If the entries that appear on the next page have file folders to the left of their names, then these are subcategories. If the products are directly below these folders, then these folders are the parent folder numbers you will use for category inclusions, exclusions and special discounts. If what is below these subcategories is more subcategories, continue drilling down until you get to products, and then go back one level. This is the "parent" category you will need to use.

Q: How do I get the category id out of this?
A: In Admin > Catalog, single click on the category you're interested in. In the address bar of your browser, you will see something like
categories.php?cPath=3&cID=11
If you were to double click on it, you'd see something like
categories.php?cPath=3_11
The category you want to use is "11".

In the same way, in the catalog you can hover over this category in the categories sidebox and see
catalog/index.php?cPath=3_11
This re-confirms that the category is "11."

Q: Why does the Quantity Discounts contribution not provide more discounting levels in the Admin Screen?
A: Having a fixed number of discounts in the Admin screen is a design consequence of the Order Total implementation. I thought five was a reasonable compromise. Remember that a limitless number of discount levels are available by calling the add_extra_level_discount() function in setup().

Q: Why are there user exits for customization? Why didn't you put this in the Admin panel?
A: There are an endless number of combinations and permutations of how people want discounting to work. Rather than design a complicated user interface to present all these options, I have provided a framework that anyone with at least a beginner's knowledge of PHP should be able to extend.

Q: I would like my discounts to show up in the shopping cart. Why don't they?
A: The way the Order Total modules work is that they show up at checkout time. However, if you require the discounts to show up in the shopping cart, you may wish to consider purchasing the Discount Preview module for $30.

Q: I only want discounts applied to items in category 11. How do I do this?
A: Update the function exclude_category() in catalog/includes/modules/order_total/ot_quantity_discount.php as follows:
    function exclude_category($category) {
        switch($category) {
           case 11:
                return false;
        }
        return true;
    }


Q: I just did an exclude_category and it doesn't work!
A: The categories in osCommerce for exclude_category() are parent categories only. If you need grandparents or ancestors further back, please look at Table Discounts.

Q: I don't want discounts applied to items in category 7. How do I do this?
A: Update the function exclude_category() in catalog/includes/modules/order_total/ot_quantity_discount.php as follows:
    function exclude_category($category) {
        switch($category) {
           case 7:
                return true;
        }
        return false;
    }


Q: I have a product in categories 7 and 8. Category 7 has a lot of linked items in it; the real parent category for this product is 8. How do I discount by category and force product to be in category 8?
A: Update the function is_linked_category() in catalog/includes/modules/order_total/ot_quantity_discount.php as follows:
    function is_linked_category($category) {
        switch($category) {
           case 7:
                return true;
        }
        return false;
    }
This will force the category to be 8 for discounting purposes.

Q: I want to double my regular discounts for category 9 if the customer buys 10 or more, and triple them if he buys 100 or more. How do I do this?
A: Update the function apply_special_category_discount() in catalog/includes/modules/order_total/ot_quantity_discount.php as follows:
    function apply_special_category_discount($category, $count, &$disc_amount) {
        switch($category) {
           case 9:
                if ($count > 100) {
                   $disc_amount = $disc_amount * 3;
                } else if ($count > 10) {
                   $disc_amount = $disc_amount * 2;
                }
                break; 
        }
    }
Note that this function is only used if Discount Basis is "Total By Category."

Q: I want to double my regular discounts for item 9 if the customer buys 10 or more, and triple them if he buys 100 or more. How do I do this?
A: Update the function apply_special_item_discount() in catalog/includes/modules/order_total/ot_quantity_discount.php as follows:
    function apply_special_item_discount($id, $count, &$disc_amount) {
        switch($id) {
           case 9:
                if ($count > 100) {
                   $disc_amount = $disc_amount * 3;
                } else if ($count > 10) {
                   $disc_amount = $disc_amount * 2;
                }
                break; 
        }
    }
Note that this function is only used if Discount Basis is "Total By Item."

Q: I only want discounts applied to product numbers 17 and 19. How do I do this?
A: Update the function exclude_product() in catalog/includes/modules/order_total/ot_quantity_discount.php as follows:
    function exclude_product($prid) {
        $id = (int)$prid;
        switch($id) {
           case 17:
           case 19:
                return false;
        }
        return true;
    }


Q: I want discounts applied to all products except products 12 and 13. How do I do this?
A: Update the function exclude_product() in catalog/includes/modules/order_total/ot_quantity_discount.php as follows:
    function exclude_product($prid) {
        $id = (int)$prid;
        switch($id) {
           case 12:
           case 13:
                return true;
        }
        return false;
    }


Q: How can I present my discounting schedule on the product page?
A: Customize the file catalog/product_info.php Look at the examples in marketing. Add the one that fits your needs best to catalog/product_info.php; start by putting it after the product description (although the placement is a matter of personal taste).

Q: Why are there three ways of getting the discounting information - get_html_policy(), get_discount_info(), and get_discount_parms()?
A: I was too nice to deprecate the first two. Everything can actually be derived from get_discount_parms(); the other functions exist simply as a convenience.