Change Woocommerce order status for Cash on delivery - php

I need to change the default order status that Woocommerce is applying to orders which are paid by Cash on delivery. The default is processing and I need to set it to on-hold. I have tried this
add_action( 'woocommerce_thankyou', 'my_order_status', 50 );
function my_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
if ( ( get_post_meta( $order->id, '_payment_method', true ) == 'cod' ) && ( $order->status == 'processing' ) ) {
$order->update_status('on-hold');
}
}
but it's not working. Any thoughts?

this solved my problem
add_action('woocommerce_thankyou_cod', 'action_woocommerce_thankyou_cod', 10, 1);
function action_woocommerce_thankyou_cod($order_id)
{
$order = wc_get_order($order_id);
$order->update_status('on-hold');
}
put this in your functions.php

To resolve the issue please use below code:
add_action( 'woocommerce_thankyou', 'wc_change_status' );
function wc_change_status( $order ) {
$order = new WC_Order($order);
$order->update_status('on-hold', 'This is the change status');
//print('<pre>');
// print_r($order);
}

Related

How to stop send email for a specific recipient for specific order status types when change order status in woocommerce [duplicate]

I have searched the web and checked the WooCommerce docs for a solution to disable the "confirmation email" that is sent to the customer when they place an order in WooCoomerce.
I also want to disable the "new order" mail that goes to the admin.
But only if the order has a custom status "mystatus", which some orders are getting based on what the customers are ordering.
Tried adding it like this, but it did not work:
remove_action( 'woocommerce_order_status_mystatus_notification', array($email_class->emails['WC_Email_New_Order'], 'trigger' ) );?>
Any advice?
This is how I change the order status for specific orders:
add_action( 'woocommerce_thankyou','woocommerce_thankyou_change_order_status', 10, 1 );
function woocommerce_thankyou_change_order_status( $order_id ){
if( ! $order_id ) return;
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
if( ($order->get_status() == 'processing' || $order->get_status() == 'pending' || $order->get_status() == 'on-hold' ) && dokan_is_user_seller( $user_id ) && is_user_logged_in()) {
$order->update_status( 'mystatus' );
}
}
No real need to make any changes to WooCommerce settings.
No email-notifications will be sent for a custom order status, unless you effectively provide this.
However, the default email-notifications are sent, because the woocommerce_thankyou hook is executed after the email-notifications have been sent.
So use the woocommerce_checkout_order_created hook (that is executed, before the e-mail notifications are sent) opposite woocommerce_thankyou to change the order status, and no emails will be sent anyway.
function action_woocommerce_checkout_order_created( $order ) {
// Get user ID
$user_id = $order->get_user_id();
// Compare
if ( ( $order->get_status() == 'processing' || $order->get_status() == 'pending' || $order->get_status() == 'on-hold' ) && dokan_is_user_seller( $user_id ) && is_user_logged_in() ) {
// Update status
$order->update_status( 'mystatus' );
}
}
add_action( 'woocommerce_checkout_order_created', 'action_woocommerce_checkout_order_created', 10, 1 );
Note: If you want to disable emails in any other case, you can use the woocommerce_email_recipient_{$email_id} filter composite hook and with the right email ID set, you have the option to disable email notifications.
For instance:
// Admin - new order email notification
// Customer - on hold
// Customer - processing
// Customer - pending
function filter_woocommerce_email_recipient( $recipient, $order, $email ) {
if ( ! $order || ! is_a( $order, 'WC_Order' ) ) return $recipient;
// Has order status
if ( $order->has_status( 'your-order-status' ) ) {
$recipient = '';
}
return $recipient;
}
add_filter( 'woocommerce_email_recipient_new_order', 'filter_woocommerce_email_recipient', 10, 3 );
add_filter( 'woocommerce_email_recipient_customer_on_hold_order', 'filter_woocommerce_email_recipient', 10, 3 );
add_filter( 'woocommerce_email_recipient_customer_processing_order', 'filter_woocommerce_email_recipient', 10, 3 );
add_filter( 'woocommerce_email_recipient_customer_pending_order', 'filter_woocommerce_email_recipient', 10, 3 );
Disable the mail under WooCommerce >> Settings >> Emails you want to only sent in case your order has a custom status.
Now just sent it in case your order has the correct status:
add_action( 'woocommerce_thankyou', 'woocommerce_thankyou_change_order_status', 10, 1 );
function woocommerce_thankyou_change_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
if ( ( $order->get_status() == 'processing' || $order->get_status() == 'pending' || $order->get_status() == 'on-hold' ) && dokan_is_user_seller( $user_id ) && is_user_logged_in() ) {
$order->update_status( 'mystatus' );
$email_oc = new WC_Email_Customer_Completed_Order();
$email_oc->trigger($order_id);
}
}
You can just sent every WooCommerce mail from PHP you want.

Disable WooCommerce order email notification for orders with custom status

I have searched the web and checked the WooCommerce docs for a solution to disable the "confirmation email" that is sent to the customer when they place an order in WooCoomerce.
I also want to disable the "new order" mail that goes to the admin.
But only if the order has a custom status "mystatus", which some orders are getting based on what the customers are ordering.
Tried adding it like this, but it did not work:
remove_action( 'woocommerce_order_status_mystatus_notification', array($email_class->emails['WC_Email_New_Order'], 'trigger' ) );?>
Any advice?
This is how I change the order status for specific orders:
add_action( 'woocommerce_thankyou','woocommerce_thankyou_change_order_status', 10, 1 );
function woocommerce_thankyou_change_order_status( $order_id ){
if( ! $order_id ) return;
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
if( ($order->get_status() == 'processing' || $order->get_status() == 'pending' || $order->get_status() == 'on-hold' ) && dokan_is_user_seller( $user_id ) && is_user_logged_in()) {
$order->update_status( 'mystatus' );
}
}
No real need to make any changes to WooCommerce settings.
No email-notifications will be sent for a custom order status, unless you effectively provide this.
However, the default email-notifications are sent, because the woocommerce_thankyou hook is executed after the email-notifications have been sent.
So use the woocommerce_checkout_order_created hook (that is executed, before the e-mail notifications are sent) opposite woocommerce_thankyou to change the order status, and no emails will be sent anyway.
function action_woocommerce_checkout_order_created( $order ) {
// Get user ID
$user_id = $order->get_user_id();
// Compare
if ( ( $order->get_status() == 'processing' || $order->get_status() == 'pending' || $order->get_status() == 'on-hold' ) && dokan_is_user_seller( $user_id ) && is_user_logged_in() ) {
// Update status
$order->update_status( 'mystatus' );
}
}
add_action( 'woocommerce_checkout_order_created', 'action_woocommerce_checkout_order_created', 10, 1 );
Note: If you want to disable emails in any other case, you can use the woocommerce_email_recipient_{$email_id} filter composite hook and with the right email ID set, you have the option to disable email notifications.
For instance:
// Admin - new order email notification
// Customer - on hold
// Customer - processing
// Customer - pending
function filter_woocommerce_email_recipient( $recipient, $order, $email ) {
if ( ! $order || ! is_a( $order, 'WC_Order' ) ) return $recipient;
// Has order status
if ( $order->has_status( 'your-order-status' ) ) {
$recipient = '';
}
return $recipient;
}
add_filter( 'woocommerce_email_recipient_new_order', 'filter_woocommerce_email_recipient', 10, 3 );
add_filter( 'woocommerce_email_recipient_customer_on_hold_order', 'filter_woocommerce_email_recipient', 10, 3 );
add_filter( 'woocommerce_email_recipient_customer_processing_order', 'filter_woocommerce_email_recipient', 10, 3 );
add_filter( 'woocommerce_email_recipient_customer_pending_order', 'filter_woocommerce_email_recipient', 10, 3 );
Disable the mail under WooCommerce >> Settings >> Emails you want to only sent in case your order has a custom status.
Now just sent it in case your order has the correct status:
add_action( 'woocommerce_thankyou', 'woocommerce_thankyou_change_order_status', 10, 1 );
function woocommerce_thankyou_change_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
if ( ( $order->get_status() == 'processing' || $order->get_status() == 'pending' || $order->get_status() == 'on-hold' ) && dokan_is_user_seller( $user_id ) && is_user_logged_in() ) {
$order->update_status( 'mystatus' );
$email_oc = new WC_Email_Customer_Completed_Order();
$email_oc->trigger($order_id);
}
}
You can just sent every WooCommerce mail from PHP you want.

Auto change order status to completed for specific products in WooCommerce

I have a virtual product and I want it to change status after payment is completed.
The following code changes all purchases to "completed", but I want to change only one of my products, not all of them.
My product is a variable product and has 4 items,
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order' );
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
if( $order->has_status( 'processing' ) ) {
$order->update_status( 'completed' );
}
}
I searched a lot but did not find an answer. please help me. Thank You
You can target specific product(s) Id(s) from order items, to make your code work only for them as follows:
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order' );
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$product_ids = array('23'); // Here set your targeted product(s) Id(s)
$product_found = false;
// Loop through order items
foreach ( $order->get_items() as $item ) {
if( array_intersect( $product_ids, array($item->get_product_id(), $item->get_variation_id()) ) ) {
$product_found = true;
break;
}
}
if( $order->has_status( 'processing' ) && $product_found ) {
$order->update_status( 'completed' );
}
}
If you want to target those products exclusively, then you will use the following instead:
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order' );
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$product_ids = array('23'); // Here set your targeted product(s) Id(s)
$product_found = true;
// Loop through order items
foreach ( $order->get_items() as $item ) {
if( ! array_intersect( $product_ids, array($item->get_product_id(), $item->get_variation_id()) ) ) {
$product_found = false;
break;
}
}
if( $order->has_status( 'processing' ) && $product_found ) {
$order->update_status( 'completed' );
}
}
Code goes in functions.php file of the active child theme (or active theme). It should works.
Addition: Avoid multiple notifications
You should better use woocommerce_payment_complete_order_status hook instead like in WooCommerce: Auto complete paid orders answer, to avoid multiple notifications.
So the code is going to be:
add_action( 'woocommerce_payment_complete_order_status', 'wc_auto_complete_paid_order', 10, 3 );
function wc_auto_complete_paid_order( $status, $order_id, $order ) {
$product_ids = array('23'); // Here set your targeted product(s) Id(s)
$product_found = false;
// Loop through order items
foreach ( $order->get_items() as $item ) {
if( array_intersect( $product_ids, array($item->get_product_id(), $item->get_variation_id()) ) ) {
$product_found = true;
break;
}
}
return $product_found ? 'completed' : $status;
}
Or targeting products exclusively:
add_action( 'woocommerce_payment_complete_order_status', 'wc_auto_complete_paid_order', 10, 3 );
function wc_auto_complete_paid_order( $status, $order_id, $order ) {
$product_ids = array('23'); // Here set your targeted product(s) Id(s)
$product_found = true;
// Loop through order items
foreach ( $order->get_items() as $item ) {
if( ! array_intersect( $product_ids, array($item->get_product_id(), $item->get_variation_id()) ) ) {
$product_found = false;
break;
}
}
return $product_found ? 'completed' : $status;
}
It should work...

Message-info WooCommerce

Use WooCommerce for digital products and as everyone knows, for this type of product or payment status it is always as processing. To resolve this, use the following code that resolves the issue, but the "payment received successfully" notification message is not synchronized. Does anyone know how to solve it?
add_action( 'woocommerce_order_status_changed', 'ts_auto_complete_by_payment_method' );
function ts_auto_complete_by_payment_method( $order_id ) {
if ( !$order_id ) {
return;
}
global $product;
$order = wc_get_order( $order_id );
if ( $order->data[ 'status' ] == 'processing' ) {
$payment_method = $order->get_payment_method();
if ( $payment_method != "cod" ) {
$order->update_status( 'completed' );
}
}
}
add_action( 'woocommerce_before_thankyou', 'update_order_status_and_show_info' );
function update_order_status_and_show_info( $order_id ) {
if ( !$order_id ) {
return;
}
$order = wc_get_order( $order_id );
if ( $order->get_status() == 'processing' ) {
$payment_method = $order->get_payment_method();
if ( $payment_method != "cod" ) {
$order->update_status( 'completed' );
echo "<span>Payment received successfully.</span>";
}
}
}
Try this code

Reduce stock only for specific order statuses and payment method in Woocommerce

I'm working on a bit of custom Woocommerce functionality for a client. They use the BACS payment gateway to handle manual payments.
However, the gateway currently reduces the stock too early for our needs, i.e., when the order is "On Hold". I would like to ONLY reduce the stock when the order is marked "Processing" or "Complete" (avoiding duplicate reductions).
I have manged to prevent the stock from reducing itself while "on hold" with the following snippet:
function wcs_do_not_reduce_onhold_stock( $reduce_stock, $order ) {
if ( $order->has_status( 'on-hold' ) ) {
$reduce_stock = false;
}
return $reduce_stock;
}
add_filter( 'woocommerce_can_reduce_order_stock', 'wcs_do_not_reduce_onhold_stock', 10, 2 );
I'm not too sure how to proceed though. While the above code works, adding the following condition does not:
else if ( $order->has_status( 'processing' ) || $order->has_status( 'completed' ) ) {
$reduce_stock = true;
}
In short, I'd ideally like the stock to change depending on the following stock statuses:
On Hold - Does nothing
Completed or Processing - Reduce Stock (Only once)
Cancelled - Increase Stock (Only if initially reduced)
Any help is much appreciated!
Using a custom function hooked in woocommerce_order_status_changed you will be able to target 'processing' and 'completed' order statuses change reducing order items stock.
I have added in your function a condition to target only "BACS" payment gateway on orders.
add_filter( 'woocommerce_can_reduce_order_stock', 'wcs_do_not_reduce_onhold_stock', 10, 2 );
function wcs_do_not_reduce_onhold_stock( $reduce_stock, $order ) {
if ( $order->has_status( 'on-hold' ) && $order->get_payment_method() == 'bacs' ) {
$reduce_stock = false;
}
return $reduce_stock;
}
add_action( 'woocommerce_order_status_changed', 'order_stock_reduction_based_on_status', 20, 4 );
function order_stock_reduction_based_on_status( $order_id, $old_status, $new_status, $order ){
// Only for 'processing' and 'completed' order statuses change
if ( $new_status == 'processing' || $new_status == 'completed' ){
$stock_reduced = get_post_meta( $order_id, '_order_stock_reduced', true );
if( empty($stock_reduced) && $order->get_payment_method() == 'bacs' ){
wc_reduce_stock_levels($order_id);
}
}
}
Code goes in function.php file of the active child theme (or active theme).
Tested and works
The accepted answer did not work for me. The first part did but not the second, this is how I modified it:
// This is the same as the accepted answer
add_filter( 'woocommerce_can_reduce_order_stock', 'wcs_do_not_reduce_onhold_stock', 10, 2 );
function wcs_do_not_reduce_onhold_stock( $reduce_stock, $order ) {
if ( $order->has_status( 'on-hold' ) && $order->get_payment_method() == 'bacs' ) {
$reduce_stock = false;
}
return $reduce_stock;
}
// This is what I changed
add_action( 'woocommerce_order_status_processing', 'reduce_stock_on_bacs_order_status_change', 10, 1 );
add_action( 'woocommerce_order_status_completed', 'reduce_stock_on_bacs_order_status_change', 10, 1 );
function reduce_stock_on_bacs_order_status_change( $order_id ) {
// Get the order object
$order = wc_get_order( $order_id );
// Check if the order was paid using BACS
if ( 'bacs' == $order->get_payment_method() ) {
// Check if the stock reduction has already been done for this order
$stock_reduction_done = get_post_meta( $order_id, '_stock_reduction_done', true );
if ( ! $stock_reduction_done ) {
// Iterate over the order items
foreach( $order->get_items() as $item_id => $item ) {
// Reduce stock for each item
$product = $item->get_product();
$qty = $item->get_quantity();
$product->reduce_stock( $qty );
}
// Mark the stock reduction as done for this order
update_post_meta( $order_id, '_stock_reduction_done', true );
}
}
}
This will not reduce the stock for BACS payments until the order is marked as processing or completed.

Categories