While many online stores, such as clothing retailers, tend to sell individual items there are those businesses that deal in part or even exclusively with bulk items instead. They might for instance have a product where the minimum amount that can be ordered is 100 units. Examples might include:
- Hardware stores selling pre-defined quantities of screws or nails
- Catering supply businesses selling large numbers of disposable knives and forks
- Other businesses selling mass produced items to retailers
In this tutorial I am going to look at a scenario where the merchant is using Shopp[1] to power their online store and where they have set up bulk items by leveraging product variants. To simulate this I created a product called Door Knockers which I am hypothetically selling to hardware stores, letting them order palettes loaded up with 100, 250 or 500 door knockers at a time.
That’s important because we aren’t letting customers specify arbitrary quantities in this scenario – we are only allowing them to buy 100, 250, 500 or whatever quantities we define.

An example I set up with a variant called Quantity, allowing door knockers to be ordered in quantities of 100, 250 or 500.
This is all very easy to set up. Simply create or edit the product in question then enable and set up a new variant (in my case I have called it Quantity) then name them accordingly. Simplicity is crucial for what we are going to do in this tutorial and here are the rules you will need to follow for every product where you set this up:
- For each product we are setting up like this we will create a variant called Quantity
- The options themselves (each reflecting a pre-defined quantity) should be named or labelled with a number – no letters or other special characters allowed
- So for a quantity of 100 items, “x100″ is a bad option name but “100″ is a good option name

Example of a “Quantity” variant where all of the option names are numbers
None of this is particularly revolutionary, it’s just a convenient way of selling bulk items in predefined quantities. As is normal, the greater the number of items purchased the greater the discount. Looking at my scenario for instance we are offering 100 door knockers for $800.00 (that’s $8 per knocker) and 500 door knockers for $2,000.00 (at this point we’re almost giving the things away at just $4 per knocker). At this point, let’s stop and look at the product on the main catalog page.

By default, variable products in Shopp have their price displayed as “From $xxx” on catalog and category views
In the context of catalog (and category) views the door knocker, as with other variable-price products, is displayed showing the least expensive option first – in this case, that’s 100 door knockers for $800.00. Our real objective here is to change this behaviour so that we see the lowest price per item ($4/knocker) instead. For all other products, we are just going to leave them untouched.
Let’s look at the category.php template and see how the price is generated.
<p class="price"> <?php shopp('product','saleprice','starting='.__('from','Shopp')); ?> </p>
Specifically we are interested in the Product Saleprice template tag. A neat thing about these tags is that they can all be filtered – and our filters can access the same information as the template tag code to make informed decisions. The logic we’re going to use in our filter is as follows:
- If there is no variant named Quantity (or just no variants at all) step back and return the default output
- Look at the price of every possible variant[2]
- Determine the price-per-unit (but only record the lowest or cheapest value)
- If we were able to determine that, return it – otherwise return the default output
And here is that logic translated into code:
/**
* Modify the saleprice text for products with a "Quantity" variant; all other
* products should have the saleprice returned without any changes.
*
* For relevant products, Quantity is assumed to contain prices with each label
* evaluating to an integer representing quantity:
*
* [Quantity [ 100 => 10.00, 250 = 20.00, 500 = 40.00, ... ] ]
*
* This will be used to return text representing the lowest price per unit.
*
* @param $result
* @param array $options
* @param Product $product
* @return string
*/
function custom_shopp_saleprice($result, array $options, Product $product) {
// Load variants and variant prices
$variants = shopp_product_variant_options($product->id);
$prices = shopp_product_variants($product->id);
// If no variants are set, or there is no "Quantity" variant, return the current result text
if (!$variants or !isset($variants['Quantity'])) return $result;
// Iterate through the variant prices and establish the lowest price-per-unit
foreach ($prices as $variantPrice) {
// Promoprice ought to be the current lowest price (even if there is no promo in effect)
$cost = $variantPrice->promoprice;
$quantity = absint($variantPrice->label);
// Avoid scenarios where quantity is zero
if ($quantity !== 0) {
// Determine the price per unit - record the lowest value as $perUnit
$variantPricePerUnit = $cost / $quantity;
if (!isset($perUnit) or $variantPricePerUnit < $perUnit)
$perUnit = $variantPricePerUnit;
}
}
// Unable to determine a price-per-unit? Return the original result
if (!isset($perUnit)) return $result;
// Format and return the price-per-unit
return sprintf('%s per item', money($perUnit));
}
/**
* Setup our filter. We set the priority to 20 to make it run after Shopp's
* own template tag code and the number of args to 3 because we want to pass
* in the Product object.
*/
add_filter('shopp_tag_product_saleprice', 'custom_shopp_saleprice', 20, 3);
This can be placed inside your theme’s functions.php file (or in a plugin if you know what you are doing) and, if you need to remove it in the future, then you can simply delete it or comment it out – because it only filters the output of the Product Saleprice tag (it doesn’t change anything in the database) this is quite safe.

The end result. Door knockers and another product with a “Quantity” variant have the prices-per-unit displayed appropriately, other products are unaffected.
Any questions or problems, please just let me know in the comments.
Notes
- In this tutorial I am working with Shopp 1.2.5.
- We look at every variant price because we have to – if there are three variables (quantity, colour and size) each with 4 different options then there are 64 different possible prices even though there are only 4 quantities. With just a Quantity and a Color variant for example it’s easy to image an impact on per-unit pricing, such as if black door knockers are cheaper to produce than bronze-effect knockers.
- The saleprice template tag options are fully respected only if the default output is returned. The approach used here for instance doesn’t consider taxation – but that capability could certainly be added.
- This post was requested and generously sponsored by Josh at Ident-a-Kid Services of America
Freshly Baked 
Nicely simple solution. I did something similar with a core mod on an old project and must revisit it and do it this way! Thanks.
Hey Ben! Glad you like it.