2

I am using Change Woocommerce Order Status based on Shipping Method code and it works beautifully for re-assigning my custom order status "awaiting-pickup" in WooCommerce based on shipping method string.

Here is my code:

add_action( 'woocommerce_thankyou', 'shipping_method_update_order_status', 10, 1 );
    function shipping_method_update_order_status( $order_id ) {
        if ( ! $order_id ) return;
    
        $search = 'local_pickup'; // The needle to search in the shipping method ID
    
        // Get an instance of the WC_Order object
        $order = wc_get_order( $order_id );
    
        // Get the WC_Order_Item_Shipping object data
        foreach($order->get_shipping_methods() as $shipping_item ){
            // When "pickup" method is used, we change the order to "awaiting-pickup" status
            if( strpos( $shipping_item->get_method_title(), $search ) !== false ){
                $order->update_status('awaiting-pickup');
                $order->save();
                break;
            }
        }
    }

I need help extending this to apply a few different rules based on other shipping methods like for 'free_shipping' and 'flat_rate' that I would like to reassign as 'awaiting-delivery' too.

$search = 'flat_rate' OR 'free_shipping';
$order->update_status('awaiting-delivery');

The shipping instances are structured like so:

'local_pickup:2'
'local_pickup:5'
'local_pickup:7'
'local_pickup:10'

'flat_rate:3'
'flat_rate:6'
'flat_rate:9'

'free_shipping:11'
'free_shipping:12'
'free_shipping:13'

Every time I create a new shipping zone extra shipping instances that are attached to that zone will have new numbers attached the method type. Ultimately I need something that use the following logic:

IF      'local_pickup' IN string
THEN    $order->update_status('awaiting-pickup');
ELSEIF  'flat_rate' OR 'free_shipping' IN string
THEN    $order->update_status('awaiting-delivery');
END
LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399

2 Answers2

2

Update 2

As you are using the real shipping method Id in here, you don't need to search for a string. Then it will be more simple to make it work for multiple shipping methods Ids as follows:

add_action( 'woocommerce_thankyou', 'shipping_method_update_order_status', 10, 1 );
function shipping_method_update_order_status( $order_id ) {
    if ( ! $order_id ) return;
    
    // Here define your shipping methods Ids
    $shipping_methods_ids_1 = array('local_pickup');
    $shipping_methods_ids_2 = array('flat_rate', 'free_shipping');

    // Get an instance of the WC_Order object
    $order = wc_get_order( $order_id );

    // Get the WC_Order_Item_Shipping object data
    foreach($order->get_shipping_methods() as $shipping_item ){
        // For testing to check the shipping method slug (uncomment the line below):
        // echo '<pre>'. print_r( $shipping_item->get_method_id(), true ) . '</pre>';

        // When "Local pickup" method is used, we change the order to "awaiting-pickup" status
        if( in_array( $shipping_item->get_method_id(), $shipping_methods_ids_1 ) && ! $order->has_status('awaiting-pickup') ){
            $order->update_status('awaiting-pickup'); // Already use internally save() method
            break; // stop the loop
        }
        // When 'Flat rate' or 'Free shipping' methods are used, we change the order to "awaiting-delivery" status
        elseif( in_array( $shipping_item->get_method_id(), $shipping_methods_ids_2 ) && ! $order->has_status('awaiting-delivery') ){
            $order->update_status('awaiting-delivery'); // Already use internally save() method
            break; // stop the loop
        }
    }
}

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

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Thank you for helping me with this! I updated my question to better reflect some of the specifics around the problem I'm dealing with. Namely, I need something that views the string as we continue to add shipping zones over time because there are multiple numbered instances with each shipping type. Also, I need one set of shipping methods to assign order status of 'awaiting-pickup' while the other two shipping methods assign a different order status of 'awaiting-delivery'. Thank you again for helping me with this problem. – beerismysavior Nov 15 '20 at 22:26
  • Alright so I tried this code out as you wrote it and it didn't seem to work as planned. The order did go through but it ended up in the processing order status and also generated critical error for the website. I noticed the following `has_status('awaiting-pickup')` so I attempted to change that to `has_status('processing')` in the event that is the old state we would be referencing. In both in attempts, it did not work for some reason. Any other ideas that might work? – beerismysavior Nov 15 '20 at 23:24
  • test generated "flat_rate" when I used the below code `foreach($order->get_shipping_methods() as $shipping_item ){print_r( $shipping_item->get_method_id());` – beerismysavior Nov 15 '20 at 23:50
  • 1
    I ran into an issue where users would occasionally go back to the thank you screen which would then send orders from 'completed' back to the shipping bucket. To get around this I added a check `if ($order->has_status('completed'))` right after `$order = wc_get_order( $order_id );` to ensure that once orders have been marked as completed they don't return to the shipping method based order status – beerismysavior Feb 27 '21 at 12:24
0

Assuming new orders are always given the status of PROCESSING, then this is how I would do it:

add_action('woocommerce_order_status_changed', 'jds_auto_change_status_by_shipping_method');
    function jds_auto_change_status_by_shipping_method($order_id) {
        // If the status of an order is changed to PROCESSING and the shipping method contains specific text then change the status.
        if ( ! $order_id ) {
            return;
        }
        global $product;
        $order = wc_get_order( $order_id );
        if ($order->data['status'] == 'processing') { // if order status is processing
            $shipping_method = $order->get_shipping_method();
            if ( strpos($shipping_method, 'local_pickup') !== false ) { // if shipping method CONTAINS
                $order->update_status('awaiting-pickup'); // change status
            } else if ( strpos($shipping_method, 'flat_rate') !== false ) { // if shipping method CONTAINS
                $order->update_status('awaiting-delivery'); // change status
            } else if ( strpos($shipping_method, 'free_shipping') !== false ) { // if shipping method CONTAINS
                $order->update_status('awaiting-delivery'); // change status
            }
        }   
    }

I will note that the $shipping_method returns the Human Readable text that the customer sees on the website when they checkout so you may need to adjust local_pickup to match the exact text the customer sees (like 'Local Pickup').

jsherk
  • 6,128
  • 8
  • 51
  • 83