I'm using this little peace of code on WooCommerce from this answer to auto-complete paid processing orders based on payment gateways:
/**
* AUTO COMPLETE PAID ORDERS IN WOOCOMMERCE
*/
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_paid_order', 10, 1 );
function custom_woocommerce_auto_complete_paid_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
if ( ( get_post_meta($order->id, '_payment_method', true) == 'bacs' ) || ( get_post_meta($order->id, '_payment_method', true) == 'cod' ) || ( get_post_meta($order->id, '_payment_method', true) == 'cheque' ) ) {
return;
}
// "completed" updated status for paid Orders with all others payment methods
else {
$order->update_status( 'completed' );
}
}
This is working mostly perfect
Mainly using a special payment gateway by SMS which API is bridged on 'cod' payment method and that can process payment after 'woocommerce_thankyou, out side frontend. In that case the ON HOLD status orders are passed afterward to PROCESSING status. To automate an autocomplete behavior on those cases, I use this other peace of code from this answer and it works:
function auto_update_orders_status_from_processing_to_completed(){
// Get all current "processing" customer orders
$processing_orders = wc_get_orders( $args = array(
'numberposts' => -1,
'post_status' => 'wc-processing',
) );
if(!empty($processing_orders))
foreach($processing_orders as $order)
$order->update_status( 'completed' );
}
add_action( 'init', 'auto_update_orders_status_from_processing_to_completed' );
THE PROBLEM: I am getting repetitive emails notifications concerning the new completed orders.
How can I avoid this repetitive email notifications cases?
Thanks
Updated (2019)
Added version code for Woocommerce 3+ - Added Woocommerce version compatibility.
To avoid this strange fact of repetitive email notifications, is possible to create a custom meta key/value for each processed order, when changing order status to completed, using WordPress update_post_meta() function. Then we will test before in a condition, if this custom meta data key/value exist with get_post_meta() function for each processed order.
So your two code snippets will be now:
1) AUTO COMPLETE PAID ORDERS IN WOOCOMMERCE (2019 update)
For woocommerce 3+:
add_action( 'woocommerce_payment_complete_order_status', 'wc_auto_complete_paid_order', 10, 3 );
function wc_auto_complete_paid_order( $status, $order_id, $order ) {
if ( ! $order->has_status('completed') && $order->get_meta('_order_processed') != 'yes') {
$order->update_meta_data('_order_processed', 'yes');
$status = 'completed';
}
return $status;
}
For all woocommerce versions (compatibility since version 2.5+):
add_action( 'woocommerce_payment_complete_order_status', 'wc_auto_complete_paid_order', 10, 3 );
function wc_auto_complete_paid_order( $status, $order_id, $order = null ) {
// Getting the custom meta value regarding this autocomplete status process
$order_processed = get_post_meta( $order_id, '_order_processed', true );
// Getting the WC_Order object from the order ID
$order = wc_get_order( $order_id );
if ( ! $order->has_status( 'completed' ) && $order_processed != 'yes' ) {
$order = wc_get_order( $order_id );
// setting the custom meta data value to yes (order updated)
update_post_meta($order_id, '_order_processed', 'yes');
$order->update_status( 'completed' ); // Update order status to
}
return $status;
}
2) SCAN ALL "processing" orders to auto-complete them (added Woocommerce compatibility)
add_action( 'init', 'auto_update_orders_status_from_processing_to_completed' );
function auto_update_orders_status_from_processing_to_completed(){
if( version_compare( WC_VERSION, '3.0', '<' ) {
$args = array('numberposts' => -1, 'post_status' => 'wc-processing'); // Before WooCommerce version 3
} else {
$args = array('limit' => -1, 'status' => 'processing'); // For WooCommerce 3 and above
}
// Get all current "processing" customer orders
$processing_orders = (array) wc_get_orders( $args );
if( sizeof($processing_orders) > 0 ){
foreach($processing_orders as $order ) {
// Woocommerce compatibility
$order_id = method_exists( $order, 'get_id' ) ? $order->get_id() : $order->id;
// Checking if this custom field value is set in the order meta data
$order_processed = get_post_meta( $order_id, '_order_processed', true );
if (! $order->has_status( 'completed' ) && $order_processed != 'yes' ) {
// Setting (updating) custom meta value in the order metadata to avoid repetitions
update_post_meta( $order_id, '_order_processed', 'yes' );
$order->update_status( 'completed' ); // Updating order status
}
}
}
}
Code goes in function.php file of your active child theme (or theme). Or also in any plugin php files.
I have test this code and it should work for you (due to your particular SMS bridged payment method)
Related
I would like to immidialtey change the order status from specific orders (payment method = stripe_sofort) to a custom status, that I already have in use:
add_action( 'woocommerce_thankyou', 'woocommerce_auto_processing_orders');
function woocommerce_auto_processing_orders( $order_id ) {
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
// If order is "on-hold" update status to "processing-melle"
if( $order->get_payment_method() == 'stripe_sofort' && $order->has_status( 'on-hold' ) ) {
$order->update_status( 'my_status' );
}
}
this code does not work, but I actually don't understand why. do you guys have an idea?
thanks!
I am trying to add a custom user meta field to the order meta data.
And I want to add this when I am changing my order status to "wordt-verwerkt" which is a custom order status I added with the WooCommerce plugin for custom order statuses.
I tried to use the code from this post, but I am getting an error when I change my order status.
(I also tried it with the status "processing", and didn't have any success neither)
What I have now is the following code:
add_action( 'woocommerce_order_status_wordt-verwerkt', 'add_order_meta_from_custom_user_meta', 10, 2 );
function add_order_meta_from_custom_user_meta( $order, $data ) {
$user_id = $order->get_user_id(); // Get the user id
if( $WefactEmail = get_user_meta( $user_id, 'KVK_nummer_2', true ) ) {
$order->update_meta_data( 'WeFact_email', $WefactEmail );
}
if( isset($WefactEmail) ) {
$order->save();
}
}
There are some mistakes in your code (the hooked function arguments are wrong).
See the related source code for this composite hook located in WC_Order status_transition() method (on line 363):
do_action( 'woocommerce_order_status_' . $status_transition['to'], $this->get_id(), $this );
where $this is $order (the WC_Order Object) and $this->get_id() is $order_id (the order Id).
Use instead the following:
add_action( 'woocommerce_order_status_wordt-verwerkt', 'add_order_meta_from_custom_user_meta', 10, 2 );
function add_order_meta_from_custom_user_meta( $order_id, $order ) {
$user_id = $order->get_user_id(); // Get the user id
$wf_email = get_user_meta( $user_id, 'KVK_nummer_2', true );
if( ! empty($wf_email) ) {
$order->update_meta_data( 'WeFact_email', $wf_email );
$order->save();
}
}
or also this works too:
add_action( 'woocommerce_order_status_wordt-verwerkt', 'add_order_meta_from_custom_user_meta', 10, 2 );
function add_order_meta_from_custom_user_meta( $order_id, $order ) {
$user_id = $order->get_user_id(); // Get the user id
$wf_email = get_user_meta( $user_id, 'KVK_nummer_2', true );
if( ! empty($wf_email) ) {
update_post_meta( $order_id, 'WeFact_email', $wf_email );
}
}
Code goes in functions.php file of the active child theme (or active theme). both should work.
For processing status, replace:
add_action( 'woocommerce_order_status_wordt-verwerkt', 'add_order_meta_from_custom_user_meta', 10, 2 );
with:
add_action( 'woocommerce_order_status_processing', 'add_order_meta_from_custom_user_meta', 10, 2 );
I get several orders where a customer selects "Direct Bank Transfer" and then they change their mind and want to pay by Credit Card. This is quite annoying because I have to manually change the order from "On Hold" to "Pending Payment" so they can pay by card via the "order-pay" endpoint which is found in "My Account" under "Orders".
I've been using the WooCommerce change order status BACS processing to automatically change the order status from "On Hold" to "Pending Payment".
// WooCommerce Change Order Status BACS Pending
add_action( 'woocommerce_thankyou', 'bacs_order_payment_pending_order_status', 10, 1 );
function bacs_order_payment_pending_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
if ( ( get_post_meta($order->id, '_payment_method', true) == 'bacs' ) && ('on-hold' == $order->status ) ) {
$order->update_status('pending');
} else {
return;
}
}
But since I have several user profiles (I sell B2B as well), this is not practical for my shop. I'm trying to expand this snippet to also check for the user role. I've used the following in my other snippets. Is it possible to add the below logic to the snippet above?
$user = wp_get_current_user();
$roles = (array) $user->roles;
$roles_to_check = array('administrator', 'customer', 'shop_manager');
$compare = array_diff($roles, $roles_to_check);
if (empty($compare)){
This is my attempt.
// WooCommerce Change Order Status BACS Pending
add_action( 'woocommerce_thankyou', 'bacs_order_payment_pending_order_status', 10, 1 );
function bacs_order_payment_pending_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
$user = wp_get_current_user();
$roles = (array) $user->roles;
$roles_to_check = array('administrator', 'customer', 'shop_manager');
$compare = array_diff($roles, $roles_to_check);
if (empty($compare)){
if ( ( get_post_meta($order->id, '_payment_method', true) == 'bacs' ) && ('on-hold' == $order->status ) ) {
$order->update_status('pending');
} else {
return;
}
}
You can use this as follows, comment with explanation added in the code
function bacs_order_payment_pending_order_status( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get user
$user = $order->get_user();
// Roles
$roles = (array) $user->roles;
// Roles to check
$roles_to_check = array( 'administrator', 'customer', 'shop_manager' );
// Compare
$compare = array_diff( $roles, $roles_to_check );
// Result is empty
if ( empty ( $compare ) ) {
if ( $order->get_payment_method() == 'bacs' && $order->has_status( 'on-hold' ) ) {
$order->update_status( 'pending' );
}
}
}
}
add_action( 'woocommerce_thankyou', 'bacs_order_payment_pending_order_status', 10, 1 );
Might Come in handy: WooCommerce: Get Order Info (total, items, etc) From $order Object
Woocommerce version 3.4.0 has introduced a much better hook that allows to change the default status for BACS payment gateway which is set to "on-hold".
Using this hook will:
Lighten your code,
Avoid "on-hold" notification to the customer when a BACS order is placed.
Here is that code:
add_filter( 'woocommerce_bacs_process_payment_order_status','filter_process_payment_order_status_callback', 10, 2 );
function filter_process_payment_order_status_callback( $status, $order ) {
// Here set the user roles to check
$roles_to_check = array( 'administrator', 'customer', 'shop_manager' );
$user = $order->get_user(); // Get the WP_User Object
$compare = array_diff( $user->roles, $roles_to_check ); // compare
if ( empty ( $compare ) ) {
return 'pending';
}
return $status;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Since WooCommerce 5+: Allow re-sending New Order Notification in WooCommerce 5+
Enabling New Order email notification (sent to the admin) for BACS payments:
As pending Orders doesn't send email notifications, you can enable that with the following
add_action( 'woocommerce_checkout_order_processed', 'pending_new_order_notification', 20, 1 );
function pending_new_order_notification( $order_id ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Only for "pending" order status and BACS payments
if( $order->has_status( 'pending' ) && $order->get_payment_method() === 'bacs' )
{
// Send "New Email" notification (to admin)
WC()->mailer()->get_emails()['WC_Email_New_Order']->trigger( $order_id );
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Related: Send an Email notification to the admin for pending order status in WooCommerce
Useful: How to get WooCommerce order details
Related: Change default WooCommerce order status to processing for cheque and bacs payments
Freshly updated answer thread: WooCommerce change order status BACS processing
I am having trouble with my COD payment method, when a customer orders... The order becomes processing and it reduces the items stock.
Based on this answer thread: Reduce stock only for specific order statuses and payment method in Woocommerce, I've customized the code and tried this script:
add_filter( 'woocommerce_can_reduce_order_stock', 'wcs_do_not_reduce_processing_stock', 10, 2 );
function wcs_do_not_reduce_processing_stock( $reduce_stock, $order ) {
if ( $order->has_status( 'processing' ) && $order->get_payment_method() == 'cod' ) {
$reduce_stock = false;
}
return $reduce_stock;
}
But it doesn't work.
How can I avoid stock reduction on order items for orders using "Cash on delivery" payment method with a "processing" status?
I have changed a your code a bit and added the missing code that will reduce stock on "completed" order status for COD payment method only:
add_filter( 'woocommerce_can_reduce_order_stock', 'processing_cod_stock_not_reduced', 20, 2 );
function processing_cod_stock_not_reduced( $reduce_stock, $order ) {
if ( ! $order->has_status( 'completed' ) && $order->get_payment_method() == 'cod' ) {
return false;
}
return $reduce_stock;
}
// Reduce stock on COD orders with completed status
add_action( 'woocommerce_order_status_completed', 'order_stock_reduction_based_on_payment_method', 20, 2 );
function order_stock_reduction_based_on_payment_method( $order_id, $order ){
if( $order->get_payment_method() == 'cod' && ! get_post_meta( $order_id, '_order_stock_reduced', true ) ){
wc_reduce_stock_levels($order_id);
}
}
Code goes in function.php file of the active child theme (or active theme). Tested and works
I am using on WooCommerce this little peace of code from this answer to autocomplete paid processing orders:
/**
* AUTO COMPLETE PAID ORDERS IN WOOCOMMERCE
*/
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_paid_order', 10, 1 );
function custom_woocommerce_auto_complete_paid_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
if ( ( get_post_meta($order->id, '_payment_method', true) == 'bacs' ) || ( get_post_meta($order->id, '_payment_method', true) == 'cod' ) || ( get_post_meta($order->id, '_payment_method', true) == 'cheque' ) ) {
return;
}
// "completed" updated status for paid Orders with all others payment methods
else {
$order->update_status( 'completed' );
}
}
But the problem is that I use a special payment gateway by SMS which API is bridged on 'cod' payment method, and the orders stay sometimes in on-hold status on this 'woocommerce_thankyou' hook.
So I will need to scan all the time the 'processing' orders to pass them in complete status. I have tried different things and hooks, but I cant get it work as expected.
How can I do this?
Thanks
To get this working you just need a little function that will scan all orders with a "processing" status on the 'init' hook, and that will update this status to "completed".
Here is that code:
function auto_update_orders_status_from_processing_to_completed(){
// Get all current "processing" customer orders
$processing_orders = wc_get_orders( $args = array(
'numberposts' => -1,
'post_status' => 'wc-processing',
) );
if(!empty($processing_orders))
foreach($processing_orders as $order)
$order->update_status( 'completed' );
}
add_action( 'init', 'auto_update_orders_status_from_processing_to_completed' );
This code is tested and works.
Code goes in function.php file of your active child theme (or theme). Or also in any plugin php files.
ADVICE & UPDATE
There is a little bug around email notifications sent twice that is solved in here:
Avoid repetitive emails notification on some auto completed orders
WooCommerce virtual orders can be automatically marked as ‘completed’ after payment with a little bit of code added to a custom plugin, or your themes functions.php file. By default WooCommerce will mark virtual-downloadable orders as ‘completed’ after successful payment, which makes sense, but some store owners will want to be able to automatically mark even a virtual order as complete upon payment, for instance in the case of a site which takes donations where no further action is required. To do so, use the following code, which is based on the core virtual-downloadable completed order status:
add_filter( 'woocommerce_payment_complete_order_status', 'virtual_order_payment_complete_order_status', 10, 2 );
function virtual_order_payment_complete_order_status( $order_status, $order_id ) {
$order = new WC_Order( $order_id );
if ( 'processing' == $order_status &&
( 'on-hold' == $order->status || 'pending' == $order->status || 'failed' == $order->status ) ) {
$virtual_order = null;
if ( count( $order->get_items() ) > 0 ) {
foreach( $order->get_items() as $item ) {
if ( 'line_item' == $item['type'] ) {
$_product = $order->get_product_from_item( $item );
if ( ! $_product->is_virtual() ) {
// once we've found one non-virtual product we know we're done, break out of the loop
$virtual_order = false;
break;
} else {
$virtual_order = true;
}
}
}
}
// virtual order, mark as completed
if ( $virtual_order ) {
return 'completed';
}
}
// non-virtual order, return original status
return $order_status;
}
OR
You can also use plugin for auto complete order
Here is the plugin URL : https://wordpress.org/plugins/woocommerce-autocomplete-order/screenshots/
Please let me know which is use full to you.
Thnaks.