2

I have the custom order status and it works.

What I need help with here is looping through the order items and checking if any of them are on backorder and if so, take the backorder items out of the original order and create a new order and give the new order the Backorder status.

The idea is to create the backorder if and only if there are items on backorder in the original order.

add_action( 'woocommerce_checkout_order_processed', 'split_order_if_order_has_backorder_products', 10, 1 );
function split_order_if_order_has_backorder_products( $order_id ) {
    if( ! $order_id ) return;

    // get order object
    $order = new WC_Order( $order_id );

    // get order currency
    $currency = $order->get_currency();

    // get order payment method
    $payment_gateway = $order->get_payment_method();

    // get order items = each product in the order
    $items = $order->get_items();

    foreach ( $items as $item ) {       
        $product = wc_get_product( $item['product_id'] );

        if ( $product->is_on_backorder() ) {

        // THIS IS NOT WORKING FOR SOME REASON?
        $backorder = wc_create_order();         
        $backorder->add_product( $product, $item['quantity'] );
        }
    }

    // THIS IS NOT WORKING EITHER = I NEED TO REMOVE THE BACKORDER ITEMS FROM THE ORIGINAL ORDER, RECALCULATE AND SAVE THE NEW TOTAL
    foreach($items as $backorder_product => $item){
        $order->remove_item($backorder_product);
        $order->calculate_totals();
        $order->save();
    }

    $address = array(
        'first_name' => $order->get_billing_first_name(),
        'last_name'  => $order->get_billing_last_name(),
        'email'      => $order->get_billing_email(),
        'phone'      => $order->get_billing_phone(),
        'address_1'  => $order->get_billing_address_1(),
        'address_2'  => $order->get_billing_address_2(),
        'city'       => $order->get_billing_city(),
        'state'      => $order->get_billing_state(),
        'postcode'   => $order->get_billing_postcode(),
        'country'    => $order->get_billing_country()
    );

    $shipping = array(
        'first_name' => $order->get_shipping_first_name(),
        'last_name'  => $order->get_shipping_last_name(),
        'address_1'  => $order->get_shipping_address_1(),
        'address_2'  => $order->get_shipping_address_2(),
        'city'       => $order->get_shipping_city(),
        'state'      => $order->get_shipping_state(),
        'postcode'   => $order->get_shipping_postcode(),
        'country'    => $order->get_shipping_country()
    );

    // Set addresses
    $backorder->set_address( $address, 'billing' );
    $backorder->set_address( $shipping, 'shipping' );

    // set the correct currency and payment gateway
    $backorder->set_currency($currency);
    $backorder->set_payment_method($payment_gateway);

    // calculate totals
    $backorder->calculate_totals();

    // set order note with original ID
    $backorder->add_order_note('Automated Backorder. Created from the original order ID: '.$order->get_id());

    // give the new backorder the correct status
    $backorder->update_status( 'backorder' );
}
7uc1f3r
  • 28,449
  • 17
  • 32
  • 50

1 Answers1

3

The code below ONLY creates a new order with the products that are in backorder, if the original order contains products with backorder status.

Last update: 02/22 - Tested in WordPress 5.9 & WooCommerce 6.2.0

function action_woocommerce_checkout_order_processed( $order_id, $posted_data, $order ) {
    // Initialize
    $check_for_back_orders = false;

    // Loop through order items
    foreach ( $order->get_items() as $item_key => $item ) {
        // Get product
        $product = $item->get_product();

        // Product is on backorder
        if ( $product->is_on_backorder() ) {
            // Will only be executed once if the order contains back orders
            if ( $check_for_back_orders == false ) {
                $check_for_back_orders = true;

                // Create new order with backorders
                $backorder_order = wc_create_order();
            }

            // Add product to 'backorder' order
            $backorder_order->add_product( $product, $item['quantity'] );

            // Delete item from original order
            $order->remove_item( $item->get_id() );
        }
    }

    // If current order contains backorders, retrieve the necessary data from the existing order and apply it in the new order
    if ( $check_for_back_orders ) {
        // Recalculate and save original order
        $order->calculate_totals();
        $order->save();
        
        // Obtain necessary information
        // Get address
        $address = array(
            'first_name' => $order->get_billing_first_name(),
            'last_name'  => $order->get_billing_last_name(),
            'email'      => $order->get_billing_email(),
            'phone'      => $order->get_billing_phone(),
            'address_1'  => $order->get_billing_address_1(),
            'address_2'  => $order->get_billing_address_2(),
            'city'       => $order->get_billing_city(),
            'state'      => $order->get_billing_state(),
            'postcode'   => $order->get_billing_postcode(),
            'country'    => $order->get_billing_country()
        );

        // Get shipping
        $shipping = array(
            'first_name' => $order->get_shipping_first_name(),
            'last_name'  => $order->get_shipping_last_name(),
            'address_1'  => $order->get_shipping_address_1(),
            'address_2'  => $order->get_shipping_address_2(),
            'city'       => $order->get_shipping_city(),
            'state'      => $order->get_shipping_state(),
            'postcode'   => $order->get_shipping_postcode(),
            'country'    => $order->get_shipping_country()
        );
        
        // Get order currency
        $currency = $order->get_currency();

        // Get order payment method
        $payment_gateway = $order->get_payment_method();
        
        // Required information has been obtained, assign it to the 'backorder' order
        // Set address
        $backorder_order->set_address( $address, 'billing' );
        $backorder_order->set_address( $shipping, 'shipping' );

        // Set the correct currency and payment gateway
        $backorder_order->set_currency( $currency );
        $backorder_order->set_payment_method( $payment_gateway );

        // Calculate totals
        $backorder_order->calculate_totals();

        // Set order note with original ID
        $backorder_order->add_order_note( 'Automated backorder. Created from the original order ID: ' . $order_id );

        // Optional: give the new 'backorder' order the correct status
        //$backorder_order->update_status( 'backorder' );
    }
}
add_action( 'woocommerce_checkout_order_processed', 'action_woocommerce_checkout_order_processed', 10, 3 );

Code goes in functions.php file of the active child theme (or active theme).


Related: Add items to an existing WooCommerce order after checkout opposite creating a default new order

7uc1f3r
  • 28,449
  • 17
  • 32
  • 50
  • This is good! The only caveat is that if you have an item as backorder allowed with say, 3 in stock, and someone buys 5, the 5 stay in the original order, instead of the extra 2 that would go to backorder going to the new order. It solves 100% the issue raised by OP though. – vazcooo1 Jun 16 '22 at 20:22
  • This still works as of July 2023, however it is important to note that the thank you page shows only the original order with the backordered product removed. As does the email that is sent. Is there a way to have this code run after the thank you page? – pathfinder Jul 10 '23 at 02:01
  • I was able to pass the new order id to a woocommerce session variable and hook into `woocommerce_after_order_details` and add a second table to my thank you page so the customer see's right away that their order was split. The second table was pretty much copy pasta from the thankyou.php file. – pathfinder Jul 10 '23 at 04:15
  • It is extremely important to note that this split takes place before payment is made so if you are running a credit card then if you recalculate the total to be after the split you will not capture the full amount. – pathfinder Jul 10 '23 at 19:23