Category Issues in Zen Cart Mods

This page describes category problems you can encounter when using the following Zen Cart mods: If you're unsure about discounting mods and which one to use for your particular need, please refer to the Zen Cart Matrix-o-discounts.

Discounting Mods (and other mods) using categories

Mod Category Functions
Quantity Discounts Discount Basis Total by Category, user exits exclude_category() or apply_special_category_discount()
Better Together Linkages add_prod_to_cat(), add_cat_to_prod(), add_cat_to_cat() or add_twoforone_cat()
Combination Discounts Linkages using CAT
Big Spender Constraints or discounts using CAT
Big Chooser Constraints or discounts using CAT
Free Gift Chooser Required contents or Free Gifts using CAT
Gift Wrap at Checkout User exits exclude_category() or apply_category_wrap_surcharge()
Newsletter Discount user exit exclude_category()
Manufacturer Discount user exit exclude_category()
Impulse Buy Constraints using CAT
Auto Add Triggers or auto_adds using CAT
Table Discounts Constraints using CAT
BOGO Discount Discount Basis Same Parent Category, Same Top Level Category, user exit exclude_category()


Category Handling in Discount Mods

Determining a product's category in Zen Cart can be confusing. Products are initially created in one category, but can be "linked" to other categories. In most of my mods, the "linked" categories are ignored; products are considered to be in the the parent category in which they were initially created. This value is reflected in a field called "master_categories_id" in the products table, with one such setting per product.

A few of my mods do respect linked categories. They look for category matches both in master category and ancestors, and in linked categories and ancestors. These mods are Free Gift Chooser and Free Gift Spender.

ToolCategory Definition
Quantity Discounts The parent category id, as determined by the master_categories_id field in the products table. Linked Categories are not used.
Better Together Same as Quantity Discounts (parent category only).
Military Discount Same as Quantity Discounts (parent category only).
Newsletter Discount Same as Quantity Discounts (parent category only).
Combination Discounts The parent category id, as determined by the master_categories_id field in the products table, and all parents of that category. Linked Categories are not used.
Big Spender Same as Combination Discounts (parent and all ancestors).
Big Chooser Same as Combination Discounts (parent and all ancestors).
Free Gift Chooser Does parent and all ancestors, like Combination Discounts, but then tries linked categories (looks up product in products_to_categories table).
Free Gift Spender Same as Free Gift Chooser (parent and all ancestors, then linked categories).
Frequency Discounts Same as Quantity Discounts (parent category only).
Table Discounts Same as Combination Discounts (parent and all ancestors).
Manufacturer Discount N/A (no category operations).
BOGO Discount Either the parent category id, as determined by the master_categories_id field in the products table, OR the top level category, as determined by the highest level parent of the parent category id. Linked Categories are not used.


Category Handling in Other Mods

ToolCategory Definition
Gift Wrap at Checkout Same as Quantity Discounts.
Spender AutoCoupon Same as Big Spender.
Chooser AutoCoupon Same as Big Chooser.
Impulse Buy Same as Big Chooser.
Auto Add Same as Big Chooser.


The Parent Category

The parent category of a product is the master_categories_id field in the products table. This can be confusing when looking at Linked Products, which appear to have multiple parents.

Note that the parent is the direct ancestor. Some mods can deal with higher level ancestors (such as grandparent or great-grandparent); others can only deal with parent and top level category. Be sure to select a mod that meets your needs.
Clothing (Top Level Category )
|
   Men's Clothing (Great Grand Parent) 
   | 
     ---->  Shirts (Grand Parent)
            |
            -------> Dress Shirts (Parent) 
                     |
                     -------> Dress Shirt A     
                              Dress Shirt B
If you need to select at the GrandParent or Great Grandparent level, the discounting mods that are appropriate are (Quantity Discounts, Better Together and BOGO do not work at those levels.)

Master Categories are used for Linked Products

References to "category" in the above modules are really references to the master_categories_id field in the products table. For linked products, this will not be the parent category that you see in the breadcrumb when you're looking at the product info page, but rather, the parent category of the original product which was linked. For example, look at this product. You'd think that the parent category was Big Linked, but in fact, it's Drama. To determine the master category used by a product, you can use this query in a phpMyAdmin SQL query window. We'll look at product 16
select master_categories_id
   from products 
   where products_id = 16;
Obviously if your tables have a prefix, you must include this (use "zen_products" instead of "products" if your prefix is "zen_" for instance).

This query, run on the database above, would give you the answer "15". So the the master category for product 16 is 15.

Master Categories set to 0 problem

This is the most common root cause of category problems. Category functions use the master_categories_id field in the products_table. Sometimes databases get messed up and have the master_categories_id in the products table set to 0. To verify whether this is your problem, use the following query in a phpMyAdmin SQL query window:
select p.products_id, p.master_categories_id, s.products_id, s.categories_id
   from products p, products_to_categories s
   where p.products_id = s.products_id 
   and p.master_categories_id = 0;
Obviously if your tables have a prefix, you must include this (use "zen_products p, zen_products_to_categories s" if your prefix is "zen_" for instance).

There is a facility within Zen Cart to fix this problem. Back up your database, then go to Admin -> Tools -> Store Manager, and click on "Reset ALL Products Master Categories ID". Note that this will use the first category id used if you have Linked Products, which may not be what you want; you may need to use phpMyAdmin and fix this problem on a case by case basis if you use Linked Products.

Once you update master categories, you must also run the Products Price Sorter, which is also under Store Manager.

Determining master and parent categories

This process can be tricky because of linked categories, so I added a tool to my downloads area called product cat info, which will print out information for a product showing what category ids can be used to select this product with which mods.

Category Handling in Mods

In all mods, the "category" is the master category. The difference between the free mods and the commercial mods is whether you can chain back up the category list or whether you are restricted to only the direct ancestor.

In the free mods - Better Together, Quantity Discounts, Newsletter Discount, Military Discount and Gift Wrap at Checkout - the "category" that is used is the master category, which will be the parent category for all but linked products. For linked products, it will be the parent category of the original product which was linked. The master category is not the top level category when subcategories are in use.
Men's Clothing 
     |
     ---->  Shirts
            |
            -------> shirt A 
                     shirt B
                     shirt C 
In this example, (assuming no linked products), the parent category of "shirt A" is "Shirts," not "Men's Clothing." "Mens' Clothing" would be considered the top level category.

My commercial mods - Combination Discounts, Big Chooser, Free Gift Chooser, Big Spender, Chooser AutoCoupon and Spender AutoCoupon - can use categories at any level, not just direct ancestor (parent) categories. The master category is still used to determine parent category, but you can use the parent of that category as well, all the way back to the top level category.

Men's Clothing (category 3)
     |
     ---->  Shirts (category 5)
     |      |
     |      -------> shirt A 
     |               shirt B
     |               shirt C 
     ---->  Pants (category 6)
     |      |
     |      -------> pants A 
     |               pants B
     |               pants C 
     ---->  Shoes (category 7) 
            |
            --->  Dress Shoes (category 12) 
            |     |
            |     -------> dress shoes A 
            |              dress shoes B
            |              dress shoes C 
            --->  Casual Shoes (category 18) 
                  |
                  -------> casual shoes A 
                           casual shoes B
                           casual shoes C 

Specifying "Men's Clothing" as a category in one of the commercial mods will include all items in Shirts, Pants and Shoes.

Specifying "Shoes" (CAT 7) as a category will include both dress shoes and casual shoes.

Quantity Discounts can be modified to use top level categories instead of parent categories, but it can only use one level (i.e. either parent or top level); if you need to group by different levels depending on product, you need to use Combination Discounts or Big Chooser.

Here's an example of a situation where parent category and master category are not the same. Consider this list of products, all non-linked:

Men's Clothing (category 3)
     |
     ---->  Shirts (category 5)
     |      |
     |      -------> shirt 10
     |               shirt 11 
     |               shirt 12 
Formal Men's Clothing (category 8)
     |
     ---->  Shirts (category 9)
     |      |
     |      -------> shirt 20
     |               shirt 21 
     |               shirt 22 


If someone wanted to create a linked product out of shirt 20 so that it would display under category 5, it would look like this:

Men's Clothing (category 3)
     |
     ---->  Shirts (category 5)
     |      |
     |      -------> shirt 10
     |               shirt 11 
     |               shirt 12 
     |               shirt 20 (linked)
Formal Men's Clothing (category 8)
     |
     ---->  Shirts (category 9)
     |      |
     |      -------> shirt 20
     |               shirt 21 
     |               shirt 22 


Shirt 20 would still only match CAT 9 for Better Together, and CAT 9 or 8 for the commercial mods. The master category - the parent category of the original product id - is the one which is used. CAT 5 and 3 would not match for shirt 20.

How to tell if your stock is organized into subcategories

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.

Marketing Text and Linked Items in Better Together and Friends

If you are using the Marketing text feature in Better Together, Combination Discounts, Big Chooser or Free Gift Spender or Free Gift Chooser or Big Spender, and you have linked products in a category that is discounted, you will see misleading marketing text messages when you view those items.

The fix in those modules is done by editing the appropriate marketing file (tpl_better_together_marketing.php, tpl_combination_discounts_marketing.php, tpl_big_chooser_marketing.php, tpl_free_gift_chooser_marketing.php or tpl_bigspender_marketing.php).

Look for the line that says
if ($discount->check() > 0) {
Then make two changes:
  1. Insert the following two lines below this line:
         $product_to_categories = $db->Execute("select master_categories_id from " . 
           TABLE_PRODUCTS . " where products_id = '" . (int)$_GET['products_id'] . "'");
         $category = $product_to_categories->fields['master_categories_id'];
    
  2. Change all instances of the string "$current_category_id" to "$category".
This change is not required unless you have linked products in a category which is discounted.

Marketing Text and Linked Items in Quantity Discounts

If you are using the Marketing text feature in Quantity Discounts, and you have linked products in a category that is discounted, and you are using one of the category exits (i.e. exclude_category() or apply_special_category_discount()), you will see misleading marketing text messages when you view those items.

The fix in those modules is done by editing tpl_product_info_display where you put the marketing text logic.

Look for the line that says
if ($discount->check() > 0) {
Then make two changes:
  1. Insert the following two lines below this line:
         $product_to_categories = $db->Execute("select master_categories_id from " . 
           TABLE_PRODUCTS . " where products_id = '" . (int)$_GET['products_id'] . "'");
         $category = $product_to_categories->fields['master_categories_id'];
    
  2. Change all instances of the string "$current_category_id" to "$category".
Of course, you will also need to add logic to the get_*() method you are using from Quantity Discounts to reflect your category exceptions.

This change is not required unless you have linked products in a category which is discounted, and you're discounting in different ways for different categories.

Marketing Text when User Exits are used

If you are using the Marketing text feature in one of my mods, but you have done an exclusion via a user exit (for example, exclude_category() in Quantity Discounts or BOGO), the marketing text doesn't know about this- you will need to add code in to handle it.

This is done by editing tpl_product_info_display where you put the marketing text logic.

Look for the line that says
if ($discount->check() > 0) {
Then right above it add:
     $product_to_categories = $db->Execute("select master_categories_id from " . 
       TABLE_PRODUCTS . " where products_id = '" . (int)$_GET['products_id'] . "'");
     $category = $product_to_categories->fields['master_categories_id'];
     $product_discountable = !($this->exclude_category($category)); 
and change
if ($discount->check() > 0) {
to
if ($product_discountable && $discount->check() > 0) {


Custom Logic with Categories

Ideas on how to configure my modules programmatically are given in the Advanced Discount Configuration for Zen Cart page.

Conditions and Discounted Items with the same Category

Having overlapping category specifications for the conditions and discounting issues can cause issues.

This applies to Better Together for Zen Cart, Combination Discounts for Zen Cart, Big Spender for Zen Cart, Big Chooser for Zen Cart, Free Gift Chooser for Zen Cart, and Free Gift Spender for Zen Cart.

Suppose you want to do a discount like, "Buy 3 items from category 1, get item 12 (also from category 1) free."

This will work fine as long as item 12 is the lowest priced item in category 1. Why? Because when choosing conditions, my mods go from highest priced item to lowest priced item, and when choosing discounts, they go from the lowest priced item to the highest priced item. So suppose the following items are in your cart:
item 8 (category 1): $20.00 
item 9 (category 1): $15.00 
item 10 (category 1): $14.00
item 12 (category 1) $25.00 
My attempt to satisfy the condition (buy 3 from category 1) with the most expensive items, which are item 12, item 8 and item 9. But since item 12 is a condition, it's not available for discounting, so you won't get the discount.

There are a few ways to solve this problem.
  • The most obvious technique, and the one I start with, is to ensure that if you are using the same category for discount conditions and discounting a specific item, make the item the least expensive in the category. So reduce the price of item 12 to $13.99 or less, depending on the lowest price of other items in category 1. Note: this will work even if the price is just one penny less than the other items.
  • If you are using Big Chooser 1.2.16 or higher, you may use the set_negative_constraint() command, which removes the item from consideration as a constraint but still allows it to be discounted.
  • You can move the discounted item to another category. Be aware of the handling of parent categories in the mod you are using, which are discussed above.
  • Sneaky solution: You can modify the master_categories_id of the product you are discounting to 0. It will still appears in category listings, but the discounting behavior of my mods (which is driven by this field) will be changed. Just remember that if you do a Reset Master Categories ID you will lose this setting.

Version issues with Categories

  • You MUST be running Zen Cart 1.3.5 or higher to use category functions in these modules.
  • Is the file includes/classes/shopping_cart.php identical to the copy in the distribution of Zen Cart? Some people have modified this file from an older version of Zen Cart and not merged with more recent changes made by the core team.
  • If you are using Better Together add_twoforone_cat() you must upgrade to Better Together 1.5 or higher, and you must install the language file as is. You may of course edit this file after installation, but do not simply merge your old language file; prior versions had a bad definition which broke add_twoforone_cat(); prior versions also had other inconsistencies and errors.