Dynamic Price Pre-calculation

We've specified early on that we will be using Rules for dynamic pricing, which incorporates things like taxes, discounts, price lists, etc. What we realized is that where this really matters - in fact, the only place we're targeting dynamic pricing for 1.0 - is when determining the purchase price of a product for a particular customer. Customers need to know when they view a product page, a catalog listing, the shopping cart, etc. what the price is for the product they want to buy. After this time, i.e. once an order is complete, the prices for products on the order will be static.

The problem this creates is that allowing dynamic pricing through the application (i.e. Rules) breaks our ability to order / filter products in database query result sets based on the actual price a customer sees. To accomplish this, we've talked for some time about a price pre-calculation mechanism whereby variations of the price are stored in a separate table in the database so the altered price values can be joined into the query. Notice this isn't caching, as we actually need to pre-populate the table with all the data available before we can generate a page depending on that data.

When we populate the table with data, we don't actually know the context in which the product's price will be displayed. For example, we don't know the date (for stores that have limited time offers) or the eventual customer's user account (for stores with role based discounts). What we do know is the actual product whose prices we're calculating and which Rules might alter its price.

Conversely when we want to fetch the pre-calculated prices into the result set of a query, we do know the current context (i.e. the user, the day, the contents of the user's shopping cart) but don't know what products will end up in that result set. We also know precisely which Rules will be applied in the current context. Therefore, when we join the result set of our query to the price table it will have to be joined on product ID and filtered by a field containing the applicable Rules.

Because of the data constraints described above, price altering Rules must meet the following criteria to be eligible for dynamic price pre-calculation:

  • The conditions of the Rule must not depend on the product whose price is being altered.
  • The actions of the Rule must only depend on the product whose price is being altered.

For example, a conference registration site might sell three types of admission to their conference. For early bird registrants, they discount the price 10%. Additionally, for repeat attendees, they offer an additional 5% off. To configure these discounts, the site would use the following two Rules:

  1. [Condition] If the current date [Action] apply a 10% discount.
  2. [Condition] If the user has the "past attendee" role,
    [Action] apply a 5% discount.

To pre-calculate these prices, we would first determine that these two Rules are the only applicable pricing Rules. Then for every possible combination of applicable Rules (i.e. 1 applies, 2 does not; 1 and 2 apply; 1 does not, 2 does) we would loop over the available products and execute the actions for the applicable Rules to arrive at the purchase price for customers satisfying any combination of the discount Rules. These values would be stored in the database

This introduces a problem, though - sometimes the discount needs to depend on information on the product itself. For example, it's quite likely this conference website will sell other types of products besides the actual event registrations - perhaps some swag or meal / lodging tickets. Those wouldn't be discounted, but we can't add conditions to the discount Rules to check for the type of product.

Fortunately, Rules 2 lets us use "components" that operate as subroutines within the actions of a Rule. A component can have its own conditions and actions just like the discount Rule itself, so we can still use conditions that check the type of product before applying a discount. The Rules would read instead like this:

  1. [Condition] If the current date [Action] (if the product type is registration, apply a 10% discount).
  2. [Condition] If the user has the "past attendee" role,
    [Action] (if the product type is registration, apply a 5% discount).

This means we will still have a row in the price table even for products that don't necessarily change for a given set of Rules, but that's ok - we will have a complete data set that still allows for product specific pricing Rules. A View listing these prices would then be able to sort and filter based on the dynamic price instead of the product's base price.

It's worth noting that a site such as the one described probably wouldn't need to worry about pre-calculating prices. In fact, if the discounts are linear for every product like on this site, sorting or ordering even by the base price would work just fine. The pay off here will be seen on sites with larger product catalogs that have multiple Rules, different discount levels, combination purchase promotions (buy X and get 10% off of Y), etc.

Ryan Szrama
Posted: Dec 13, 2010


nightowl77 (not verified) on December 27, 2010


I'm a complete noob when it comes to Rules, so please ignore me if I misunderstand this particular problem, but I must point out this screencast by nodeone http://nodeone.se/blogg/learn-rules-with-nodeone-part-7-conditions-on-lo....

According to the screencast you can use Rulesets to get data to load earlier, so you can use that data as part of the if statement.

I could have this all wrong, it just so happened that I ran into this post and then ran into node one's screencast 30 minutes later. Hope it helps.

csdco on June 27, 2011

Wondering this too. How can you check product quantities with Rules? There doesn't seem to be any way to match Conditions against any product entities / quantities / selections.

minutiae on August 3, 2011

"for dynamic price pre-calculation:

The conditions of the Rule must not depend on the product whose price is being altered."

Curious if this statement still holds true with the RC release of DC?

If I'm selling tshirts and hats, I want to be able to provide a discount if X or more tshirts are in the cart ( not just that SKU, any tshirt in a taxonomy or similar. ) If the quoted statement still holds true, I'm guessing there is no way to accomplish this?

I've looked at the price table module, but I see that works from the line item, so there is no access to the order/cart, only that single line item. If someone can point me in a direction that can accomplish this, I'd be happy to work on a contributed module to accomplish Quantity breaks in this fashion. Thanks.