WooCommerce: How to Split Orders After Purchase

Splitting WooCommerce orders programmatically can be incredibly useful for store owners who need to manage complex workflows.

Whether you’re dealing with pre-orders, dropshipping, or simply organizing items for multiple shipments, dividing an order based on its products ensures smoother operations and a better customer experience.

However, splitting an order isn’t just about separating items; replicating crucial details like payment methods, shipping methods, and totals is equally important to maintain consistency.

In this tutorial, we’ll explore how to programmatically split orders in WooCommerce, ensuring that cloned orders include all relevant details from the original, such as payment method, order status, and billing and shipping addresses.

By the end of this guide, you’ll have a robust solution to automate order splitting directly in your WooCommerce store. Let’s dive into the code and get started!

Order #243193 contains 3 shipping classes, so the snippet below generated 2 additional orders and moved items under the relevant order!

PHP Snippet: Programmatically Split Order Upon Purchase Based on Shipping Class

Of course, you can change the split behavior and make it happen by product ID, product category, custom meta, etc. I chose shipping class because each product can only have one shipping class (or none, in such a case it’s equal to 0), and it’s a very common case scenario.

/**
 * @snippet       Split Woo Order Based on Shipping Class
 * @tutorial      Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli, Business Bloomer
 * @compatible    WooCommerce 9
 * @community     https://businessbloomer.com/club/
 */

add_action( 'woocommerce_thankyou', 'bbloomer_split_order_after_checkout', 9999 );

function bbloomer_split_order_after_checkout( $order_id ) {
	
	 $order = wc_get_order( $order_id );
	 if ( ! $order || $order->get_meta( '_order_split' ) ) return;
    $items_by_shipping_class = array();

    foreach ( $order->get_items() as $item_id => $item ) {
        $product = $item->get_product();		
		  $class_id = $product->get_shipping_class_id();
		  $items_by_shipping_class[$class_id][$item_id] = $item;
    }

    if ( count( $items_by_shipping_class ) > 1 ) {
		foreach ( array_slice( $items_by_shipping_class, 1 ) as $class_id => $items ) {
			
	      $new_order = wc_create_order();			
			$new_order->set_address( $order->get_address( 'billing' ), 'billing' );
    		if ( $order->needs_shipping_address() ) $new_order->set_address( $order->get_address( 'shipping' ) ?? $order->get_address( 'billing' ), 'shipping' );
			
			foreach ( $items as $item_id => $item ) {
				$new_item = new WC_Order_Item_Product();
				$new_item->set_product( $item->get_product() );
				$new_item->set_quantity( $item->get_quantity() );
				$new_item->set_total( $item->get_total() );
				$new_item->set_subtotal( $item->get_subtotal() );
				$new_item->set_tax_class( $item->get_tax_class() );
				$new_item->set_taxes( $item->get_taxes() );
				foreach ( $item->get_meta_data() as $meta ) {
					$new_item->add_meta_data( $meta->key, $meta->value, true );
				}
				$new_order->add_item( $new_item );
				$order->remove_item( $item_id );
			}
			$new_order->add_order_note( 'Splitted from order ' . $order_id );
			$new_order->calculate_totals();			
			$new_order->set_payment_method( $order->get_payment_method() );
			$new_order->set_payment_method_title( $order->get_payment_method_title() );			
			$new_order->update_status( $order->get_status() );

			$order->calculate_totals();
			$order->update_meta_data( '_order_split', true );
			$order->save();
		}

    }
	
}

Function Overview

The function bbloomer_split_order_after_checkout is hooked into the WooCommerce woocommerce_thankyou action. This means the code runs once an order has been completed and the user is redirected to the “Thank You” page.

Step-by-Step Breakdown

  1. Check if Order is Already Split
    • The function first checks whether the order has already been split (using a custom meta field _order_split). If so, it skips the splitting process to avoid duplication.
  2. Group Items by Shipping Class
    • The order’s items are looped through, and they are grouped by their shipping class (get_shipping_class_id). This is useful when you want to split the order based on different shipping methods or classes.
  3. Check If Splitting is Needed
    • If there’s more than one shipping class in the order, it proceeds to split the order into multiple smaller orders.
  4. Create New Orders for Each Shipping Class
    • For each group of items in a shipping class (other than the first one), a new order is created (wc_create_order).
    • Billing and shipping addresses are copied from the original order to the new order.
    • The items are cloned, and the new order is populated with these items, along with all relevant details such as quantity, total price, taxes, and meta data.
    • The original order has the items removed after they’ve been transferred to the new order.
  5. Order Note & Totals
    • A note is added to the new order to indicate that it was split from the original order.
    • The new order’s totals are recalculated, and the payment method is copied over from the original order.
  6. Finalizing the Original and New Orders
    • The original order has its totals recalculated and the _order_split meta is added to mark it as split.
    • The original order is saved to reflect the changes, and the new order is finalized with the correct payment method, status, and totals.

This code is ideal for situations where an order needs to be split into multiple smaller orders based on certain conditions—like different shipping classes. It ensures that each new order retains the relevant details from the original, such as billing/shipping address, payment method, and item information. This can be useful in scenarios like dropshipping or orders requiring separate shipments.

Where to add custom code?

You should place custom PHP in functions.php and custom CSS in style.css of your child theme: where to place WooCommerce customization?

This code still works, unless you report otherwise. To exclude conflicts, temporarily switch to the Storefront theme, disable all plugins except WooCommerce, and test the snippet again: WooCommerce troubleshooting 101

Related content

Rodolfo Melogli

Business Bloomer Founder

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as an independent WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza. Follow @rmelogli

4 thoughts on “WooCommerce: How to Split Orders After Purchase

  1. Hi! I see that we remove items from original order and we are creating new orders as guest I guess ? So in terms to keep other features like discounts based on money spent we need to assign the user? Also what would happen in user account when he is checking orders? He will see only the original order which now doesnt hold all items in it? Thanks and Happy New Year wishing you all best in 2025!

    1. Hello Martin! This should create guest orders if the order is a guest order, and customer orders if the order is by a registered customer. For custom applications, such as conditional discounts, probably you need additional custom code

  2. Hi Rolf, further to my previous comment i am quite keen to get this working properly with your bbloomer_split_shipping_packages_by_class code.
    i placed a test order that had 1 item from shipping class 807 with $12 shipping and 1 from class 826 with $7 shipping. when it split the full $19 shipping remained on the 807 class order and the 826 order had no shipping. Any suggestions on how to get the shipping to split to the correct new order?

    1. Peter, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

Questions? Feedback? Customization? Leave your comment now!
_____

If you are writing code, please wrap it like so: [php]code_here[/php]. Failure to complying with this, as well as going off topic or not using the English language will result in comment disapproval. You should expect a reply in about 2 weeks - this is a popular blog but I need to get paid work done first. Please consider joining the Business Bloomer Club to get quick WooCommerce support. Thank you!

Your email address will not be published. Required fields are marked *