Eyal Avitan
Eyal Avitan

Reputation: 191

Prevent WooCommerce order status Change from / to specific statuses

Hello I'm trying to create a function in the mu-plugins to prevent certain users to change order status from specific order statuses to specific order statuses.

I've been looking everywhere and I have tried many different ways, but nothing seems to work.

Actually the function is running using woocommerce_order_status_changed action hook. The thing is that this hook runs after the order status has already been changed, what's causing an infinite loop.

The most useful hook I found seems to be woocommerce_before_order_object_save.

I found "Add an additional argument to prevent 'woocommerce_order_status_changed' from being called" useful related thread on WooCommerce Github.
I tried using @kloon code snippet solution:

add_filter( 'woocommerce_before_order_object_save', 'prevent_order_status_change', 10, 2 );
function prevent_order_status_change( $order, $data_store ) {
    $changes = $order->get_changes();
    if ( isset( $changes['status'] ) ) {
        $data = $order->get_data();
        $from_status = $data['status'];
        $to_status = $changes['status'];

        // Do your logic here and update statuses with CRUD eg $order->set_status( 'completed' );
                // Be sure to return the order object

    }
    return $order;
}

but $changes variable is always an empty array.

I tried to use wp_insert_post_data Wordpress hook, but when I set:

$data['post_status'] = "some status";

it just prevents the whole update (the whole new data) from being saved.

This is the code I would like to run is:

function($data){
   if($data['order_status'] == 'comlpeted' && $data['new_order_status'] == 'proccessing'){
      // prevent the order status from being changed
      $data['new_order_status'] = $data['order_status'];
   }

  few more if conditions... 

   return $data;
}

Any help or advise is appreciated.

Upvotes: 3

Views: 2262

Answers (1)

LoicTheAztec
LoicTheAztec

Reputation: 253773

Based on @kloon code snippet, I have been able to get the old order status and the new order status. Then I can disable any status change from a specific defined order status to a specific defined order status.

With the following code, specific defined user roles can't change order status from "processing" to "on-hold":

add_filter( 'woocommerce_before_order_object_save', 'prevent_order_status_change', 10, 2 );
function prevent_order_status_change( $order, $data_store ) {
    // Below define the disallowed user roles
    $disallowed_user_roles = array( 'shop_manager');

    $changes = $order->get_changes();

    if( ! empty($changes) && isset($changes['status']) ) {
        $old_status    = $order->get_data()['status'];
        $new_status    = $changes['status'];
        $user          = wp_get_current_user();
        $matched_roles = array_intersect($user->roles, $disallowed_user_roles);

        // Avoid status change from "processing" to "on-hold"
        if ( 'processing' === $old_status && 'on-hold' === $new_status && ! empty($matched_roles) ) {
            throw new Exception( sprintf( __("You are not allowed to change order from %s to %s.", "woocommerce" ), $old_status, $new_status ) );
            return false;
        }
    }
    return $order;
}

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

Upvotes: 8

Related Questions