How to split Drupal Commerce orders at checkout
This week I needed to design a checkout workflow that, depending on the contents of a users shopping cart, would separate the current cart into two, and allow the user to check each out individually. Drupal Commerce comes with cart and checkout modules, to enable users to add products to a shopping cart, and then to checkout of your store. The commerce_cart module is really designed for a 1:1 user:cart ratio. That being said, it is possible to allow users to have more than one cart.
Commerce cart orders are standard orders with an order status that is recognized by the commerce_cart module as indicating that an order is in the shopping cart. All checkout pages provide an order status that is a cart status. For example the 'checkout', and 'review' checkout pages correspond to the checkout_checkout and checkout_review order statuses, which are recognized by commerce_cart as being cart orders. When selecting which order should be presented as the current shopping cart, commerce_cart looks for the first order, with a cart status, the greatest order_id. This is what is returned by commerce_cart_order_load().
In order to provide the required functionality I first provided a new checkout page and checkout pane, using hook_commerce_checkout_page_info() and hook_commerce_checkout_pane_info(). I placed the pane on the page and made the page the first in the commerce checkout workflow. I created a rule, using the Rules module, that would detect which types of items are in the cart, and depending on the business logic I was provided, would decide whether or not to show or hide the pane, and therefore the page as well. The rules were custom coded using some condition and action handlers that I created specifically for this project.
The checkout pane contains a simple form that allows the user to select which part of their order to checkout first. In the submit handler for the pane the line items that correspond with the order to be checked out first are removed from the current order, and added to a new cart order, created with comerce_cart_order_new(). Each order is then saved with the 'cart' status using commerce_order_status_update(), this is to make sure that some custom discount recalculation rules will fire for each order.
Finally I set the status of the new order to the original status of the current order (to reflect the current checkout page, and assign the new order object to $form_state['order'], this is so that the form will successfully redirect to the correct next checkout page for the new order. Once the customer completes the first order they are prompted, with an additional checkout pane on the 'complete' page of the checkout workflow, to begin the checkout process again with their remaining shopping cart.
I'm pretty happy with the current workflow and code, and am considering ways that it could be generalized and contributed back to the community.