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.
Related
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
How to change in woocommerce order status from processing to completed
?
I found snippet, but it only changes status if you go to thank you page, but if my customer decides just to close paypal page and don't go to thank you page ?
Then it is still processing, tested it already. I need automatically to detect processing status and change it to processing.
This will check for when the order changes status. If the status changes to processing, it will set the status to completed. Note that it does go through processing, so the user will probably get two emails back to back. You can disable one of the emails in the WC settings dashboard.
function wc_status_change($order_id,$old_status,$new_status) {
$order = new WC_Order( $order_id );
if($order->status == 'processing'){
$order->update_status('completed');
}
}
In your case you have two options the first may not work as it is related to previous versions of woocommerce but the second one should work
add the code to your functions.php
1º
add_filter( 'woocommerce_payment_complete_order_status', 'update_order_status_woo', 10, 2 );
function update_order_status_woo( $order_status, $order_id ) {
$order = new WC_Order( $order_id );
if ( $order_status == 'processing' && ( 'on-hold' == $order->status || 'pending' == $order->status || 'failed' == $order->status ) ) {
return 'completed';
}
return $order_status;
}
2º
add_action('woocommerce_order_status_changed', 'auto_update_processing_to_complete');
function auto_update_processing_to_complete($order_id)
{
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
if ($order->data['status'] == 'processing') {
$order->update_status( 'completed' );
}
}
I made a two-step payment on the site. Payment occurs after confirmation of the order by the manager. First, the user selects the payment method "for confirmation"(renamed "cash on delivery") and pay only after receiving the invoice for payment. On the checkout page, I hide paypal via js. I would like paypal to be hidden when on-hold status. When the status of "Pending payment" is disabled "for confirmation"(renamed "cash on delivery") and payment via paypal is available.
Update July 2020
The following code will show hide payment gateways:
On checkout page it will remove "paypal" payment option (So you can remove your jQuery code)
On Order Pay page it will:
Keep "paypal" only payment option if the order status is "pending" (removing all other options)
For others order statuses than "pending", the payment is not allowed by Woocommerce…
The code:
// Show/hide payment gateways
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_hide_payment_gateways', 100, 1 );
function conditionally_hide_payment_gateways( $available_gateways ) {
// 1. On Order Pay page
if( is_wc_endpoint_url( 'order-pay' ) ) {
// Get an instance of the WC_Order Object
$order = wc_get_order( get_query_var('order-pay') );
// Loop through payment gateways 'pending', 'on-hold', 'processing'
foreach( $available_gateways as $gateways_id => $gateways ){
// Keep paypal only for "pending" order status
if( $gateways_id !== 'paypal' && $order->has_status('pending') ) {
unset($available_gateways[$gateways_id]);
}
}
}
// 2. On Checkout page
elseif( is_checkout() && ! is_wc_endpoint_url() ) {
// Disable paypal
if( isset($available_gateways['paypal']) ) {
unset($available_gateways['paypal']);
}
}
return $available_gateways;
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.
Copy and paste the same code and it didn't work
syntax error, unexpected 'elseif' (T_ELSEIF)
I correct the code
// Show/hide payment gateways
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_hide_payment_gateways', 100, 1 );
function conditionally_hide_payment_gateways( $available_gateways ) {
// 1. On Order Pay page
if( is_wc_endpoint_url( 'order-pay' ) ) {
// Get an instance of the WC_Order Object
$order = wc_get_order( get_query_var('order-pay') );
// Loop through payment gateways 'pending', 'on-hold', 'processing'
foreach( $available_gateways as $gateways_id => $gateways ){
// Keep paypal only for "pending" order status
if( $gateways_id !== 'paypal' && $order->has_status('pending') ) {
unset($available_gateways[$gateways_id]);
}
}
}
// 2. On Checkout page
elseif( is_checkout() && ! is_wc_endpoint_url() ) {
// Disable paypal
if( isset($available_gateways['paypal']) ) {
unset($available_gateways['paypal']);
}
}
return $available_gateways;
}
enter image description here
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'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)