First of all, Thanks to a guys who helped me write a script which changes order status to completed(script below).
Unfortunately it doesn't trigger subscription to go active. When I manually change order status in WooCommerce it does. So my idea was to activate a subscription based on given order_id.
How to change subscription status from "Pending" to "Active" based on order_id in WooCommerce?
Here is a code which changes the order status when I use 100% coupon and it works: in functions.php
<?php
add_action('woocommerce_checkout_order_processed', '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->get_total() == 0) {
$order->update_status('processing');
$order->update_status('completed');
}
}
I have found smth like this to activate those subscriptions, but I cannot implement this, maybe somebody can help me solve this?
Here is the code I have found on github
/**
* Activates all the subscriptions created by a given order.
*
* #param WC_Order|int $order The order or ID of the order for which subscriptions should be marked as activated.
* #since 1.0
*/
public static function activate_subscriptions_for_order( $order ) {
$subscriptions = wcs_get_subscriptions_for_order( $order );
if ( ! empty( $subscriptions ) ) {
foreach ( $subscriptions as $subscription ) {
try {
$subscription->update_status( 'active' );
} catch ( Exception $e ) {
// translators: $1: order number, $2: error message
$subscription->add_order_note( sprintf( __( 'Failed to activate subscription status for order #%1$s: %2$s', 'woocommerce-subscriptions' ), is_object( $order ) ? $order->get_order_number() : $order, $e->getMessage() ) );
}
}
do_action( 'subscriptions_activated_for_order', $order );
}
}
I tried this but it doesn't work:
add_action('woocommerce_checkout_order_processed', '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->get_total() == 0) {
$order->update_status('processing');
$order->update_status('completed');
}
}
do_action('woocommerce_checkout_order_processed', 'subscriptions_activated_for_order', $order_id );
function activate_subscriptions_for_order( $order_id ) {
$subscriptions = wcs_get_subscriptions_for_order( $order_id );
if ( ! empty( $subscriptions ) ) {
foreach ( $subscriptions as $subscription ) {
try {
$subscription->update_status( 'active' );
} catch ( Exception $e ) {
// translators: $1: order number, $2: error message
$subscription->add_order_note( sprintf( __( 'Failed to activate subscription status for order #%1$s: %2$s', 'woocommerce-subscriptions' ), is_object( $order_id ) ? $order->get_order_number() : $order_id, $e->getMessage() ) );
}
}
}
}
Best Regards
There are errors and mistakes in your code. You could try to merge everything together like:
add_action('woocommerce_checkout_order_processed', '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->get_total() == 0) {
// $order->update_status('processing'); // Unneeded as you complete the order
$order->update_status('completed');
}
$subscriptions = wcs_get_subscriptions_for_order( $order );
if ( ! empty( $subscriptions ) ) {
activate_subscriptions_for_order( $order );
}
}
Code goes in functions.php file of the active child theme (or active theme). It could work.
Or you could keep:
add_action('woocommerce_checkout_order_processed', '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->get_total() == 0) {
// $order->update_status('processing'); // Unneeded as you complete the order
$order->update_status('completed');
}
}
and use the hook woocommerce_order_status_completed triggered when order status is "completed" as follows to activate the subscription related to the order:
add_action( 'woocommerce_order_status_completed', 'order_complete_activate_subscription', 10, 2 );
function order_complete_activate_subscription( $order_id, $order ) {
$subscriptions = wcs_get_subscriptions_for_order( $order );
if ( ! empty( $subscriptions ) ) {
activate_subscriptions_for_order( $order );
}
}
Code goes in functions.php file of the active child theme (or active theme). It could work.
Related
I'm looking for a way to highlight the admin order list line based on the order shipping method. (specifically for local pickup)
Based on Highlight WooCommerce admin orders list based on order payment method
anwser code, I have changed $payment_method for $shipping_method and .type-shop_order.cod for .type-shop_order.local_pickupcode for shipping methods but it did nothing in admin panel
I have also tried using shipping method title instead of $shipping_method, but it did nothing.
i have also tried to get all classes with
public function get_shipping_classes() {
if ( empty( $this->shipping_classes ) ) {
$classes = get_terms(
'product_shipping_class',
array(
'hide_empty' => '0',
'orderby' => 'name',
)
);
$this->shipping_classes = ! is_wp_error( $classes ) ? $classes : array();
}
return apply_filters( 'woocommerce_get_shipping_classes', $this->shipping_classes );
}
but it hid all orders except local pickup.
Here is the code:
function filter_post_class( $classes, $class, $post_id ) {
// Determines whether the current request is for an administrative interface page
if ( ! is_admin() ) return $classes;
// Get the current screen object
$current_screen = get_current_screen();
// Only when
if ( $current_screen->id === 'edit-shop_order' ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $post_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get the payment method
$shipping_method = $order->get_shipping_method();
//NOT empty
if ( ! empty( $shipping_method ) ) {
$classes[] = $shipping_method;
}
}
}
// Return the array
return $classes;
}
add_filter( 'post_class', 'filter_post_class', 10, 3 );
// Add CSS
function action_admin_head() {
// Get the current screen object
$current_screen = get_current_screen();
// Only when
if ( $current_screen->id === 'edit-shop_order' ) {
echo '<style>
.type-shop_order.local_pickup {
background-color: #e9a5a5 !important;
}
</style>';
}
}
add_action( 'admin_head', 'action_admin_head' );
Any advice how to change it please? i am not php coder so this is kinda google try thing for me :/
i have made it thanks to CBroe.
function filter_post_class( $classes, $class, $post_id ) {
// Determines whether the current request is for an administrative interface page
if ( ! is_admin() ) return $classes;
// Get the current screen object
$current_screen = get_current_screen();
// Only when
if ( $current_screen->id === 'edit-shop_order' ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $post_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get the shipping method
$shipping_method = #array_shift($order->get_shipping_methods());
$shipping_method_id = $shipping_method['method_id'];
//NOT empty
if ( ! empty( $shipping_method ) ) {
$classes[] = $shipping_method_id;
}
}
}
// Return the array
return $classes;
}
add_filter( 'post_class', 'filter_post_class', 10, 3 );
// Add CSS
function action_admin_head() {
// Get the current screen object
$current_screen = get_current_screen();
// Only when
if ( $current_screen->id === 'edit-shop_order' ) {
echo '<style>
.type-shop_order.local_pickup {
background-color: #e9a5a5 !important;
}
</style>';
}
}
add_action( 'admin_head', 'action_admin_head' );
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 know how to add a meta on the order shipping method :
$shippingMethodItem->update_meta_data('num_packages', 0);
But I want to add/update this meta automatically when the order is placed OR when someone add/edit order items manually.
I already tried with no result :
add_action( 'woocommerce_checkout_create_order_shipping_item', [$this, 'actionCheckoutCreateOrderShippingItem'] );
...
public function actionCheckoutCreateOrderShippingItem ($shippingMethodItem, $package_key, $package, $order)
{
$shippingMethodItem->update_meta_data( 'num_packages', 0);
$shippingMethodItem->save();
}
The following will add custom specific order "shipping" item meta data on orders via checkout and/or manual admin orders too:
// For new orders via checkout
add_action( 'woocommerce_checkout_create_order_shipping_item', 'action_checkout_create_order_shipping_item', 10, 4 );
function action_checkout_create_order_shipping_item ( $item, $package_key, $package, $order ){
$item->update_meta_data( 'num_packages', 0 );
}
// For manual admin orders
add_action( 'woocommerce_saved_order_items', 'action_saved_order_items_callback', 10, 2 );
function action_saved_order_items_callback( $order_id, $items ) {
if ( isset( $items['shipping_method_id'] ) ) {
foreach( $items['shipping_method_id'] as $item_id ) {
$num_packages = wc_get_order_item_meta($item_id, 'num_packages');
if ( empty($num_packages) || $num_packages != 0 ) {
wc_update_order_item_meta( $item_id, 'num_packages', 0 );
}
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
To use this code in a plugin with a class (OOP), add the following inside the constructor function:
// For checkout order
add_action( 'woocommerce_checkout_create_order_shipping_item', [$this, 'action_checkout_create_order_shipping_item'], 10, 4 );
// For checkout order
add_action( 'woocommerce_saved_order_items', [$this, 'action_saved_order_items_callback'], 10, 2 );
And the following outside the constructor function:
public function action_checkout_create_order_shipping_item ( $item, $package_key, $package, $order ){
$item->update_meta_data( 'num_packages', 0 );
}
public function action_saved_order_items_callback( $order_id, $items ) {
if ( isset( $items['shipping_method_id'] ) ) {
foreach( $items['shipping_method_id'] as $item_id ) {
$num_packages = wc_get_order_item_meta($item_id, 'num_packages');
if ( empty($num_packages) || $num_packages != 0 ) {
wc_update_order_item_meta( $item_id, 'num_packages', 0 );
}
}
}
}
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
In WooCommerce any order placed with the BACS (direct bank transfer) is set to "on-hold".
How would one go about changing this automatically to processing?
I wan't it to work inside the functions.php
I have the following code but that doesn't work:
add_filter( 'woocommerce_payment_complete_order_status', 'rfvc_update_order_status', 10, 2 );
function rfvc_update_order_status( $order_status, $order_id ) {
$order = new WC_Order( $order_id );
if ( 'on-hold' == $order_status && 'on-hold' == $order->status ) {
return 'processing';
}
return $order_status;
}
Any help would be great!
New 2020 update
WooCommerce version 3.4 has introduced a much better hook than woocommerce_thankyou or woocommerce_thankyou_bacs, that allows to change the default order status for BACS payment method.
Using this hook will:
clearly lighten the necessary code,
avoid "on-hold" notification to the customer when a BACS order is placed.
So use instead the following:
add_filter( 'woocommerce_bacs_process_payment_order_status','filter_bacs_process_payment_order_status_callback', 10, 2 );
function filter_bacs_process_payment_order_status_callback( $status, $order ) {
return 'processing';
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Original answer:
Update (added a version for woocommerce 3+ at the end)
It seems that woocommerce_payment_complete_order_status action hook doesn't trigger with BACS payment method.
Based on this thread, 'woocommerce_thankyou' action hook does the job:
add_action( 'woocommerce_thankyou', 'bacs_order_payment_processing_order_status', 10, 1 );
function bacs_order_payment_processing_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 || 'pending' == $order->status) ) {
$order->update_status('processing');
} else {
return;
}
}
Code goes in function.php file of your active child theme (or active theme). tested and works.
For woocommerce 3+ versions:
Here we use the similar composite hook woocommerce_thankyou_{$order->get_payment_method()}:
add_action( 'woocommerce_thankyou_bacs', 'bacs_order_payment_processing_order_status', 10, 1 );
function bacs_order_payment_processing_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
if ( in_array( $order->get_status(), array('on-hold', 'pending') ) ) {
$order->update_status('processing');
} else {
return;
}
}
Code goes in function.php file of your active child theme (or active theme). tested and works.
There is a new filter which will allow you to set the status when BACS 'payment' is processed.
/**
* Change the default status when BACS 'payment' is processed.
*
* #see WC_Gateway_BACS::process_payment()
* woocommerce/includes/gateways/bacs/class-wc-gateway-bacs.php:362
* #since Mar 8, 2018
* #link https://github.com/woocommerce/woocommerce/blob/750fda3b1b55c55645f626d3873d956282e3ac1b/includes/gateways/bacs/class-wc-gateway-bacs.php#L364
*
* #filter woocommerce_bacs_process_payment_order_status
* #priority 10
* #args 2
*
* #param string $status Status to filter. Default 'on-hold'.
* #param WC_Order $order
* #return string New status 'processing'.
*/
add_filter( 'woocommerce_bacs_process_payment_order_status', function( $status = 'on_hold', $order = null ) {
return 'processing';
}, 10, 2 );
Try changing the code to this:
function rfvc_update_order_status( $order_status, $order_id ) {
$order = new WC_Order( $order_id );
if ( 'on-hold' == $order_status && 'on-hold' == $order->status ) {
$order->update_status('processing', 'order_note');
}
return $order_status;
}
The key change here is this:
$order->update_status('processing', 'order_note');
You can add order note too if you prefer.