See posts about...

My other sites...

Drupal Commerce Alpha 4

Well, Christmas is already over for a lot of you, but there's still an hour left here in the States for me to wish everyone a Merry Christmas with a Drupal Commerce Alpha 4 release. I drove down to Louisiana to celebrate with my wife's family, and for once I took the passenger seat and got a good bit of work in to tighten up our Entity / Rules integration. Éowyn had a meltdown after 8 hours, too, so thanks to the Super 8 Motel in Wheatley, AR, I made even more headway on the dynamic Add to Cart form and its dependent attribute display.

However, integration and maintenance issues aren't the most exciting things in this release. I'm much more excited to announce the extensive work done on Drupal Commerce's Rules powered dynamic pricing system. We now have a centralized system empowering site builders and store administrators to implement pricing rules for discounts, tax calculation and display, and currency conversion through the user interface. While some of the subsystems still need attention, the Rules are in and working on the Calculating the sell price of a product event. You can see the results immediately on product field displays and in the shopping cart.

For example, I might want to give members to my Wombat Dating site a 10% discount. Never mind the absurdity of the scenario (if that's even possible) and see the basic product display node showing the product image and price fields:

The price field is displayed with the Formatted amount display formatter, which knows that modules are enabled that allow for dynamic price calculation:

This particular calculation option allows product sell prices to be calculated through Rules, so I've setup a 10% Members Discount using this simple rule configuration:

The end result, as you would expect, is a much cheaper night out on the town with our lovely wombat:

This system is a huge step forward from what we attempted in Ubercart, which lacked a baked in UI, touched every price on the site, and involved a pretty heavy caching system that couldn't address the problem of executing queries based on calculated prices. The system eventually got the boot, but we got a chance to tackle the challenges afresh with Drupal Commerce. Through some good discussion at DrupalCon Copenhagen with Damien, Miro Dietiker, and others, we realized the problem was actually in how we understood our dynamic pricing needs.

The first thing we did was restrict our initial scope to calculating product sell prices. That solved a lot of performance problems and API confusion on its own. However, we still had to solve the querying problem since we're depending on Rules to perform the calculations. This means we require PHP to execute to find the actual sell price of a product for any given customer. Simple queries that order and filter lists of products based on the sell price would be inaccurate once the price was altered for display, so a customer with a special discount on a product won't see it at the top of a catalog View when he orders by price. We've run into this problem several times at Commerce Guys, especially when displaying prices with tax included.

What we determined was that we needed to pre-calculate dynamic prices so the data would exist in the database before a customer ever needed it. Since we know the Rules attached to the price calculation event, we can pre-calculate prices for every combination of applicable Rules. The pre-calculated price data can then be joined into queries for any given set of Rules, enabling ordering, filtering, and faceting to work with actual sell prices instead of just the base price stored on the field. There are some guidelines to follow when constructing Rules for pre-calculation, and we've also devised a few simple ways for larger sites to keep the calculated price table from breaching epic row counts.

If I lost you, my apologies. It's very exciting stuff for the project, and I tend to talk too much about it. I've already explained it to all of my in-laws, including my 75 year-old grandfather-in-law whom I'll be teaching Drupal while I'm down here for his personal website.

I'll be blogging more about the dynamic price pre-calculation system, especially once it gets an actual user interface. For now it's a small API and a database table, but it's itching to be put through its paces. I'll be testing it first with Views and welcome additional eyes on the code. For more information on how it functions, refer to the development specification and the copious amount of comments on the pertinent functions in the Product Reference module.

Anyways, I've been closeted up with the laptop enough for one Christmas, so I'm going to join the rest of the family with some doctored eggnog and a seat in front of the fire. I hope you all had a Merry Christmas, and I look forward to feedback on the new hotness in Alpha 4.

Check out the release notes for the full changelog since Alpha 3, If you're thinking of resolving to visit Paris for the New Year, check out the details on our Paris Commerce Sprint, January 17-21, 2011 and consider joining us in Paris to push this code from a planned beta to 1.0 in time for DrupalCon Chicago. We'd love to have you.

You've hit the nail on the head, Tom, but even underestimated it. By my calculations, it's quite feasible for a medium sized store to end up with millions of rows in the table if they're not careful.

The trick is going to be mitigating how many Rules get considered in pre-calculating prices. The number of rows will grow exponentially with each additional Rule that gets considered, as I calculate the number of possible prices for a product by raising 2 to the power of X - where X is the number of applicable Rules. For example, if I have two Rules, one that offers a membership discount based on role and one that offers an early-bird discount based on the date, there will be 2^2 possible calculations for the sell price of a particular product:

Neither discount applies.

The membership discount applies but the early-bird date has passed.

The user doesn't have the membership role but the early-bird discount is still available.

Both discounts apply.

When I pre-calculate prices, I have to take every possible combination into account and store the calculated price by executing the actions for each applicable Rule. When I want to fetch a product's pre-calculated price, I determine which Rules apply in the given context (i.e. 1, 2, 3, or 4 above) and then join to the price table using a key that identifies the particular combination of applicable Rules.

I'll follow-up with more detail, as it's actually been a fun problem to work on. The gist of the optimization, though, is that we don't need to consider every pricing Rule when pre-calculating, because some will always apply (i.e. Rules with no conditions will always alter the price a particular way) and some will always apply together (i.e. multiple Rules with the same exact conditions). By bypassing these Rules and then enforcing guidelines for what arguments can be used in the conditions / actions of the Rules we do consider, we can mitigate the ginormous row count.

It'll take some more thinking, though, and it certainly won't be as simple as it could be from the casual user's perspective. The worst case scenario, of course, is that someone just won't be able to use the pre-calculation system for their Views or facets, so we'll just have to find an alternate solution when that happens.

I think the basic idea is a display formatter could be used to show the original price stroke through with the calculated price beside it. Right now there isn't a core accommodation for this, but it really would be just a few minutes' work if you know how to write a field display formatter.

One really hopes you will build this formatter either into core or as an add on module that simply works out of the box. This is a huge problem in Ubercart with literally dozens of (half-baked?) solutions.