Development

Free Shipping for Specific Products in Drupal Commerce

Recently I had a client call me asking if she could offer free shipping on a specific promotional/limited-run product. Unfortunately I didn’t find any clear-cut solutions in contrib or Drupal Commerce core. Also, I could not come up with a way to do this in core through the Drupal admin UI. It’s very easy to allow a “Free” shipping method if a specific product is found in the cart/order, but we only wanted the “free” deal to apply to the single product (any quantity of it).

Disclaimer: the following only handles a single product for free-shipping… it will require modification and some thought to make work for multiple free-shippable products. I provide some ideas throughout the post.

What I ended up doing may leave a sour taste in your mouth, but I’ve got to tell you it’s been working really well, and uses only a few hooks in a custom module in tandem with a separate free shipping method specific to the product. I understand this doesn’t solve the problem (easily) for the masses, but I simply didn’t have the time to write a module or patch modules to make this happen.

Methodology: We’re using Commerce UPS and Flat Rate shipping methods. How might we make a single line item within a “shipment” free? Well, I gave this some thought and came up with the idea that, well, if a product doesn’t have a weight or dimensions, it should not affect the shipping quote that UPS provides. I began browsing the code and traced down a few hooks that would let me alter line item weights and dimensions on-the-fly. Now, this can obviously impact more than just the shipping calculations, so please test this out should you decide to implement similar functionality. You’ll read more disclaimers as you proceed. This is by no means a solution for the masses.

The only hiccup I had after writing the code was that if the order contained ONLY the “free-shippable” product than Commerce UPS wouldn’t return any shipping costs, which would prevent users from checking out (because it’s the only shipping method on the site and I have the “Require a shipping service at all times, preventing checkout if none are available.” option enabled). The simple solution to this was to create a new Flat rate shipping method that would only be available if the cart contained ONLY the “free” product.

I understand this is NOT a very portable/reusable solution, but I’m throwing it out there because it may help someone with more free time (and/or ambition) than me build something more robust, or it might get the thought train rolling.

So, let’s cover the free shipping method first. Using the Flat Rate shipping method you can create a new service. I called mine “Free shipping for the GX1234”. After you create the new shipping service you can add a new condition (Execute custom PHP code) to the component. Here’s the function I came up with (explained in its documentation) to check if the specific product was the only product in the cart. I put this in my custom module’s .module file. Note that this requires, among other things, the entity api module.

At this point, and after a cache clear, you should be able to setup your “Execute custom PHP code” to use a php value of:

For this particular site I also added two conditions in an OR group that check the shipping address country to make sure it’s either US or CA. (using Order address component comparison” conditions)

If you’ve been following along, at this point you should be able to choose this free shipping method if you add only that specific product to your cart. If it’s not working, re-read and make sure you haven’t missed anything. If it still doesn’t work, leave a comment or email me, though I may be slow to respond.

So we’ve covered orders containing only that specific product, but we also need to exclude this product from shipping cost calculations if there are other products in the cart. The Commerce UPS module builds a request that it sends to UPS. During the processing it asks Commerce Physical for the dimensions and weights of each line item. Commerce Physical has a nice set of API hooks to let developers alter the physical properties of line items on-the-fly.

Another disclaimer: I had to get this out the door quickly. As I’m writing this post I have thought of a few cleaner ways to handle this piece, though they would require modifications to Commerce UPS. Wouldn’t it be awesome if we had “remove line item from shipping calculations” or “modify physical properties of line item” rule events?

Anyhow, these hook implementations are pretty self-explanatory. The first function relies on the function we created earlier, as you can see.

Please take the time to read through this code. You’ll notice I also have a check in there to make sure the order is US or Canada.

So, that’s it. It really boils down to a small amount of code and a flat rate shipping service. There ARE, without question, better ways to do this. If I have to do it again (or have to add an additional free product to this site) I will rework my solution to provide something for the community. I like the rules ideas (see a few paragraphs back…) I came up with, but I haven’t given them too much thought. Another nice option would be a simple checkbox on products to make them “ship for free”, though the issue there is that you couldn’t target only specific countries (just as an example). Rules is probably the next step for all of this, unless someone wants to make a full-blown module.

2 Comments

  • dinesh kashera

    i want to create a custom module where we get the product dimension (length,width,heigh) ,product weight,and variable, and then calculate the dim weight,and on the basis of dim weight and product weight it create the shipping charge ,How to start for this in drupal commerce 7

  • Guilord

    I was very disappointed to find that the speical free shipping offer disapeared when I tried to used my 10% off voucher code, if I had used the offer code it was more expensive than not using and getting free shipping.10% less than happy!Jim

Leave a Reply

Your email address will not be published.