Discussions

Orders, Line Items, and the Cart

What follows is a rough summary of our conception of orders on Drupal 7. This brainstorming began in October at the first San Francisco sprint, and we spent a lot of time on Monday in Paris hashing out the details. There's still plenty more to consider, so feel free to speak up!

Order Summary

Like Products, Orders in Drupal Commerce are top-level fieldable entities. Unlike Products, we are initially supporting only a single Order Type, but this in no way limits the ability of contributed modules to alter in support for multiple Order Types.

The data inherent to the order object includes:

  • A serial numeric order ID as the primary key used internally (order_id).
  • An additional merchant-defined order ID (alphanumeric, pattern based - order_number), like product_id vs. sku.
  • Meta data including a creator_uid and customer_uid, created and changed timestamps, and a revision ID (revision_id instead of the confusing vid used by nodes).

We'll retain the concept of order states as used in Ubercart but possibly turn the order status into a field. This needs to be teased out further, but the basic idea is that order states are major workflow steps in the life of an order (i.e. cart / checkout -> post-checkout / paid -> fulfillment -> complete). Each of these major steps can have many components that are going to be custom to the store's particular workflow, but modules need to be able to depend on the presence of some major order categories. To that end, order states would continue to be defined via a hook_commerce_order_state().

Customer Information

Customer information will be added to the order via default fields. I'm undecided on the primary e-mail address, (which really only applies to anonymous orders), as there is no default e-mail Field and I'm not sure we want that as a dependency at this point. However, we'll be depending on the Postal Address developed according to the xNAL Name and Address standard to attach billing / shipping addresses to orders. By default, the order module will attach a Billing Address field, while a separate Shippable Products module will add the appropriate shipping related fields to products and orders (defaulting to a single shipping address). This separate module should also handle hiding shipping related fields in checkout / on order viewing / editing.

Line Items

Line items will be the heart of orders. We believe the best approach to be a new Line Item entity with multiple Line Item Types. Line items are anything on an order affecting the order total (e.g. products, taxes, shipping, discounts, fees, etc.). Line items will have a serial numeric line item ID (line_id), a reference to the appropriate order (order_id), a type, a merchant-defined ID (i.e. the product SKU, a tax rate name, a coupon code, etc.), a display title (fully editable on the order), a quantity, and a price field. When a line item is added to the order, we'll also save the total price of the line item (qty. * unit price) in a second price field to avoid rounding snafus. Line item types should also be able to specify display attributes, enabling for example the tax line item to not display a qty. field or a unit price. We're working with Bojhan on a way to make this system usable and will certainly need more detail as we implement specific line item types.

Comments

Orders will still have comments, but our goal is to move a majority of the administrative / logging comments either to a Payments tab or to the revisions tab as log messages. We'll further make use of versioning to store the changes to various order fields in revisions, and we hope to visualize those changes in a manner similar to the Project Issue module's handling of changes to issue fields in issue comments. Customer facing order comments will be preserved as is but simplified a bit in their presentation.

Shopping Cart Orders

Shopping Carts in Drupal Commerce will be saved and represented as orders. When a customer adds an item to his shopping cart, the action will create a new shopping cart order for the customer with the product in it. On subsequent pageloads, this order object will be available for us. However, as long as the order is still in the shopping cart state, its line items will be re-validated on load against the latest product prices / availability, etc. To that end, line items will need to specify at what point they're saved. For example, a discount line item doesn't need to be saved before an order is checked out, because discounts will be recalculated every time the order is loaded. Persisting such data across pageloads isn't really useful.

If an item is removed, delete the item from the order.  If all items are deleted from the order, delete the order and provide a hook for modules to preserve any bit of data entered for reuse.

Orders can be identified as being in the shopping cart by both the order state and the fact that it will not have a merchant-defined order ID. As long as an order has not been checked out, it will not be revisioned. This could result in the loss of significant marketing data, so we want to evaluate and implement a way to capture customer interactions with the store in some sort of custom watchdog.

The shopping cart will be totally optional, meaning checkout will be implemented in a separate module. For now, we aren't even specifying the development of a shopping cart block and are targeting a simple shopping cart form based on Shopify. Ideally the block and/or the form could be Views based in the near future.

Further reading:

Ryan Szrama
Posted: Mar 2, 2010

Comments

bendiy on March 2, 2010

How does xNAL fit into the Semantic Web concept? The value of micro formats to search results are going to be exponential in the future. See adr from the hCard format.

In interest of data normalization with Third Normal Form as a goal. The overall structure of the system should be very compartmentalized.

Customers should be a general object with a basic set of parameters. Additional objects can then build upon customers to create a full set of order header information or customer profiles.

Contact information should be separate from customers. This allows for multiple contacts to be associated with a customer; Purchasing Contact, Billing Contact, Marketing Contact, Ship To 1, Ship To 2, etc. All with the basic data: First Name, Last Name, Phone, Alt-Phone, Fax, email, etc.

You should always keep addresses separate from customers and contacts. This allows customers or contacts to have multiple addresses; Billing, Ship To, Marketing, Home, Work, etc. These should then be referenced by pulling all adr_id that reference a contact_id. The Customer profile then pulls all contact_id that reference the customer_id. The Order pulls the customer_id.

Orders and Line Items should be the same way. An Order is a general object and Line Items are objects that attach to it. This allows you to list all your Orders without wading through all the Line Item details.

When building an e-commerce core that can be easily extended, keeping this data separate will go a long ways to achieving this goal.

Ryan Ryan Szrama on March 2, 2010

I mostly agree, and I really think we're generally saying the same thing. The data model I described does seperate line items from orders, for example, and the Postal Address Field will also serve to allow multiple addresses for any entity it's attached to / cross referencing of the same address used elsewhere. I think conceptually we have the same starting point, but these posts aim at a first pass implementation of the features on Drupal within the constraints of it's APIs.

Re: xNAL, we can look into that further, but again the model used to gather / store the data within Drupal won't necessarily be the way it's marked up for display or even stored long term.

Thanks for the comments thus far, and keep 'em coming.

harrisben on March 2, 2010

While I'm a big supporter of referencing data from other tables, things like addresses and contact details (etc) should be stored in full in the order header to avoid problems later on where customer address data is modified. Doing it this way is also an easy way to allow anonymous user addresses/details.

Ryan Ryan Szrama on March 2, 2010

Yeah, I raised the question of making addresses an entity and using an address reference in the order itself. Damien was where you are, and the precedent in Ubercart is also to keep a snapshot of the address used for the order within the order itself. With addresses being added to the order via fields, this should continue, and we'll have the added benefit of order revision tracking to follow changes to the addresses on the order.

Ryan Ryan Szrama on March 2, 2010

Objects like nodes with specific access control rules have a uid field to track authorship. We implemented this with products, but for orders we felt you really needed two potential uid fields. One relates the order to the user who actually placed the order (customer_uid) while the other ties the order to an administrator who creates the order manually for a customer (creator_uid). If the order comes in through checkout, creator_uid will just be 0.

klavs on April 13, 2010

Hi guys,

I've been working on moving my shop to Drupal 6 and Ubercart 2, and have run into several issues, which made me think hard about how taxes support could be implemented properly, to support the absurdly flexible EU taxes system :)

The current system has a HUGE issue with discounts in particular - see

http://blog.klavsen.info/content/designing-proper-taxes-support-webshop

I hope the ideas can be of use.

Anonymous (not verified) on May 12, 2010

Line items should have some way of relating to or subordinating other line items. Particularly so that if shipping, taxes, or discounts are calculated on a per-item basis, there is a record of what they were at the time the order was placed, instead of only an order 'total shipping' (or other total) line item.

Useful if items need to be shipped separately, or credit for a single item (including shipping and taxes) needs to be given, etc.

Ryan Ryan Szrama on May 13, 2010

This is technically possible with the line item reference field, but it would require a separate widget type so the line item could reference existing line items on the same entity.

Rory (not verified) on May 17, 2010

Ryan,

The order line item view looks amazing. Did you custom build this or did you manage it through views? I think the inline editing and ajax Add new / delete would be really useful for many other areas of Drupal. I've been experimenting with editview and editable fields but they aren't as neat as your implementation. Can you point me to where this is being called in the code (Order or Line Item module?) and I'll see if I can extract it as a generic module. I'm not the best at php but I'll give it a try. Any help is appreciated.

Thanks,
Rory

Ryan Ryan Szrama on May 17, 2010

The table in the Line Item Manager widget isn't provided by Views (at the moment - would be awesome to make it happen). For the code, look in commerce_line_item.module. The form is built using the function commerce_line_item_field_widget_form() and themed with theme_line_item_manager(). When one of the #ajax enabled buttons is clicked, the whole form is submitted and commerce_line_item_manager_validate() is called, performing all the CRUD management of line items. Notice especially the use of $form_state['line_item_add'].

Thanks for the feedback! : )

tOf on November 21, 2011

Hi,
I have the same problem !
Only administrator can add line item on the site I'm building.
I have create a new line item type, with custom field (user_reference for example).
When I create a new order (admin form) I add new line item, but the field displayed are always the same !! and not depending of the line item type!!(it was the case in ubercart 6.2),
How Can I customize the display of the line item in the order form?
Thanks,

Nicola on December 16, 2011

The form is /admin/commerce/orders/#/edit
I think "line-item-manager" widget is the problem, how can I customize this form?
thanks

Ryan Ryan Szrama on May 25, 2010

I can't remember if this was addressed above or not, but since I've finally implemented order revisions, I wanted to make a note: while there will be a UI for viewing diffs between revisions, there will be no capability to revert / delete order revisions. As an audit trail, the whole point is to not lose changes to an order, and the appropriate corrective action will be to simply make a new revision of the order with the corrections.

tOf on December 16, 2011

No, we can't customize the order form because it's not a view.
We can customize the order managing view, the cart, but not the order screen, that 's the problem.
here is the issue on drupal.org : http://drupal.org/node/1352302