Zen Cart custom software development, Zen Cart modules, Zen Cart Expert eCommerce Websites 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

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™ 2.2, 2.3 or 3.0

Cost: Free, but donation appreciated

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

osCommerce 2.2 Download: Quantity Discounts for osCommerce 2.2

osCommerce 2.3 Download: Quantity Discounts for osCommerce 2.3

osCommerce 3.0 Download: Quantity Discounts for osCommerce 3.0

Installation for osCommerce 2.2, 2.3: click here

Installation for osCommerce 3.0: 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).
Here are some suggestions on how to fix this:
osCommerce 2.2     osCommerce 2.3 These are only suggestions and ideas; actually doing this integration is your responsibility.

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

osCommerce 2.2, 2.3 Support Thread: Quantity Discounts for osCommerce 2.2 Support Thread

osCommerce 3.0 Support Thread: Quantity Discounts for osCommerce 3.0 Support Thread

FAQ: click here

Marketing Text for osCommerce 2.2, 2.3: click here

Marketing Text for osCommerce 3.0: click here

See it Live: Go to my demo shop and add ten of any item to your cart, except for Gadgets, which are handled by Table Discounts. You will immediately see the discount because of Discount Preview, but without it, you would see the discount on the Order Confirmation page. Also note the marketing text for Quantity Discounts on the Product Info page. The format shown in example 1 is used.


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 Totals->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 in osCommerce 2.2 and 2.3, 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.

    In osCommerce 3.0, discounts are shown on the Shopping Cart page, so Discount Preview is not needed.
    Depending on your configuration, the Order Confirmation page will look something like this:

    osCommerce 2.2 Order Confirmation Page displaying Quantity Discount

    osCommerce 2.2 Order Confirmation Page with Quantity Discounts

    osCommerce 3.0 Shopping Cart Page displaying Quantity Discount

    osCommerce Shopping Cart Page showing Quantity Discount

    In osCommerce 2.2 and 2.3, "Quantity Discount" is a link, which will show the breakdown of the discount, depending on the Discount Basis you selected on the Admin screen. For instance, if the Discount Basis is "Total Items in Cart," with a Discount Level 1 of "4" and a Discount Amount 1 of "10," you would see this dialog if you clicked the "Quantity Discount" link:

    osCommerce Quantity Discount Explanation

    In osCommerce 3.0, the link is provided on the shopping cart page below the order summary.

    With a Discount Basis of "Total By Category," assuming these items were all in the same category (and the category was "Fish"), you would see this dialog if you clicked the "Quantity Discount" link:

    osCommerce Quantity Discount Explanation

    Next, we'll use a Discount Basis of "Total By Item." The original items were all in the same category, and we'll add four more of another item from the same category. The two items have model numbers "BLUEFISH-127" and "REDFISH-492". You would now see this dialog if you clicked the "Quantity Discount" link:

    osCommerce Quantity Discount Explanation

  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); 
        }
    
    

    The behavior of these {level, discount} pairs is identical to those you added in the admin panel. Specifically,

Installation Instructions for osCommerce 2.2:

These installation instructions are for osCommerce 2.2 and 2.3. Click here for osCommerce 3.0 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. In osCommerce 2.2: Go to Admin->Modules->Order Total. You will see 'Quantity Discount' listed along with all the other modules available. Click on 'Quantity Discount' to highlight the module and click on 'Install'
  5. In osCommerce 2.3: 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.
  6. 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.
  7. 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.
  8. 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.
  9. If you wish, follow the guidelines in marketing to advertise your discounts.
  10. 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 2.2 and Payment Modules in osCommerce 2.3.
  11. 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 2.3


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 2.2

Note: This only applies to osCommerce 2.2. For osCommerce 2.3, please click here.

Some contributed payment modules in osCommerce 2.2 (such as PayPal) 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. Here are some guidelines:
  • If the payment module passes in a subtotal, shipping and tax, modify the subtotal (as shown below) to include the discount.
  • If the payment module passes in a shipping, tax, and total, modify the total (as shown below) to include the discount.
  • In the case of PayPal, turn OFF the sending of line item details, which will confuse PayPal since the total is not the sum of the line items. This can be done through the admin panel by setting Transaction Type to "Aggregate."
The modification involves subtracting the discount amount. If the order total is computed as follows (example taken from paypal_ipn.php):
      foreach ($order_totals as $ot) {
        $order_total[$ot['code']] = $ot['value'];
      }
then the discount is available in the $order_total variable as follows:
ModuleVariable Name
Quantity Discounts$order_total['ot_quantity_discount']
Better Together$order_total['ot_better_together']
Big Chooser$order_total['ot_big_chooser']
Big Spender$order_total['ot_bigspender_discount']
Table Discounts$order_total['ot_table_discounts']
Free Gift Chooserot_freegift_chooser.php


So for instance, subtracting the Quantity Discounts discount from a variable called $subtotal would be done as follows:
$subtotal = $subtotal - $order_total['ot_quantity_discounts'];
This would be done in process_button() for example.

Another more sophisticated approach would be to execute the order totals and use ot_total, which is the final total after discounts.
 

// BEGIN: Change to use order_totals to get values 

      $order_id = substr($cart_PayPal_IPN_ID, strpos($cart_PayPal_IPN_ID, '-')+1);
      $ord_totals = array();
      $ord_totals_query = tep_db_query("select class, value from " . TABLE_ORDERS_TOTAL . 
          " where orders_id = '" . (int)$order_id . "' order by sort_order");
      while ($totals = tep_db_fetch_array($ord_totals_query)) {
        $ord_totals[$totals['class']] = $totals['value'];
      }

// END

. . . 

      $parameters['business'] = MODULE_PAYMENT_PAYPAL_IPN_ID;
// BEGIN : use order_totals['ot_total'] for order total to include all discounts.  
      if ($ord_totals['ot_total'] > 0) {
        $parameters['amount'] = number_format($ord_totals['ot_total'], $currencies->get_decimal_places($currency));
      } else {
        $parameters['amount'] = number_format($order->info['total'] - $order->info['shipping_cost'] 
           - $order->info['tax'], $currencies->get_decimal_places($currency));
      }
// END 


Payment Modules in osCommerce 2.3

Note: This only applies to osCommerce 2.3. For osCommerce 2.2, please click here.

Some contributed payment modules in osCommerce 2.3 (2.3.1-2.3.3.x) 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
This is in osC 2.3.3, with no changes made to PayPal. But 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 2.3 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
Big Chooserot_big_chooser.php
Big Spenderot_bigspender_discount.php
Table Discountsot_table_discounts.php
Free Gift Chooserot_freegift_chooser.php



Installation Instructions for osCommerce 3.0:

These installation instructions are for osCommerce 3.0. Click here for osCommerce 2.2 and 2.3 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 Modules, selecting "Quantity Discounts" and pressing the "Remove" button. Make a note of your settings so you can apply them to the new version.
    If you have customized the Quantity Discounts code (includes/modules/order_total/quantity_discounts.php) to include or exclude particular categories or modify discount levels, back up this file.
  3. Copy the contents of the unzipped folder to the root directory of your shop. There are two new files in this mod.
  4. Login to admin and in Modules->Order Total Modules you will see 'Quantity Discounts' listed along with all the other modules available.
  5. Click on 'Quantity Discounts' to highlight the module and click on 'Install'
  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."
  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. Displaying Product and Category IDs: If you have exceptions for products/categories, follow these steps so you can easily determine product and category ids:
    a) Edit the file admin/includes/applications/categories/pages/main.php Find the line that starts with:
          newCell.innerHTML = '<a href="' + categoryLink.replace('CATEGORYID', 
          parseInt(record.categories_id)) + '">' + categoryLinkIcon + ' ' 
          + htmlSpecialChars(record.categories_name) + '</a>';
    
    Add the category id to the end of this line. Change the line above to
          newCell.innerHTML = '<a href="' + categoryLink.replace('CATEGORYID', 
          parseInt(record.categories_id)) + '">' + categoryLinkIcon + ' ' 
          + htmlSpecialChars(record.categories_name) + '</a>'
          + '(' + record.categories_id + ')';
    
    b) Edit the file admin/includes/applications/products/pages/main.php Find the line that starts with:
          newCell.innerHTML = '<a href="' + productLink.replace('PRODUCTID', parseInt(record.products_id)) + 
           '">' + useProductLinkIcon + ' ' + htmlSpecialChars(record.products_name) + '</a>';
    
    Add the product id to the end of this line. Change the line above to
          newCell.innerHTML = '<a href="' + productLink.replace('PRODUCTID', parseInt(record.products_id)) + 
           '">' + useProductLinkIcon + ' ' + htmlSpecialChars(record.products_name) + '</a>'
            + '(' + record.products_id + ')'; 
    
  9. 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.

Variant handling in osCommerce 3.0:

For the purposes of computing a Quantity Discount, a product is considered to only have one product id and one category id. Category handling was discussed above and is the same in osCommerce 2.2/2.3 and 3.0.

Product id handling is different in 3.0 because of variants. Product variants are assigned their own product ids, but are linked back to the "master" product via a parent id.

The product id used in Quantity Discounts for product variants is the product parent id. So if product 2 has 2 variants, 3 and 4, both 3 and 4 are considered product id 2 for the purposes of counting and exclusion.


Quantity Discounts Marketing Text for osCommerce 2.2 and 2.3

Donate! Show your appreciation by supporting my efforts.

These marketing text examples are from osCommerce 2.2 and 2.3. Click here for osCommerce 3.0 marketing text examples.

You can add any of these examples of marketing code you like to your own product info page. Edit the file catalog/product_info.php to add one of the code blocks shown below.

Examples

Quantity Discounts Marketing Example 1 Code
<!-- bof osCommerce Quantity Discounts Contribution Example 1 Marketing Text -->
<tr><td>
<?php
  $value = "ot_quantity_discount.php"; 
  require_once(DIR_WS_LANGUAGES . $language . 
          '/modules/order_total/'. $value);
  require_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if (($discount->check() > 0) && 
      ($discount->is_discountable((int)$HTTP_GET_VARS['products_id'])) ) { 
     echo '<div class="content" id="discountPolicy1" 
       style="margin-left:25px; border: 1px solid #000000; width: 600px; padding: 5px; background: #C0C0C0;">';
     echo '<h3>' . STORE_POLICY . '</h3>'; 
     echo $discount->get_html_policy();
     echo '</div>'; 
  }
?>
</td></tr>
<!-- eof Quantity Discounts Contribution Example 1 Marketing Text -->

This creates output that will look something like this:

Quantity Discounts Marketing Example 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


If you want your discounts to appear in "largest first" order, use get_discount_info (which returns an array) instead of get_html_policy (which returns a string). Then simply reverse sort the array. Since we have each item broken out into an array element, we'll make a table with a single column this time:

Quantity Discounts Marketing Example 2 Code

<!-- bof osCommerce Quantity Discounts Contribution Example 2 Marketing Text -->
<tr><td>
<?php
  $value = "ot_quantity_discount.php"; 
  require_once(DIR_WS_LANGUAGES . $language . 
          '/modules/order_total/'. $value);
  require_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if (($discount->check() > 0) && 
      ($discount->is_discountable((int)$HTTP_GET_VARS['products_id'])) ) { 
     echo '<div class="content" id="discountPolicy2">'; 
     echo '<table style="border: 1px solid #999999; background: #000000;">'; 
     echo '<tr><td><h2 style="color: #FFFFFF;">' . STORE_POLICY . '</h2></td></tr>';
     $dislist = $discount->get_discount_info();
     for ($i = 0; $i < sizeof($dislist); $i++) {
       if (($i % 2) == 0) 
            $style = 'class="qd_style_1"';
       else 
            $style = 'class="qd_style_2"';
       echo '<tr ' . $style . '><td>'. $dislist[$i] . "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>'; 
  }
?>
</td></tr>
<!-- eof Quantity Discounts Contribution Example 2 Marketing Text -->


This produces a result that will look like the following:

Quantity Discounts Marketing Example 2 Output

Store Quantity Discount Policy

Buy 10 or more of any item, get 15% off
Buy 20 or more of any item, get 20% off
Buy 30 or more of any item, get 30% off


What if we want to do something that shows the levels as a range? We can always do this sort of thing:

Quantity Discounts Marketing Example 3 Code

<!-- bof osCommerce Quantity Discounts Contribution Example 3 Marketing Text -->
<tr><td>
<?php
  $value = "ot_quantity_discount.php"; 
  require_once(DIR_WS_LANGUAGES . $language . 
          '/modules/order_total/'. $value);
  require_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if (($discount->check() > 0) && 
      ($discount->is_discountable((int)$HTTP_GET_VARS['products_id'])) ) { 
     echo '<div class="content" id="discountPolicy3">'; 
     echo '<table class="qd_style_3"><tr><td>Quantity</td><td>Discount</td></tr>'; 
     $dislist = $discount->get_discount_parms();
     for ($i = 0; $i < sizeof($dislist); $i++) {
         echo '<tr><td>' . $dislist[$i]['level']; 
         if ($i == (sizeof($dislist) - 1)) {
            echo " + "; 
         } else { 
            echo " - ";
            echo $dislist[$i+1]['level'] - 1; 
         }
         echo "</td><td>" . $dislist[$i]['discount'] .  "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>';
  }
?>
</td></tr>
<!-- eof Quantity Discounts Contribution Example 3 Marketing Text -->


This produces an output like the following:

Quantity Discounts Marketing Example 3 Output

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


Quantity Discounts Marketing Example 4 Code - Counting by Currency

Some of the examples above do not work well when using Counting by Currency. Here are is a modification to example 3 to show the effect of discounting by dollars spent.
<!-- bof osCommerce Quantity Discounts Contribution Example 4 Marketing Text -->
<tr><td>
<?php
  $value = "ot_quantity_discount.php"; 
  require_once(DIR_WS_LANGUAGES . $language . 
          '/modules/order_total/'. $value);
  require_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if (($discount->check() > 0) && 
      ($discount->is_discountable((int)$HTTP_GET_VARS['products_id'])) ) { 
     echo '<div class="content" id="discountPolicy4">'; 
     echo '<table class="qd_style_3"><tr><td>Amount Spent</td><td>Discount</td></tr>'; 
     $raw_dislist = $discount->get_discount_parms(true);
     global $currencies; 
     for ($i = 0; $i < sizeof($raw_dislist); $i++) {
         echo '<tr><td>' . $currencies->display_price($raw_dislist[$i]['level'], 0); 
         if ($i == (sizeof($raw_dislist) - 1)) {
            echo " + "; 
         } else { 
            echo " - ";
            echo $currencies->display_price($raw_dislist[$i+1]['level'] - 0.01, 0); 
         }
         echo "</td><td>" . $raw_dislist[$i]['discount'] .  "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>';
  }
?>
</td></tr>
<!-- eof Quantity Discounts Contribution Example 4 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 4 Output

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



Quantity Discounts Marketing Example 5 Code - Counting by Currency

To flip this around and not use currency ranges is straightforward.

<!-- bof osCommerce Quantity Discounts Contribution Example 5 Marketing Text -->
<tr><td>
<?php
  $value = "ot_quantity_discount.php"; 
  require_once(DIR_WS_LANGUAGES . $language . 
          '/modules/order_total/'. $value);
  require_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if (($discount->check() > 0) && 
      ($discount->is_discountable((int)$HTTP_GET_VARS['products_id'])) ) { 
     echo '<div class="content" id="discountPolicy5">'; 
     echo '<table class="qd_style_3"><tr><td>Amount Spent</td><td>Discount</td></tr>'; 
     $raw_dislist = $discount->get_discount_parms(true);
     global $currencies; 
     for ($i = sizeof($raw_dislist) - 1; $i >= 0; $i--) {
         echo '<tr><td>' . $currencies->display_price($raw_dislist[$i]['level'],0) . ' +'; 
         echo "</td><td>" . $raw_dislist[$i]['discount'] .  "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>'; 
  }
?>
</td></tr>
<!-- eof Quantity Discounts Contribution Example 5 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 5 Output

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



Notes:
  • Yes, there really are three ways of getting the quantity discount information out of the ot_quantity_discount object. To review, they are:
    • $discount->get_html_policy() - gets a preformatted string (example 1)
    • $discount->get_discount_info() - returns an array of strings (example 2)
    • $discount->get_discount_parms() - returns an array of {range, discount} pairs (example 3)
    where $discount is an ot_quantity_discount object created using

    $discount = new ot_quantity_discount();
  • If you have used the apply_special_category_discount() or apply_special_item_discount() exits, you should add text below the call to the get_*() method you use to describe these special cases. As a convenience, each of the get_* methods listed above: (get_html_policy(), get_discount_info(), and get_discount_parms() takes a product id, so you can modify the get_* function you're using in ot_quantity_discount.php to be aware of your exit logic.
  • You may also put the marketing logic described above into catalog/product_info.php if you wish to advertise your discounts there.

Quantity Discounts Marketing Text for osCommerce 3.0

Donate! Show your appreciation by supporting my efforts.

These marketing text examples are from osCommerce 3.0. Click here for osCommerce 2.2 marketing text examples.

Adding the marketing text in osCommerce 3.0 uses different code, file placement and html markup. Follow the examples below.

Edit the file templates/<your template>/content/products/info.php to add one of the code blocks shown below.
<!-- bof osCommerce Quantity Discounts Contribution Example 1 Marketing Text -->
<?php
  require_once("includes/classes/order_total.php"); 
  require_once("includes/modules/order_total/quantity_discounts.php"); 
  $osC_Language->load('modules-order_total');
  $discount = new osC_OrderTotal_quantity_discounts();  
  $prid = $osC_Product->getMasterID();
  if (($discount->check()) &&
      ($discount->is_discountable($prid))) { 
     echo '<div class="content" id="discountPolicy1" 
       style="border: 1px solid #000000; padding: 5px; background: #ffffcc;">';
     echo '<h4>' . $discount->STORE_POLICY . '</h4>'; 
     echo $discount->get_html_policy($prid);
     echo '</div>'; 
     echo '<br />';
  }
?>
<!-- eof Quantity Discounts Contribution Example 1 Marketing Text -->

This creates output that will look something like this:

Quantity Discounts Marketing Example 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


<!-- bof osCommerce Quantity Discounts Contribution Example 2 Marketing Text -->
<?php
  require_once("includes/classes/order_total.php"); 
  require_once("includes/modules/order_total/quantity_discounts.php"); 
  $osC_Language->load('modules-order_total');
  $discount = new osC_OrderTotal_quantity_discounts();  
  $prid = $osC_Product->getMasterID();
  if (($discount->check()) &&
      ($discount->is_discountable($prid))) { 
     echo '<div class="content" id="discountPolicy2">'; 
     echo '<table style="border: 1px solid #999999; background: #000000;">'; 
     echo '<tr><td><h2 style="color: #FFFFFF;">' . $discount->STORE_POLICY . '</h2></td></tr>';
     $dislist = $discount->get_discount_info($prid);
     for ($i = 0; $i < sizeof($dislist); $i++) {
       if (($i % 2) == 0) 
            $style = 'class="qd_style_1"';
       else 
            $style = 'class="qd_style_2"';
       echo '<tr ' . $style . '><td>'. $dislist[$i] . "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>'; 
  }
?>
<!-- eof Quantity Discounts Contribution Example 2 Marketing Text -->


This produces a result that will look like the following:

Quantity Discounts Marketing Example 2 Output

Store Quantity Discount Policy

Buy 10 or more of any item, get 15% off
Buy 20 or more of any item, get 20% off
Buy 30 or more of any item, get 30% off

<!-- bof osCommerce Quantity Discounts Contribution Example 3 Marketing Text -->
<?php
  require_once("includes/classes/order_total.php"); 
  require_once("includes/modules/order_total/quantity_discounts.php"); 
  $osC_Language->load('modules-order_total');
  $discount = new osC_OrderTotal_quantity_discounts();  
  $prid = $osC_Product->getMasterID();
  if (($discount->check()) && 
      ($discount->is_discountable($prid))) { 
     echo '<div class="content" id="discountPolicy3">'; 
     echo '<table><tr><td>' . $discount->HEADER_QUANTITY . '</td><td>' 
       . $discount->HEADER_DISCOUNT . '</td></tr>'; 
     $dislist = $discount->get_discount_parms($prid);
     $raw_dislist = $discount->get_discount_parms($prid, true);
     for ($i = 0; $i < sizeof($dislist); $i++) {
         echo '<tr><td>' . $dislist[$i]['level']; 
         if ($i == (sizeof($dislist) - 1)) {
            echo " + "; 
         } else { 
            echo " - ";
            echo $raw_dislist[$i+1]['level'] - 1; 
         }
         echo "</td><td>" . $dislist[$i]['discount'] .  "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>';
  }
?>
<!-- eof Quantity Discounts Contribution Example 3 Marketing Text -->


This produces an output like the following:

Quantity Discounts Marketing Example 3 Output

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


<!-- bof osCommerce Quantity Discounts Contribution Example 4 Marketing Text -->
<?php
  require_once("includes/classes/order_total.php"); 
  require_once("includes/modules/order_total/quantity_discounts.php"); 
  $osC_Language->load('modules-order_total');
  $discount = new osC_OrderTotal_quantity_discounts();  
  $prid = $osC_Product->getMasterID();
  if (($discount->check()) &&
      ($discount->is_discountable($prid))) { 
     echo '<div class="content" id="discountPolicy4">'; 
     echo '<table class="qd_style_3"><tr><td>' . $discount->HEADER_AMOUNT_SPENT . '</td><td>' . 
       $discount->HEADER_DISCOUNT . '</td></tr>'; 
     $dislist = $discount->get_discount_parms($prid);
     $raw_dislist = $discount->get_discount_parms($prid, true);
     global $osC_Currencies;
     for ($i = 0; $i < sizeof($dislist); $i++) {
         echo '<tr><td>' . $dislist[$i]['level']; 
         if ($i == (sizeof($dislist) - 1)) {
            echo " + "; 
         } else { 
            echo " - ";
            echo $osC_Currencies->displayPrice($raw_dislist[$i+1]['level'] - 0.01, 0); 
         }
         echo "</td><td>" . $dislist[$i]['discount'] .  "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>';
  }
?>
<!-- eof Quantity Discounts Contribution Example 4 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 4 Output

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



<!-- bof osCommerce Quantity Discounts Contribution Example 5 Marketing Text -->
<?php
  require_once("includes/classes/order_total.php"); 
  require_once("includes/modules/order_total/quantity_discounts.php"); 
  $osC_Language->load('modules-order_total');
  $discount = new osC_OrderTotal_quantity_discounts();  
  $prid = $osC_Product->getMasterID();
  if (($discount->check()) &&
      ($discount->is_discountable($prid))) { 
     echo '<div class="content" id="discountPolicy5">'; 
     echo '<table class="qd_style_3"><tr><td>' . $discount->HEADER_AMOUNT_SPENT . '</td><td>' . 
       $discount->HEADER_DISCOUNT . '</td></tr>'; 
     $dislist = $discount->get_discount_parms($prid);
     $raw_dislist = $discount->get_discount_parms($prid, true);
     global $osC_Currencies;
     for ($i = sizeof($dislist) - 1; $i >= 0; $i--) {
         echo '<tr><td>' . $dislist[$i]['level'] . ' +'; 
         echo "</td><td>" . $dislist[$i]['discount'] .  "</td></tr>";
     }
     echo '</table>';
     echo '<br /></div>'; 
  }
<!-- eof Quantity Discounts Contribution Example 5 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 5 Output

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



Notes:
  • Yes, there really are three ways of getting the quantity discount information out of the ot_quantity_discount object. To review, they are:
    • $discount->get_html_policy() - gets a preformatted string (example 1)
    • $discount->get_discount_info() - returns an array of strings (example 2)
    • $discount->get_discount_parms() - returns an array of {range, discount} pairs (example 3)
    where $discount is an ot_quantity_discount object created using

    $discount = new ot_quantity_discount();
  • If you have used the apply_special_category_discount() or apply_special_item_discount() exits, you should add text below the call to the get_*() method you use to describe these special cases. As a convenience, each of the get_* methods listed above: (get_html_policy(), get_discount_info(), and get_discount_parms() takes a product id, so you can modify the get_* function you're using in ot_quantity_discount.php to be aware of your exit logic.
  • You may also put the marketing logic described above into catalog/product_info.php if you wish to advertise your discounts there.

osCommerce 2.2/2.3 Files

(new) catalog/includes/languages/english/modules/order_total/ot_quantity_discount.php
(new) catalog/includes/modules/order_total/ot_quantity_discount.php


osCommerce 3.0 Files

(new) admin/includes/modules/order_total/quantity_discounts.php
(new) includes/languages/en_US/modules/order_total/quantity_discounts.xml
(new) includes/modules/order_total/quantity_discounts.php


Major Versions

  • 1.0.3 09/21/2013 - Updated for osC 2.3.3.3
  • 1.0.2 11/01/2011 - Added "VAT" tax setting
  • 1.0.1 06/14/2009 - Update for use with Order Editor
  • 1.0 05/16/2009 - First Release

FAQ

Q: How do I install this software?
A: Installation instructions for osCommerce 2.2/2.3 are here. Installation instructions for osCommerce 3.0 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: I can't seem to get Quantity Discounts for osCommerce 3.0 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 Modules. 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 quantity_discounts.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).
Q: In osCommerce 3.0, why don't I see my changes to includes/languages/en_US/modules/order_total/quantity_discounts.xml?
A: The strings are actually loaded into the database once only at install time. To import the new strings into your database, go to Admin->Configuration->Languages, check the box next to your language and press the Import button. Select the "Replace All" radio button and press the Import button. You will now see the changes you have made.

Q: What else do I have to know about osCommerce 3.0?
A: Read Installation for osCommerce 3.0, Marketing Text for osCommerce 3.0, and Variant Handling in osCommerce 3.0.

Q: How do you make this work in osCommerce 2.2-MS2? The admin is full of links and I can't edit anything.
A: Edit the file catalog/includes/modules/order_total/ot_quantity_discounts.php and look for the function install().

Delete the first line of code in the function

tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('&copy; That Software Guy<br /><a href=\"https://www.thatsoftwareguy.com/donate.html\" target=\"_blank\">Donate</a>  <a href=\"https://www.thatsoftwareguy.com/osc_quantity_discounts.html\" target=\"_blank\">Help</a><br /><br />This module is installed', 'MODULE_ORDER_TOTAL_QUANTITY_DISCOUNT_STATUS', 'true', '', '6', '1','tep_cfg_select_option(array(\'true\'), ', now())");


In its place, insert

tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('&copy; www.thatsoftwareguy.com<br />Module Installed', 'MODULE_ORDER_TOTAL_QUANTITY_DISCOUNT_STATUS', 'true', '', '6', '1','tep_cfg_select_option(array(\'true\'), ', now())");


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.