Woocommerce which hook to use for order status changes - wordpress

In my custom plugin I need to catch every time an order status changes from wc_on_hold to wc_completed so I tried to write down:
function so_status_completed( $order_id, $old_status, $new_status ) {
// if user is active , then get order amount and do all other personal calculations
global $wpdb;
$order = wc_get_order( $order_id );
//$payment_method = $order->get_payment_method(); //returns payment method bacs,cheque,cod etc
$user_id = $order->get_user_id();
$total = $order->get_total();
$order_data = $order->get_data();
//$order_total = $order->get_formatted_order_total();
$order_total = $order->get_total();
echo '<script>console.log("Debug Objects: Check order_total ' . $order_total. '");</script>';
}
add_action('woocommerce_order_payment_status_changed','so_status_completed',10,1);
But when I tried to change an order test from suspended to completed, I couldn't get on Chrome's Console that echo giving me the order price.....maybe using add_action isn't the right way to put a listener to that event?
Plus, since I am just here, I am using $order-get_total() which I searched on the net about his functionality but no deep docs found so I want to ask you if that method is the right one to retrieve order amount without fee applied?
Thanks! Cheers!!!

"I need to catch every time an order status changes from wc_on_hold to wc_completed"
You would need to change your hook to woocommerce_order_status_changed.
"I couldn't get on Chrome's Console that echo giving me the order price"
You can't use javascript and console.log in this hook. You would need to either use the die function or the error_log function to log it to your debug.log file.
Using die function
add_action('woocommerce_order_status_changed', 'so_status_completed', 10, 3);
function so_status_completed($order_id, $old_status, $new_status)
{
$order = wc_get_order($order_id);
//$order_total = $order->get_formatted_order_total();
$order_total = $order->get_total();
die($order_total);
}
Using error_log function
dd_action('woocommerce_order_status_changed', 'so_status_completed', 10, 3);
function so_status_completed($order_id, $old_status, $new_status)
{
$order = wc_get_order($order_id);
//$order_total = $order->get_formatted_order_total();
$order_total = $order->get_total();
error_log(print_r('order total: ' . $order_total, true));
}
Second question
"since I am just here, I want to ask you if $order-get_total() is the right one to retrieve order amount without fee applied?"
You might want to take a look at the $order->get_subtotal() for the totals before shipping, coupons and taxes:
Please see these related answers:
https://stackoverflow.com/a/47782647/15040627
https://stackoverflow.com/a/61102779/15040627
https://stackoverflow.com/a/32634480/15040627
https://stackoverflow.com/a/44708344/15040627

Related

woocommerce_before_order_object_save hook not work [duplicate]

Hello I'm trying to create a function in the mu-plugins to prevent certain users to change order status from specific order statuses to specific order statuses.
I've been looking everywhere and I have tried many different ways, but nothing seems to work.
Actually the function is running using woocommerce_order_status_changed action hook. The thing is that this hook runs after the order status has already been changed, what's causing an infinite loop.
The most useful hook I found seems to be woocommerce_before_order_object_save.
I found "Add an additional argument to prevent 'woocommerce_order_status_changed' from being called" useful related thread on WooCommerce Github.
I tried using #kloon code snippet solution:
add_filter( 'woocommerce_before_order_object_save', 'prevent_order_status_change', 10, 2 );
function prevent_order_status_change( $order, $data_store ) {
$changes = $order->get_changes();
if ( isset( $changes['status'] ) ) {
$data = $order->get_data();
$from_status = $data['status'];
$to_status = $changes['status'];
// Do your logic here and update statuses with CRUD eg $order->set_status( 'completed' );
// Be sure to return the order object
}
return $order;
}
but $changes variable is always an empty array.
I tried to use wp_insert_post_data Wordpress hook, but when I set:
$data['post_status'] = "some status";
it just prevents the whole update (the whole new data) from being saved.
This is the code I would like to run is:
function($data){
if($data['order_status'] == 'comlpeted' && $data['new_order_status'] == 'proccessing'){
// prevent the order status from being changed
$data['new_order_status'] = $data['order_status'];
}
few more if conditions...
return $data;
}
Any help or advise is appreciated.
Based on #kloon code snippet, I have been able to get the old order status and the new order status. Then I can disable any status change from a specific defined order status to a specific defined order status.
With the following code, specific defined user roles can't change order status from "processing" to "on-hold":
add_filter( 'woocommerce_before_order_object_save', 'prevent_order_status_change', 10, 2 );
function prevent_order_status_change( $order, $data_store ) {
// Below define the disallowed user roles
$disallowed_user_roles = array( 'shop_manager');
$changes = $order->get_changes();
if( ! empty($changes) && isset($changes['status']) ) {
$old_status = str_replace( 'wc-', '', get_post_status($order->get_id()) );
$new_status = $changes['status'];
$user = wp_get_current_user();
$matched_roles = array_intersect($user->roles, $disallowed_user_roles);
// Avoid status change from "processing" to "on-hold"
if ( 'processing' === $old_status && 'on-hold' === $new_status && ! empty($matched_roles) ) {
throw new Exception( sprintf( __("You are not allowed to change order from %s to %s.", "woocommerce" ), $old_status, $new_status ) );
return false;
}
}
return $order;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.

How to bulk change external column values defined in Orders table and stored in database?

I am working on return processes in Woocommerce. I am getting returned or not returned information from table named return_table. I added this information to a new column in the orders table. The table is simply as follows:
I need to make changes to the column I just added. For example, I want to update the value from no return to returned. For this, I should make a new definition in the default actions column or add a new bulk action. I decided to add bulk action as it is more functional:
However, I was unable to make any progress after that. I've done research on Stackoverflow or other platforms. I found an answer to add buttons (new actions) but still couldn't solve the problem:
Source-1
Others I've found are mostly geared towards changing the "Order Status":
Source-2, Source-3, Source-4...
So, i've been researching this for a while and haven't found a solution. I will be grateful if anyone can help.
My Code:
<?php
// Add new column
add_filter( 'manage_edit-shop_order_columns', 'return_column', 11);
function return_column( $columns ) {
$columns['return_column'] = 'Return Status';
return $columns;
}
// Add the data to the custom columns
add_action( 'manage_shop_order_posts_custom_column', 'return_column_content',11);
function return_column_content ($column){
if($column == 'return_column'){
// Create required global variables
global $post, $wpdb;
$order = wc_get_order( $post->ID );
$order_id = $order->get_id();
$result = $wpdb->get_results("SELECT return_status FROM {$wpdb->prefix}return_table WHERE order_id ='$order_id' LIMIT 1");
if(count($result)):
echo $result[0]->return_status;
else:
echo '-';
endif;
}
}
//Add new order status to bulk change
add_filter( 'bulk_actions-edit-shop_order', 'refund_register_bulk_action' );
function refund_register_bulk_action( $bulk_actions ) {
$bulk_actions['mark_returned'] = 'Returned';
$bulk_actions['mark_not_returned'] = 'Not Returned';
return $bulk_actions;
}

Troubleshooting Woocommerce order emails customized according to shipping method

Currently my two options for shipping are regular shipping (using the Flexible Shipping plugin) and local pickup. I would like my Completed Order emails to be customized according to the shipping method used for the order (e.g. I'd like the header to say "We've shipped your order!" for regular shipping and "Your order is ready for pickup!" for local pickup).
Here's my first attempt at modifying the text through my child theme's functions.php (code based on LoicTheAztec answer in this thread). Currently, instead of inserting the desired text, it shows the default text that I entered in Woocommerce>Settings>Emails>Order Completed>Completed order>Email heading, regardless of shipping method selected.
add_action( 'woocommerce_email_header ', 'modify_header_by_shipping', 10, 2 );
function modify_header_by_shipping( $email_heading, $email ) {
// Only modify "Customer Completed Order" email notification
if( 'customer_completed_order' != $email->id ) return;
//Initialize variable
$found = false;
// Get $order object from $email
$order = $email->object;
// Iterating through Order shipping methods
foreach($order->get_shipping_methods() as $value){
$rate_id = $value->get_method_id(); // Get the shipping rate ID
if ( 'shipping_method_0_local_pickup5' == $rate_id )
$found = true;
}
if ($found)
echo '<p>'.__("Your order is ready for pickup!","woocommerce").'</p>';
else
echo '<p>'.__("We've shipped your order!","woocommerce").'</p>';
}
Here's my ordering page: https://carrickseeds.ca/checkout/

WooCommerce Eway plugin not creating customer token for new customer

I have setup a fresh docker container with Wordpress 5.0.3 and the latest WC and WC Eway plugin (WooCommerce eWAY Gateway).
Created a store with some products, hooked up my Eway sandbox environment, enabled Save Cards (which would enable the token) and created an order.
After checking the post_meta in my DB for the order, I didn't see a _eway_token_customer_id field. While being logged in as a customer, I tried again and with the new order I still do not get a token.
The reason for this tests is that I got this strange behaviour in my real, new website, where the first order with a NEW customer, doesn't result in a token.
However, when I create a second order whilst being logged in, I do get a _eway_token_customer_id value within the order_meta.
It is imperative for me to get that token with the first order, because after that I will auto renew the product using the tokenp ayment option.
Debugging this issue is hell, and I find it very disconcerting that on my fresh WP installation I get no token at all.
Is there anyone that has a bright idea?
**update
After some digging around in the Eway Plugin, I found out that the first time I do an order, the function request_access_code() from the class WC_Gateway_EWAY is checking if there is a token in the database for this user.
The function body:
protected function request_access_code( $order ) {
$token_payment = $this->get_token_customer_id( $order );
if ( $token_payment && 'new' === $token_payment ) {
$result = json_decode( $this->get_api()->request_access_code( $order, 'TokenPayment', 'Recurring' ) );
} elseif ( 0 === $order->get_total() && 'shop_subscription' === ( version_compare( WC_VERSION, '3.0', '<' ) ? $order->order_type : $order->get_type() ) ) {
$result = json_decode( $this->get_api()->request_access_code( $order, 'CreateTokenCustomer', 'Recurring' ) );
} else {
$result = json_decode( $this->get_api()->request_access_code( $order ) );
}
if ( isset( $result->Errors ) && ! is_null( $result->Errors ) ) {
throw new Exception( $this->response_message_lookup( $result->Errors ) );
}
return $result;
}
The function handles three possible outcomes:
1) new customer: results in calling `$this->get_api()->request_access_code( $order, 'TokenPayment', 'Recurring' )` <-- this is the one we are after!
2) shop_subscription: calls `$this->get_api()->request_access_code( $order, 'CreateTokenCustomer', 'Recurring' )`
3) else..: calls `$this->get_api()->request_access_code( $order )`
What is happening during debugging, is that the $token_payment variable has the value of an empty string for a new customer, instead of new.
So I will attempt to fix this, either via a filter/action hook, or figure out why this is happening.
When I forced the function the always use the first if block, I got my token. :)
**Update 2:
I tested with an existing user account, created a new order.
When I look in the post_meta table:
Voila, the new value is present.
However, when I am not logged in and I create an account, the new value is not added and that is where it goes wrong.
A temp fix would be to use a hook and add the new value to the order so that when get_token_customer_id is called it retrieves a new value and not an empty string.
I think this is a bug, since this value should be added. It explains why the second transactions get the token but not the first.
If only Woocommerce Eway plugin had a git repo.... I could flag an issue or fork it.
***Solution without hack
Added this to my plugin (or functions.php if you like):
add_action( 'woocommerce_checkout_order_processed', function( $order_id, $posted_data, $order ) {
update_post_meta( $order_id, '_eway_token_customer_id', 'new' );
}, 10, 3);
This will add the new value when you checkout with a non-existent user.
The token was added nicely after adding my creditcard details.
The matter of the fact stays that the plugin still has a bug, which you can work around.

Trigger woocommerce_payment_complete when creating order manually

I have a hook to woocommerce_payment_complete, in which I send the order to the distributor. This is working fine.
Now, since I'm also selling through 3rd party marketplace, sometimes I want to create an order form the admin panel, and I expect the woocommerce_payment_complete hook to be triggered by setting the order status to 'Processing' but it's not.
Is there any way to trigger this hook by creating an order manually?
Thanks
You can use the following to set 'processing' for admin orders. action_woocommerce_process_shop_order_meta is used to detect the order update.
// define the woocommerce_admin_order_actions_end callback
function action_woocommerce_admin_order_actions_end( $order_id ) {
global $woocommerce;
if (!$order_id)
return;
$order = new WC_Order($order_id);
$order_status = $order->get_status();
if ($order_status != "failed") {
$order->update_status('processing');
}
};
// add the action
add_action( 'action_woocommerce_process_shop_order_meta', 'action_woocommerce_admin_order_actions_end', 10, 1 );

Resources