Reputation: 3174
Continuation of this Question Answered by @LoicTheAztec was a great solution. But I need to do some update on it. I want to update it the time delay to 30min or 1 hr instead of 5 days.
Also it should work on order status pending/on-hold.
The problem is when I try to use this code for "pending payment" every day when it execute the very last order auto canceled.
So the code should avoid the very last order or should check the order place time.
add_action( 'woocommerce_order_status_changed', 'daily_cancel_unpaid_orders', 10, 4 );
function daily_cancel_unpaid_orders( $order_id, $old_status, $new_status, $order ) {
// Enable the process to be executed daily
if( in_array( $new_status, array('processing', 'completed') )
&& get_option( 'unpaid_orders_daily_process' ) < time() ) :
$days_delay = 1; // <=== SET the delay (number of days to wait before cancelation)
$one_day = 24 * 60 * 60;
$today = strtotime( date('Y-m-d') );
// Get unpaid orders (5 days old)
$unpaid_orders = (array) wc_get_orders( array(
'limit' => -1,
'status' => 'pending',
'date_created' => '<' . ( $today - ($days_delay * $one_day) ),
) );
if ( sizeof($unpaid_orders) > 0 ) {
$cancelled_text = __("The order was cancelled due to no payment from customer.", "woocommerce");
// Loop through WC_Order Objects
foreach ( $unpaid_orders as $unpaid_order ) {
$order->update_status( 'cancelled', $cancelled_text );
}
}
// Schedule the process to the next day (executed once restriction)
update_option( 'unpaid_orders_daily_process', $today + $one_day );
endif;
}
Upvotes: 0
Views: 4375
Reputation: 637
As this action is only called when an order status is changed, it may not be called if there is no new event in 30m or 1h time interval. Anyway in case you want to change the interval to 1h you should perform something like this:
add_action( 'woocommerce_order_status_changed', 'hourly_cancel_unpaid_orders', 10, 4 );
function hourly_cancel_unpaid_orders( $order_id, $old_status, $new_status, $order ) {
// Enable the process to be executed daily
if( in_array( $new_status, array('processing', 'completed') )
&& get_option( 'unpaid_orders_hourly_process' ) < time() ) :
$hours_delay = 1; // <=== SET the delay (number of hours to wait before cancelation)
$one_hour = 60 * 60;
$current_hour = strtotime( date('Y-m-d H:00:00') );
// Get unpaid orders (1 hours old)
$unpaid_orders = (array) wc_get_orders( array(
'limit' => -1,
'status' => 'pending',
'date_created' => '<' . ( $current_hout - ($hours_delay * $one_hour) ),
) );
if ( sizeof($unpaid_orders) > 0 ) {
$cancelled_text = __("The order was cancelled due to no payment from customer.", "woocommerce");
// Loop through WC_Order Objects
foreach ( $unpaid_orders as $unpaid_order ) {
$order->update_status( 'cancelled', $cancelled_text );
}
}
// Schedule the process to the next day (executed once restriction)
update_option( 'unpaid_orders_hourly_process', $current_hour + $one_hour );
endif;
}
The main thing I changed is to add also hour to the line strtotime( date('Y-m-d H:00:00') )
so it will not be the same each time that the function is running in one day.
If you want to have 30m intervals you should change that so that it will be on times like 2:30 3:00... I think the code would be like this:
$current_time = strtotime( date('Y-m-d H:i:00') );
$current_time = $current_time - $current_time % (30 * 60);
Please let me know whether this code does what you want or not.
Regarding the last order that you say it will be canceled automatically: I think it's because its status was pending
and is going to be completed
. So we pass the first if and it will also appear in the $unpaid_orders
query.
You could check that the orders you are canceling are not the same as this last order by:
foreach ( $unpaid_orders as $unpaid_order ) {
if ( $unpaid_order->get_id() != $order_id ) {
$order->update_status( 'cancelled', $cancelled_text );
}
}
Upvotes: 1