quantity discounts

Quantity Discounts

A Zen Cart™ discounting module for bulk purchases.

Zen Cart has a built-in quantity discounting mechanism (native quantity discounts), which allows you to discount bulk purchases of a single item. However, some merchants wish to discount quantity purchases of a group of items from a category or even the entire number of items purchased or dollars spent. The Quantity Discounts contribution permits you to offer discounts that are structured in ways like this.

For a comparison of native quantity discounts and this contribution, click here.

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

Background: See the Zen Cart Matrix-o-discounts

Relevance: Zen Cart™ 1.3.5 - 1.3.9, 1.5.x

Current Version: 1.12.1 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.

Support Thread: Quantity Discounts Support Thread

Cost: Free, but donation appreciated

Installed Cost: $60.00 Buy Professional Installation by That Software Guy

Installation Difficulty: Easy

Installation Instructions: click here

Tax Notes: Notes on tax handling

Location: Zen Cart Downloads page, under Pricing Tools.

Download: Quantity Discounts on Zen Cart Downloads Page

FAQ: click here

Configuration: All configuration is done through Admin->Modules->Order Total.

Marketing Text: click here

See it Live: Go to my demo shop and add ten of any item to your cart (excluding gift certificates). You will immediately see the discount because of Discount Preview, but without it, you would see the discount on the second page of checkout. Also note the marketing text for Quantity Discounts on the Product Info page. The format shown in example 1 is used.
Promotional Page: free from my website

Add-Ons:
Quantity Discounts is an order total module, so the discount is not visible until the second page of checkout. If you want to see the discount on the shopping cart page (or sidebox), look at Discount Preview.




For example, in my demo shop, adding 10 of the same item to the cart displays the discount on the shopping cart page because of Discount Preview.

Here is a sample of the Quantity Discounts promotional page, which is available for download here.

Alternatives: Quantity Discounts is not perfect for every situation. Please match the scenarios below with your store's needs:

Description Solution
You have different quantity discounts (and possibly none) for each product in your store. STOP. Quantity Discounts is not right for you. You should look at the native Quantity Discounting mechanism in Zen Cart, which is called the Products Price Manager. Search the forum for help or read some details in the FAQ for this mod.
You have different quantity discounts, or different quantity or purchase requirements for discounts, for specific categories in your store. STOP. Quantity Discounts is not right for you. Please look at Table Discounts.
Otherwise: Carry on, pilgrim, and see if Quantity Discounts meets your needs.


Bugs: click here

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
    Configuration of Quantity Discounts
    Quantity Discounts is configured through the Admin interface (Admin-> Modules->Order Totals->Quantity Discount).

    The image to the right 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).

    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 parent subcategory, not the top level category.

    Men's Clothing 
         |
         ---->  Shirts
                |
                -------> shirt A     
                         shirt B
                         shirt C 
    
    In this example, the parent category of "shirt A" is "Shirts," not "Men's Clothing." "Mens' Clothing" would be considered the top level category.

    In stores where the first level of categories has products directly underneath it, the top level category and the parent category are the same. If have the ability to reference categories other than the parent category is important to your store, you may wish to consider purchasing Table Discounts, Combination Discounts or Big Chooser.

    Specifying "include tax = true" will gross the discount up by the amount of tax that would have been paid on the goods pre-discount. 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 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. Checkout Page User Interface
    Quantity Discounts is an "Order Total" module, which means that by default, the discount is not shown until the second page of checkout (Payment Information). However, using the Discount Preview module will allow you to show the discount in the shopping cart.

    Depending on your configuration, the second page of checkout will look something like this:

    Checkout Payment Information Page with Quantity Discounts

    Notice that "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:

    Quantity Discount Explanation

    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:

    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:

    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?

    Quantity Discounts 1.4 has the answer. In the file 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,
  5. Big Spender vs. Quantity Discounts by Dollars Spent:
    Big Spender offers discounting by dollars spent with many more options than Quantity Discounts. Configuration of Big Spender is in PHP, much like Better Together. If you simply wish to offer a price sensitive discount on the entire cart, you can use Quantity Discounts, but if you want to do things like offer a free gift over a certain level of spending, or have multiple price sensitive offerings which are mutually exclusive, you should check out Big Spender.
  6. Free Gift Spender vs. Quantity Discounts by Dollars Spent:
    Free Gift Spender offers discounting by dollars spent with many more options than Quantity Discounts with a built-in admin panel. If you simply wish to offer a price sensitive discount on the entire cart, you can use Quantity Discounts, but if you want to do things like offer a free gift over a certain level of spending, or have multiple price sensitive offerings which are mutually exclusive, you should check out Free Gift Spender.

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. Login to admin and in Modules->Order Total you will see 'Quantity Discount' listed along with all the other modules available.
  5. Click on 'Quantity Discount' 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 second step of checkout in "Your Total" 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. If you wish, install a Quantity Discounts Promotional Page by following the links at the top of this page.
  10. Not donating often causes problems. Donate! Show your appreciation by supporting my efforts.


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.
  2. I believe it is potentially confusing to show the shopping cart sidebox on pages which already display the shopping cart contents. Zen Cart has a built in feature to turn off the shopping cart sidebox on the shopping cart page (Go to Admin->Configuration->Layout Settings->Shopping Cart Box Status, and set this value to 2.). However, turning it off on additional pages is not difficult, and I recommend doing so. Here are the steps:
    1. If you do not have a includes/modules/sideboxes/YOUR_TEMPLATE/shopping_cart.php file, create one by copying the file includes/modules/sideboxes/shopping_cart.php.
    2. Above the line
         if ($show_shopping_cart_box == true) {
      
      add this block
           if ($show_shopping_cart_box == true) { 
              if (($current_page_base == "shopping_cart") || 
                  ($current_page_base  == "account_history_info") ||
                  ($current_page_base  == "checkout") ||
                  ($current_page_base  == "checkout_payment") ||
                  ($current_page_base  == "checkout_confirmation") 
                 ) { 
                    $show_shopping_cart_box = false;
              }
           }
      
      I did not turn the sidebox off on the checkout shipping page, but you certainly may do so by adding that as a case.


Marketing

Donate! Show your appreciation by supporting my efforts.

If you would like to have a Quantity Discounts Promotional Page that describes your store quantity discounts as part of your categories sidebox, you may download a copy here.

You can also add any of these examples of marketing code you like to your own product info page. Create your custom template and edit the file tpl_product_info_display.php to add one of the code blocks shown below.

Quantity Discounts Marketing Example 1 Code
<!-- bof Quantity Discounts Contribution Example 1 Marketing Text -->
<?php
  $value = "ot_quantity_discount.php"; 
  include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
          '/modules/order_total/', $value, 'false'));
  include_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if ($discount->check() > 0) { 
     echo '<div class="content" id="discountPolicy1" 
       style="margin-left:25px; border: 1px solid #000000; width: 300px; padding: 5px; background: #C0C0C0;">';
     echo '<h2>' . STORE_POLICY . '</h2>'; 
     echo $discount->get_html_policy();
     echo '<br /></div>'; 
  }
?>
<!-- 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 Quantity Discounts Contribution Example 2 Marketing Text -->
<?php
  $value = "ot_quantity_discount.php"; 
  include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
          '/modules/order_total/', $value, 'false'));
  include_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if ($discount->check() > 0) {
     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>'; 
  }
?>
<!-- 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 Quantity Discounts Contribution Example 3 Marketing Text -->
<?php
  $value = "ot_quantity_discount.php"; 
  include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
          '/modules/order_total/', $value, 'false'));
  include_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if ($discount->check() > 0) {
     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>'; 
  }
?>
<!-- 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%


If you want to do something close to what the native Quantity Discounts does, which is show each of the prices and the range required to get that price, it can also be done. However, note that this only works for discount units percentage. I also believe that it only makes sense for a Discount Basis of Total By Item, but this restriction is not enforced.

To show final, discounted prices at various quantity levels, you could do something like this:

Quantity Discounts Marketing Example 4 Code

<!-- bof Quantity Discounts Contribution Example 4 Marketing Text -->
<?php
  $value = "ot_quantity_discount.php"; 
  include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
          '/modules/order_total/', $value, 'false'));
  include_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if ($discount->check() > 0) {
     echo '<div class="content" id="discountPolicy4">'; 
     echo '<table class="qd_style_4"><tr><td>Quantity</td><td>Price</td></tr>'; 
     $priceinfo =  $discount->get_discounted_prices($_GET['products_id'], $current_category_id); 
     for ($i = 0; $i < sizeof($priceinfo); $i++) {
         echo '<tr><td>' . $priceinfo[$i]['level']; 
         if ($i == (sizeof($priceinfo) - 1)) {
            echo " + "; 
         } else { 
            echo " - ";
            echo $priceinfo[$i+1]['level'] - 1; 
         }
         echo "</td><td>" . $priceinfo[$i]['price'] .  "</td></tr>";
     }
     echo '</table>';
     echo '<br /><br /></div>'; 
  }
?>
<!-- eof Quantity Discounts Contribution Example 4 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 4 Output

QuantityPrice
10 - 19$424.96
20 - 99$399.96
100 + $249.98


If you just wanted to print out the best price, you could do something like this:

Quantity Discounts Marketing Example 5 Code

<!-- bof Quantity Discounts Contribution Example 5 Marketing Text -->
<?php
  $value = "ot_quantity_discount.php"; 
  include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
          '/modules/order_total/', $value, 'false'));
  include_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if ($discount->check() > 0) { 
     echo '<div class="content" id="discountPolicy5" style="background: #999933; padding: 3px; width: 400px;">'; 
     $priceinfo =  $discount->get_discounted_prices($_GET['products_id'], $current_category_id); 
     $last = sizeof($priceinfo) - 1; 
     echo "Priced as low as " . $priceinfo[$last]['price'] . 
        " in quantities of " . $priceinfo[$last]['level'] . " or more."; 
     echo '<br /><br /></div>'; 
  }
?>
<!-- eof Quantity Discounts Contribution Example 5 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 5 Output

Priced as low as $249.98 in quantities of 100 or more.


The restrictions for example 4 also apply here.

Quantity Discounts Marketing Example 6 Code - Counting by Currency

Some of the examples above - examples 3 and 4 for instance, 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 Quantity Discounts Contribution Example 6 Marketing Text -->
<?php
  $value = "ot_quantity_discount.php"; 
  include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
          '/modules/order_total/', $value, 'false'));
  include_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if ($discount->check() > 0) {
     echo '<div class="content" id="discountPolicy3">'; 
     echo '<table class="qd_style_3"><tr><td>Amount Spent</td><td>Discount</td></tr>'; 
     $dislist = $discount->get_discount_parms();
     $raw_dislist = $discount->get_discount_parms(true);
     global $currencies; 
     for ($i = 0; $i < sizeof($dislist); $i++) {
         echo '<tr><td>' . $currencies->display_price($dislist[$i]['level'], 0); 
         if ($i == (sizeof($dislist) - 1)) {
            echo " + "; 
         } else { 
            echo " - ";
            echo $currencies->display_price($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 6 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 6 Output

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



Quantity Discounts Marketing Example 7 Code - Counting by Currency

To flip this around and not use currency ranges is straightforward.
<!-- bof Quantity Discounts Contribution Example 7 Marketing Text -->
<?php
  $value = "ot_quantity_discount.php"; 
  include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
          '/modules/order_total/', $value, 'false'));
  include_once(DIR_WS_MODULES . "order_total/" . $value);
  $discount = new ot_quantity_discount();  
  if ($discount->check() > 0) {
     echo '<div class="content" id="discountPolicy3">'; 
     echo '<table class="qd_style_3"><tr><td>Amount Spent</td><td>Discount</td></tr>'; 
     $dislist = $discount->get_discount_parms();
     global $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 7 Marketing Text -->


This would produce output looking like this:

Quantity Discounts Marketing Example 7 Output

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



Notes:
  1. Yes, there really are four 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)
    • $discount->get_discounted_prices() - returns an array of {range, price} pairs (examples 4,5)
    where $discount is an ot_quantity_discount object created using
       $discount = new ot_quantity_discount();
    

  2. If you have used the apply_special_category_discount() or apply_special_item_discount() exits, and you are using the marketing text, bear in mind that the marketing text doesn't know and cannot figure out how your have coded the apply_special* functions. You will need to hardcode the marketing text for these cases.

    We'll use the example 1 code to show the difference:

    Quantity Discounts Marketing Example 1 Code
    <!-- bof Quantity Discounts Contribution Example 1 Marketing Text -->
    <?php
      $value = "ot_quantity_discount.php"; 
      include_once(zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . 
              '/modules/order_total/', $value, 'false'));
      include_once(DIR_WS_MODULES . "order_total/" . $value);
      $discount = new ot_quantity_discount();  
      if ($discount->check() > 0) { 
         if ($current_category_id == 6) { 
            echo '<div class="content" id="discountPolicy1" 
              style="margin-left:25px; border: 1px solid #000000; width: 300px; padding: 5px; background: #C0C0C0;">';
            echo '<h2>' . STORE_POLICY_CATEGORY_6 . '</h2>'; 
            echo "Buy 3 items from category 6, get 20% off"; 
            echo '<br /></div>'; 
         } else { 
            echo '<div class="content" id="discountPolicy1" 
              style="margin-left:25px; border: 1px solid #000000; width: 300px; padding: 5px; background: #C0C0C0;">';
            echo '<h2>' . STORE_POLICY . '</h2>'; 
            echo $discount->get_html_policy();
            echo '<br /></div>'; 
         }
      }
    ?>
    <!-- eof Quantity Discounts Contribution Example 1 Marketing Text -->
    
    If you have multiple quantity discounting schedules for different categories or items, I would encourage you to look at Table Discounts.
  3. If you have used one of the user exits - for example, exclude_category() or exclude_product(), you will need to change the marketing text code to wrap it in a check for the exclusion. For category exclusions, change tpl_product_info_display.php from
      if ($discount->check() > 0) {
    
    
    to
      if ( ($discount->check() > 0) &&
           (!$discount->exclude_category($current_category_id)) ) {
    
    For product exclusions, use
      if ( ($discount->check() > 0) &&
           (!$discount->exclude_product($_GET['products_id'])) ) {
    
  4. If you have linked products, remember that $current_category_id may not be the master category id of the product in question. This is important if you are handling different categories in different ways (i.e. using exclude_category() or apply_special_category_discount()). In this case, it's safer to query the category id value directly than to rely on $current_category_id. My Zen Cart Category Issues page discusses how to get this value.
  5. If you are also using the Discount Preview Extension, please be sure that the second and third statements above in your code are "include_once" and not "include" as they were formerly.
  6. You may also put the marketing logic described above into tpl_product_info_display.php if you wish to advertise your discounts there. In the case of get_discounted_prices(), this is the only place where it would make sense.
  7. Use get_discounted_prices() with caution - it only makes sense for Discount Units percentage and Discount Basis Total By Item.
  8. The examples shown here do inline styling. What you likely want to do is create the file includes/templates/YOUR_TEMPLATE/css/product_info.css and put the styling in there. For example, using Example 3,
    #discountPolicy3 table {
         border: 1px solid #cccccc;
         background-color: #FFFF66; 
    }
    

Quantity Discounts and Discount Preview

Discount Preview makes Quantity Discounts pop! It shows the discount on the shopping cart page, instead of making your customers wait until the checkout payment page to see their discounts. Here's a screenshot of the shopping cart page on a cart using Discount Preview and Quantity Discounts. The discount was $2 per bag off over 25.

Zen Cart Quantity Discounts and Discount Preview

Discount Preview is sold separately. Buy Discount Preview Now!

Notes on Taxes


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.

Be sure that the sort order for the Tax module (set in Admin->Modules->Order Total->Tax) is greater than the largest order total sort order, so that your taxes are shown after all discounts. 399 is a good value for most stores.

Files

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

Earlier versions of Quantity Discounts included a customized copy of includes/classes/shopping_cart.php; this was removed in Quantity Discounts 1.2 because the customization which Quantity Discounts required was incorporated into the 1.3.5 Zen Cart core code. Thus, Quantity Discounts 1.2 and higher are only compatible with Zen Cart 1.3.5 and higher.

Major Versions

  • 1.12 07/25/2016 Updating to new style constructor for PHP7. No functional changes.
  • 1.12 04/16/2013 Code inspection and cleanup. No functional changes.
  • 1.11 04/03/2010 Add VAT setting for taxes.
  • 1.10a 02/13/2010 Remove donate link for forum rules compliant.
  • 1.10 05/10/2008 Make explanatory link show up only on checkout page, adding discounts by dollars spent.
  • 1.9 01/06/2008 Remove unused code in order total calculation
  • 1.8 12/11/2007 Add compatibility for 1.3.8 changes.
  • 1.7 11/19/2007 Add per item currency discount
  • 1.6 09/06/2007 Fix computational error in get_discounted_prices(); standardize labels for level/amount fields in Admin.
  • 1.5 03/31/2007 Fix errant line accidentally left in setup() during testing.
  • 1.4 01/13/2007 Added ability to specify {level, discount} pairs so that the limit of 5 in the admin screen could be exceeded. Added many more options for creating marketing text.
  • 1.3 09/26/2006 Updated documentation; No functional difference from 1.2.
  • 1.2 09/04/2006 Fixed sales tax calculation for various combinations; integrated with 1.3.5 fixes
  • 1.1 08/24/2006 User exits for various customizations, support for dynamic creation of discount policy explanation, minor fixes.
  • 1.0 08/12/2006 - First Release

Bugs

  • Users wishing to sort native discounts (such as Coupons or Group Discounts) after my discounts, with tax recalculation, should look at this page.
  • Quantity Discounts versions prior to 1.8 require a patch to run under Zen Cart 1.3.8. Take this function and paste it right above the function calculate_deductions() in the file includes/modules/order_total/ot_quantity_discount.php. The latest versions include this patch, but if you haven't upgraded, you must manually apply the patch.
  • If your Zen Cart version is 1.3.0 (or higher) but less than 1.3.5, you may still run Quantity Discounts version 1.1, but not 1.2 or higher. If your Zen Cart version is less than 1.3.0, you must upgrade to a more recent version of Zen Cart in order to run Quantity Discounts.

FAQ

Q: I have different levels and/or amounts for different categories. How can I handle this using Quantity Discounts?
A: If you have multiple discounting schedules with different levels or amounts, you should look at Table Discounts.

Q: I'm using Discount Basis Total by Category, or a category related user exit, like exclude_category() or apply_special_category_discount(), and it's not working!
A: There are several possible root causes. Please see the Category Issues page for solutions.

Q: How do I install this software?
A: If you've never installed a Zen Cart mod before, please read my Guide to Mod Installation on Zen Cart.

Q: I can't seem to get Quantity Discounts to work. What am I doing wrong?
A: Please check the following things:
  • Go to Admin->Modules->Order Total. Do you see Quantity Discounts? If not, then you haven't installed it. Follow the README.
  • If you do see it, the circle at the right hand end of the row for Quantity Discounts should be green. If it's not green, reinstall it.
  • Click on Quantity Discounts. The Level and Amount fields are all numeric. Do not use dollar or percent signs in these fields.
  • If you're using Discount Basis "Total By Category," see the previous FAQ question.
  • 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).
  • Re-read my Guide to Mod Installation on Zen Cart.

Q: I want to use discount units 'currency per item' but I don't see it!
A: This feature was added in Quantity Discounts 1.7 and requires a database change. Please download version 1.7 (or better), and copy the files to your store. Then, in Admin->Modules->Order Total, press remove on Quantity Discounts, then press install. You will have to reconfigure Quantity Discounts with your desired settings at this point.

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: Why is there a Quantity Discounts contribution in the first place?
A: Zen Cart has a number of intrinsic discounting mechanisms: coupons, specials, sales, group discounts, discounts via the products price manager, etc.
While each of these fills a need, some retailers have different discounting needs that cannot easily be met by any of these features. The Quantity Discounts contribution was designed to meet these needs.

Q: What is the Products Price Manager?
A: The Products Price Manager implements Zen Cart's native Quantity Discounting mechanism. Go to Admin->Catalog->Categories/Products, select a product, and press the green $ sign. You are now in Products Price Manager.

There is a small FAQ entry on Products Price Manager. More comprehensive documentation is available in the e-Start Your Web Store with Zen Cart manual on p. 231-232. You can also look at Picaflor Azul's page on this topic.

Q: Is the Quantity Discounts contribution related to the native Quantity Discounting feature in Products Price Manager?
A: No. The Quantity Discounts contribution has its own configuration (through Modules->Order Total) and database entries, and is completely separate from the Products Price Manager.

Q: What does the Quantity Discounts Contribution do that Zen Cart's native Quantity Discounting mechanism doesn't?
A: Installing the Quantity Discounts Contribution and trying it out is the best way to get an understanding of the differences. A partial list is:

Zen Cart's Quantity Discount feature That Software Guy's Quantity Discount Contribution
Only allows per-product quantity discounts Allows you to discount by item, by parent category, by all items in the cart or by dollars spent.
Must be configured for every product on which you want to offer quantity discounts Quantity Discounts Contribution applies to all products (except those you specifically exempt)
Allows unlimited numbers of discount levels, but discounts must be individually configured on each product Allows only five discount levels in the admin panel with the ability to add more in the setup() function; discounting is applied to all products except where specified
Changing behavior involves modifying core code Changing behavior involves modifying the contribution.
Marketing text is fixed, automatically generated Marketing text may be customized to your needs, but must be added manually
Discounting by items purchased only Discounting by items purchased *or* by dollars spent


Q: What does the Quantity Discounts Contribution using counting by currency do that the Price Sensitive Discount doesn't?
A: Here's the list:
  • Extensive support for automatically created marketing text
  • Automatically created Zen Cart Promotional Page for your discounts
  • Support for including/excluding products/categories from the spend computation
  • Support for unlimited numbers of spend levels
  • It works with Discount Preview


Q: I have a table on my product info screen that looks like this (below). Is this the Quantity Discounts Contribution?
Zen Cart Native Quantity Discounts
A: No - this is the native Quantity Discounts mechanism. Go to Catalog->Categories/Products and find the product this that shows this table. Press the green $ to enter Products Price Manager, and delete these discounts; they are separate from (and will be applied in addition to) any discounts you configure in the Quantity Discounts Contribution.

The Quantity Discounts Contribution's marketing text is different; examples are provided here.

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.

Alternately, you can indicate that you have a quantity discount policy by adding to TEXT_INFORMATION in includes/languages/english/shopping_cart.php, and inform the user that the discounts will be calculated (and visible) at checkout time. Additionally, changing SUB_TITLE_SUB_TOTAL in the same file to something like 'Sub-Total BEFORE Discount' will emphasize the fact that a discount will be added at checkout time.

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


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


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


If you have a lot of rules like this, please look at Table Discounts.

Q: I only want discounts applied to product numbers 17 and 19. How do I do this?
A: Update the function exclude_product() in 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 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: I don't want discounts applied to items whose DESCRIPTION fields include the phrase "Sold:NET" or "No Quantity Discount". How do I do this?
A: Update the function exclude_product() in includes/modules/order_total/ot_quantity_discount.php as follows:
   function exclude_product($id) {
      global $db;
      $prid = (int)$id;
      $anti_patterns = array("No Quantity Discount", "Sold: NET"); 
      $desc = zen_get_products_description($prid); 
      foreach ($anti_patterns as $anti) { 
         if (false !== strstr($desc,  $anti)) return true;
      }
      return false;
   }


Q: How can I present my discounting schedule on the product page?
A: Create your custom template if you haven't already done so. Then customize the file includes/templates/template_default/templates/tpl_product_info_display.php. Look at the examples in marketing. Add the one that fits your needs best to tpl_product_info_display.php; start by putting it after the product description (although the placement is a matter of personal taste).

Q: Why are there four ways of getting the discounting information - get_html_policy(), get_discount_info(), get_discount_parms() and get_discounted_prices()?
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.

Q: Can you explain how sales tax works?
A: Yes. See this page.


Extensions

The following Quantity Discounts extensions are available:
  • Show Quantity Discounts on the shopping cart page using Discount Preview
  • Market your Quantity Discounts using the Quantity Discounts Promotional Page (this one's free).
  • Other customizations as required: include and exclude items, offer discounts to certain groups only, etc.
I charge a fee for each of these extensions. Contact me for details.