As 2019 draws to a close, it’s time for one last Commerce release. We’ve had a very busy month since the 2.15 release, and we’re excited to show you the results. This new release closes 40 issues thanks to the help of over 30 contributors!

Cart expiration

One powerful aspect of the Commerce architecture is that carts are stored as order entities, ready to be converted to a fully placed order with just a few field changes. However, customers often change their minds, leaving a site before completing (or even attempting) checkout. This can be a problem for high traffic websites where thousands of abandoned carts accumulate in the database in a short period of time.

Many sites attempt what’s called "abandoned cart recovery", notifying customers after a certain number of days using a module like Commerce Abandoned Carts. They might even offer a discount as an incentive to get the customer to come back. Whether you take that approach or not, it eventually makes sense to remove abandoned carts from the system.

In Drupal 7 this was the job of the Commerce Cart Expiration module. For Drupal 8, we’ve added this functionality to the core Commerce framework.

Cart expiration is configurable per order type. Each time cron runs, it will find carts that were abandoned for longer than the specified number of days and then queue them for deletion. Using Drupal’s Queue API, we then delete carts in groups of 50. Developers can subscribe to the ORDER_PREDELETE event to react to this deletion, which may be helpful to capture final information about the deleted carts in an analytics system.

Views integration

For historical reasons the Views module requires a different integration method for fields created via config vs. the base fields pre-defined on an entity type. The base field integration is underdeveloped and has been a source of bugs and missing features since the early Drupal 8 days.

For this release we attacked the problem head on.

First, we reviewed all open Views issues in the Commerce queue. Then we performed our own testing for every Commerce entity type, discovering several additional problems in the process. We ensured that each confirmed problem has a matching issue in the core issue queue. Finally, we wrote our own Views integration class (CommerceEntityViewsData) for the entire Commerce ecosystem to use. Over time we’ll work on moving this code into the Entity APi module and then further upstream.

What did we gain in the process?

Entity backreferences allow relating a user to their products or an order to its payments. Entity types with non-config bundles (payments, payment methods) can now have views. Mutli-value base fields (variations, order_items, stores) now have a “delta” filter. Address, Price, Date, State Machine, and List fields now have proper fields and filters. Several fixes to product variation views (link to the individual variation, view available operations).

Promotion start / end times

We have always allowed promotions to have a start date and an end date. We’ve now expanded this feature to also allow start and end times to be specified. This allows more granular control over promotion availability and removes ambiguity about when the promotion actually starts and ends.

Adding time fields forced us to confront one of our least favorite subjects as developers: timezones. When we say that a promotion ends at midnight, December 15th, whose midnight is that? The site’s? The customer’s?

Businesses tend to think about such times in their local timezone, and in the case of taxes, they are legally obligated to. If a VAT rate in France changes from 20% to 21% on January 1st, that rule applies based on a French store’s local time, not the time of an Australian visitor.

Complicating the situation, each Commerce site can have multiple stores. A Swedish store and a Finnish store are one hour apart, timezone-wise.

We solved the problem by adding a timezone field to the store entity. This way each store can control the timezone used for its promotion and tax dates. Promotion start and end times are stored as-is without conversion, with the store’s timezone only applied once known. This means that a promotion configured to start at “December 15th 00:00” will start at “December 15th 00:00” in the local time for customers belonging to both the Finland and Sweden stores.

Product specific tax rates

By default, Commerce always selects the standard tax rate for a resolved tax type and zone. Many sites need to use a reduced rate for certain products or apply no tax at all. Until now, this required writing a custom tax resolver.

Providing a user interface for this has long been a roadmap item for us. We’ve now developed the Commerce Product Tax module, which aims to solve this problem in contrib. We would like to see how the community uses it before considering it for inclusion in a future Commerce branch (e.g. 3.x).

Setup is as simple as creating a “Tax rate” on your product variation type. Try it out and let us know what you think!

Next steps

In 2020 we are officially shifting from a monthly to a quarterly release schedule. This means there will be a Commerce release once every 3 months. This cadence better matches the mature state of Commerce core.

Meanwhile, we will be concentrating on improving contrib. Our current focus is on Shipping, where we hope to have the next release ready by early January.