Updating Completed Order Timestamps in Drupal Commerce

One of the things I'll miss about Louisville, KY when I move my family to Greenville, SC next month is the buying club we're a part of that sources raw / organic foods and other products from local farms and other good companies. It was put together by a genius I knew through college and church (John Moody, who writes and speaks about food clubs and co-ops), and last year another college buddy (Kane Holbrook) started working with him to help manage the club and oversee deliveries and pick-ups. Among the many yummy things we get through the club are a variety of raw milk cheeses from Welsh Mountain Farms, an Amish farm in Lancaster County, Pennsylvania.

It just so happens that this farmer, yea, though he has no internet access himself, understands the advantages of selling online and asked Kane last year about starting a website to sell his cheese. Kane floated the idea by me, and it quickly became a business plan and partnership to start selling his cheese using Drupal Commerce. Not only did it help the farmer and open a door for Kane and I to do some business together, but it also finally gave me a chance to experience using Drupal Commerce as a site owner and administrator. After two years of planning and development, I finally started eating my own dog food when we launched RealMilkCheese.com in November.

I'll have more to write about the site in the future, as I learned a lot through it and contributed a lot of code from the project to drupal.org. It became the driving factor behind my development of the flat rate shipping module and UI improvements for on-site order management in Drupal Commerce 1.2. It's also the primary example I use to demonstrate multiple flat rate shipping options with conditional availability (think free shipping on orders over a certain amount) and custom discounts to products through an integration with quicksketch's Facebook OAuth module. It was my second foray into theming with Omega and turned up a few tips that I need to pass on to other theme developers with respect to Drupal Commerce components. Finally, it gave me my first experience launching a site on Acquia Dev Cloud using one of those handy coupons they give out at Drupal events.

But the primary purpose for this blog post is to highlight one specific difficulty we encountered while administering orders and the simple solution I put in place to resolve it. In Drupal Commerce, the shopping cart is simply an order in a special status that indicates it's "in progress" and therefore needs to be continually updated to reflect current product prices and availability. By default an authenticated shopping cart order (i.e. for a logged in user) may exist indefinitely until the user finally completes checkout for the order. (There's an issue to expire them through Rules if you're interested in reviewing it. )

We encountered a scenario where a customer added a product to his shopping cart the first week of December but didn't actually purchase the cheese until we sent out a special offer over the holidays for a free 8 oz. cheddar for orders placed in a certain timeframe. It was great to see the immediate effectiveness of the offer (recovered cart sales are a big deal), but because the Orders View on the back-end sorted orders in reverse order by creation timestamp, his order appeared down the page below orders that had already been shipped and marked as Completed. It's not a huge problem, because a filter on the View to only show Pending orders by default would highlight orders needing attention, but it still isn't an ideal user experience.

The thought occurred to me that for our scenario, it would be fine for the creation timestamp of an order to be reset to the current timestamp on checkout completion. As far as we're concerned, that's when an order has been "created" that we actually need to respond to. It actually existed before then, and we'll retain that data in the order's revision log, but we really want to know when the order was finally submitted through a complete checkout process. I started imagining where I would put code to do this when I realized I wouldn't do that at all... a simple Rule would do the trick!

This Rule reacts to the event "Completing the checkout process" and includes a single "Set a data value" action to update the created timestamp to the current time. Because every order save triggers a new revision, we'll have the historical creation date if we want it, but the date that matters to the customer and to administrators (i.e. the date of checkout completion) will now appear properly. If you need the same behavior, you can import the following Rule:

{ "rules_update_the_created_timestamp_to_now" : {
    "LABEL" : "Update the created timestamp to now",
    "PLUGIN" : "reaction rule",
    "REQUIRES" : [ "rules", "commerce_checkout" ],
    "ON" : [ "commerce_checkout_complete" ],
    "DO" : [
      { "data_set" : { "data" : [ "commerce-order:created" ], "value" : "now" } }

Now, I should be used to the level of flexibility we achieved in Drupal Commerce by now, but it still tickles me to no end that it really is that simple to change even obscure aspects of your e-commerce system through the user interface. When I communicate the strengths of the project as an eCommerce framework, concrete examples like this make the approach come to life for merchants and store administrators. It's a clear case of the software conforming to your business needs instead of forcing you to work within its constraints, and I look forward to seeing how else we can trick it out to grow our little cheese business.

Destination Training: Drupal Commerce in Paris and Denver

It's no secret that I love to travel (in appropriate doses) and meet new people, especially those doing e-commerce on Drupal. I love visiting the larger cities in the States, and I love even more visiting the older cities in Europe. Fortunately for me, these are the places that draw large Drupal events and make the most sense for Commerce Guys to put on Drupal Commerce trainings.

I'm currently wrapping up the final commits for a Drupal Commerce 1.2 release this week and will then start revising our training curriculum to accommodate a few recent UI improvements. Drupal Commerce is growing in popularity as an e-commerce framework, particularly among tech savvy developers and organizations (we still need to cater better to those with limited resources and Drupal skills), highlighting the need for better "self-help" documentation and resources and the continued need for personal training from expert users and developers.

I'll be involved in a couple of trainings in the next couple of months, and I hope there will be even more that I don't have to attend. (Unless of course they're in cities that are too good to pass up. )

If you have a project about to start or have recently been handed a site to maintain, consider one of these upcoming opportunities:

  • Drupal Commerce Developer Training in Paris
    January 31 - February 2, 2012

    Paris is both my favorite city to visit and home to my favorite company to work for - what luck! We'll be presenting a three day developer training including personal instruction from myself and a few other members of our team. As my French knowledge goes about as far as a 1 year old's, and since we're expecting attendees from a variety of countries, rest assured this training is in English.
  • Drupal Commerce Site Building Training at DrupalCon Denver
    March 19, 2012

    This will be our regular one day extravaganza where we introduce all that Drupal Commerce can do for you and demonstrate how to build a store using the core components and a few essential contributed modules. We'll provide a sandbox server so you can follow along and take your site with you when you go.

I've also recently put in work with Kent Bye of Lullabot to turn that one day course into a video series for Drupalize.me. We basically put down about four hours of video demonstrating how to build a site like my RealMilkCheese.com - no code required for a simple catalog, payment via PayPal, flat rate shipping, and a couple types of discounts. I'm looking forward to seeing it edited to a fine polish - Kent's a star at that stuff.

My Disappointment with Google+

When Google+ first arrived, I jumped on the bandwagon very quickly and looked forward to moving my social activity over from Facebook to the new service. I'm a longtime Google fan, and their new aesthetic really appeals to me. Moreover, their concept of Circles is exactly what I wanted out of a single social sharing system.

I have three very distinct groups of people I interact with online, and rarely do these groups of people intersect. I share things with friends and family, with the Drupal community, and with indie game developers / enthusiasts. Until now, I've interacted with the first group over Facebook, the second over Twitter (and on drupal.org), and the third on a variety of blogs (including my sparsely updated Rogue Wombat.

Rarely do I need or want to share something with all three groups of people. This means that friends and family / indie game developers and enthusiasts are likely to be annoyed by my constant stream of Drupal updates on Twitter. It also means I don't "friend" a lot of people on Facebook from the Drupal community unless I actually consider them people who might actually care about seeing notes about my adventures in parenting.

Out of nowhere Google+ comes along with the promise of being able to categorize everyone into one of these three circles and share appropriate updates with people in the different circles all through one service. It was too good to be true - in fact, as I said above it was exactly what I wanted at the time as Facebook's lists and default status update settings had really failed me.

So, what did I do? I set about adding the first couple hundred people I knew to my Drupal / Friends and Family circles. It was great - I could target updates appropriately and not worry about turning anyone off or sharing personal family photos with the internet at large. However, as my circles started filling up, my Google+ stream suddenly became nothing but noise. Any time I went to look at Google+, I saw posts in languages I didn't know from people who obviously weren't practicing selective sharing themselves.

The problem, though, is that I have to put someone into my Drupal circle if I want them to see my Drupal updates. In other words, I am forced to practice a "follow back" policy on Google+ if I want to target my updates, a practice which I stay well away from (e.g. I follow a mere 88 accounts on Twitter compared with the 2,461 that follow me). I just don't like a noisy stream - I only want to see posts I care about, but those posts could come from any of my main circle areas.

So, what to do? I could just make all my Drupal posts Public so I'm not forced to follow anyone random Drupaller back just so they can see relevant information from my account. However, then anyone I connect with for indie gaming and my family and friends will then see my Drupal posts, which will be uninteresting to them and create a bad experience for them on Google+. I don't want to do this to them.

A better suggestion I heard from Matt Butcher was that Google+ should allow you to make a Circle public so others can subscribe to it. Drupallers can just follow my Drupal posts and I don't even have to know about it, and I'd be free to follow those Drupallers whose posts I want to see in my stream. I really like this idea, though for recommendation and statistical purposes, it would still be useful to have those people taken into account even if I don't add them back.

I could just not use the stream any more - but that's always going to be the first thing I see when I open Google+. If that's the first thing I see, I'm not likely to go there any more. I can go see relevant posts on Facebook without any "work" (though lately I have to constantly re-sort my news feed to show recent posts first...). I could make yet another circle of people whose updates I want to read and just always switch to it, but that's always going to be my entire Friends and Family / Indie Gaming circles, so why should I have to maintain yet another circle just to make my stream interesting?

So, that sums up my disappointment with Google+. I really wanted it to be a platform for me to share targeted status updates with friends and Drupallers from around the world, but I can't do that and still make it useful for me. That means I rarely hop in there except to add someone back or post a quick status update. And if I'm not feeling the need to check it continually throughout the day, I'm much less likely to go to it to post updates - even with the status update box conveniently integrated into every Google service.

As much as I want to switch, I'm still mostly just on Twitter / Facebook and don't know when I'll be able to move further into Google+. Against the day that Google sorts this out, feel free to add me to your circles and I might just add you to mine. Wink

Shipping and product line item types in Drupal Commerce

In Drupal Commerce, a line item is any item added to an order that affects the order total. They represent products, shipping rates, certain types of discounts, and more. During sell price calculation for products and rate calculation for shipping, you actually apply discounts, fees, and tax rates as manipulations to the unit price of line items.

I've been dealing a lot with line items during a personal shipping sprint I've been on the last week and a half to help launch a client site. That work has involved a complete rewrite of Commerce Shipping in the 2.x branch to support carrier calculated rates (such as those returned by ConnectShip, UPS, FedEx, etc.). That work in turn has resulted in the release of a Physical Fields module that defines weight and dimensions fields and a Commerce Physical Product module that provides API support for determining when orders contain shippable products, the total weight of the order, and other useful things.

Once this site launches, I'll give more attention to the upgrade path for folks using Shipping 1.x to move up to Shipping 2.x. (I'll also need to rewrite the flat rate shipping method to go along with it.) To push forward integration with different carriers, we'll be holding a Shipping Sprint at DrupalCamp Atlanta, fittingly the corporate home of UPS.

During the course of my work on Commerce Physical Product, I was reminded that determining whether or not a line item represents a product isn't as straightforward as you would first think. It's nothing terribly tricky, but you can't just perform a simple check on the product type to reliably determine if a line item represents a product.

This is insufficient:

if ($line_item->type == 'product') {
// ...

The reason is Drupal Commerce allows you to have multiple product line item types, each with their own unique fields that may be exposed through the Add to Cart form. This feature lets you sell customizable products where the various customizations are stored with the product reference in the line item.

A simple use case is adding a custom price field to a donation product line item type allowing customers to tell you how much they want to donate to a specific campaign. This can all be configured through the Field UI and Rules, mitigating the need for modules like UC Variable Price. Trés cool.

Since you can have multiple product line item types, we provide a helper function that returns an array of the names of every product line item type defined on the site. Instead of the code above, you can perform an in_array() check on the type of a line item to see if it is of any of the valid product line item types:

if (in_array($line_item->type, commerce_product_line_item_types())) {
// ...

Any time you're writing code that should apply to a product line item, be sure to use this method instead of the first. Hope that helps you as you work on your Drupal Commerce contribs and custom modules. I almost forgot about this when writing Commerce Physical Product. Granted, I haven't been sleeping much recently, but if it's that easy for me to forget, I bet it's a bit unknown to the general public.