Save order meta when using the "woocommerce_new_order" hook - wordpress

I need to trace if a product is bought from a user coming from a specific site, that's why I need to set a meta for the order.
Let's focus on this piece of my code:
add_action( 'woocommerce_new_order', 'add_affilate_meta', 10, 1);
function add_affilate_meta( $order_id ){
$targetProd = 3115;
// check if product is present
$order = wc_get_order( $order_id );
$affiliationProduct = false;
foreach ($order->get_items() as $item_key => $item ):
$product_id = $item->get_product_id();
if($product_id == $targetProd):
$affiliationProduct = true;
break;
endif;
endforeach;
// this is just for debug
update_post_meta( $order_id, 'test_affiliate', $product_id );
... some other stuff ...
}
$affiliationProduct and, consequently, test_affiliate meta are always false/empty. Why? I'm sure, of course, that the article is present in the order. It seems like the order is not "ready" when I try to analyze it's content.
I cannot find any other way to debug the code, because I cannot var_dump() nothing without causing JSON errors.
Any help will be appreciated.

You can indeed use the woocommerce_new_order hook, but what is missing in your code is $order->save();
So you get:
function action_woocommerce_new_order( $order_id ) {
// Get the WC_Order Object
$order = wc_get_order( $order_id );
// Setting
$target_prod = 30;
// Initialize
$affiliation_product = false;
$product_id = 0;
// Loop trough
foreach ( $order->get_items() as $item_key => $item ) {
// Get
$product_id = $item->get_product_id();
// Compare
if ( $product_id == $target_prod ) {
$affiliation_product = true;
break;
}
}
// Update meta data
$order->update_meta_data( 'test_affiliate', $product_id );
// Save
$order->save();
// When true
if ( $affiliation_product ) {
// Do something
}
}
add_action( 'woocommerce_new_order', 'action_woocommerce_new_order', 10, 1 );

Related

No result when using the "woocommerce_new_order" hook when order has status "processing"

I need Telegram notifications to be sent when the order status is "Processing", so I have this code:
add_action( 'woocommerce_new_order', 'new_order_send_tg', 1, 1 );
function new_order_send_tg( $order_id ) {
$order = wc_get_order( $order_id );
$msg .= '✅ Order № '.$order_id . ' *success*';
$userId = '-xxxxxxxxxxxxxx'; // ID Chat
$token = 'xxxxxxxxxxxxxxxxxxxxxxxx'; // Token bot
if( 'processing' == $order->get_status() ) {
file_get_contents('https://api.telegram.org/bot'. $token .'/sendMessage?chat_id='. $userId .'&text=' . urlencode($msg) . '&parse_mode=markdown'); // Send
} else {
//
}
}
Without the if( 'processing' == $order->get_status() ) condition, everything works as expected. But with the if condition added this doesn't seem to be the case. Any advice?
It looks like the woocommerce_new_order hook is executed before the order status is changed to processing
function action_woocommerce_new_order( $order_id, $order = null ) {
// Get order
$order = is_a( $order, 'WC_Order' ) ? $order : wc_get_order( $order_id );
$status = $order->get_status();
// Result: status = pending
}
add_action( 'woocommerce_new_order', 'action_woocommerce_new_order', 10, 2 );
So you can use the woocommerce_order_status_' . $status_transition['to'] composite action hook instead, where you will replace $status_transition[to] by processing
function action_woocommerce_order_status_processing( $order_id, $order ) {
// Do your thing
}
add_action( 'woocommerce_order_status_processing', 'action_woocommerce_order_status_processing', 10, 2 );

Add extra customer note on order creation for specific user role in WooCommerce

For a specific user role I want to add a specific customer note to the order.
This is my code:
add_action( 'woocommerce_new_order', 'add_customer_note_user_role' );
function add_customer_note_user_role( $order_id ) {
$user_info = get_userdata(get_current_user_id());
if ( $user_info->roles[0]=="administrator" ) {
$order = wc_get_order( $order_id );
// The text for the note
$note = 'This is the message';
// Add the note
$order->add_order_note( $note );
// Save the data
$order->save();
}
}
But this puts the message in the wrong place. When you check an order in the backend it's been displayed in the purple message boxes.
I want the message to be displayed as a customer note which is displayed under the shipping address. Because my API is picking up the notes from that place and puts them in our ERP.
I tried to change
$order->add_order_note( $note );
to
$order->add_order_note( $note, 'is_customer_note', true );
But without the desired result, any advice?
To display the message as a customer note displayed below the shipping address, you can use set_customer_note() instead.
So you get:
function action_woocommerce_new_order( $order_id ) {
// Get the WC_Order Object
$order = wc_get_order( $order_id );
// Get the WP_User Object
$user = $order->get_user();
// Check for "administrator" user roles only
if ( is_a( $user, 'WP_User' ) && in_array( 'administrator', (array) $user->roles ) ) {
// The text for the note
$note = 'This is the message';
// Set note
$order->set_customer_note( $note );
// Save
$order->save();
}
}
add_action( 'woocommerce_new_order', 'action_woocommerce_new_order', 10, 1 );
To keep the original message (when NOT empty) use this instead:
function action_woocommerce_new_order( $order_id ) {
// Get the WC_Order Object
$order = wc_get_order( $order_id );
// Get the WP_User Object
$user = $order->get_user();
// Check for "administrator" user roles only
if ( is_a( $user, 'WP_User' ) && in_array( 'administrator', (array) $user->roles ) ) {
// The text for the note
$note = 'This is the message';
// Get customer note
$customer_note = $order->get_customer_note();
// NOT empty
if ( ! empty ( $customer_note ) ) {
$note = $customer_note . ' | ' . $note;
}
// Set note
$order->set_customer_note( $note );
// Save
$order->save();
}
}
add_action( 'woocommerce_new_order', 'action_woocommerce_new_order', 10, 1 );

Auto update product custom field based on WooCommerce product stock

I have an ACF field "DeliverySpeed" attached to Products with the values 'Fast' and 'Slow'
I would like to update this field to change to value 'Slow' every time the product stock is zero or less (product on backorder)
I am still learning PHP so far this is what I got to but I am sure different things are missing, I just don't know in which direction to go:
based on acf update field documentation
function automatically_change_delivery( ) {
global $product;
$fieldkey = "DeliverySpeed";
$valueslow = "Slow";
if($product->get_stock_quantity()<0) { update_field( $field_key, $valueslow, $post_id );
} }
Thank you in advance for the attention and advice.
The following code will update automatically your product custom field once order has reduced product stock levels. So when product has stock the custom field value will be 'Fast' otherwise 'Slow'.
The code:
add_action( 'woocommerce_payment_complete', 'update_product_custom_field_after_reduced_stock_levels', 20, 2 );
add_action( 'woocommerce_order_status_completed', 'update_product_custom_field_after_reduced_stock_levels', 20, 2 );
add_action( 'woocommerce_order_status_processing', 'update_product_custom_field_after_reduced_stock_levels', 20, 2 );
add_action( 'woocommerce_order_status_on-hold', 'update_product_custom_field_after_reduced_stock_levels', 20, 2 );
function update_product_custom_field_( $order_id, $order = '' ) {
// Continue only when order has reduced product stock levels
if ( wc_string_to_bool( get_post_meta( $order_id, '_order_stock_reduced', true ) ) )
return $order_id; // Exit
if( ! $order || ! is_a( $order, 'WC_Order') ) {
$order = wc_get_order( $order_id ); // Get the WC_Order object if it's empty
}
$field_key = 'DeliverySpeed';
// Loop through order items
foreach ( $order->get_items() as $item ) {
$product = $cart_item['data'];
$product_id = $product->get_id();
$stock_qty = $product->get_stock_quantity();
$field_value = get_field( $field_key, $product_id ); // Get ACF field value
if ( $stock_qty <= 0 && $field_value === 'Fast' ) {
update_field( $field_key, 'Slow', $product_id );
}
elseif ( $stock_qty > 0 && $field_value === 'Slow' ) {
update_field( $field_key, 'Fast', $product_id );
}
}
}
Code goes in functions.php file of the active child theme (or active theme). It should works.

Stop WooCommerce For Reducing Stock On Abandoned Admin New Order

If an admin goes to create an order but abandons it, the stock level is still reduced.
Steps to reproduce:
install WordPress
install WooCommerce
create simple product and tick "manage stock?" and set stock level to 10
view on front-end (see screenshot before.png)
as admin create new order but don't save it (new -> order -> add item -> exit page)
view on front-end (see screenshot after.png)
Notice stock level has been reduced even those order wasn't saved.
Is there anyway to avoid this?
Before.png:
After.png
I have worked on this issue and wrote a basic code for that.
Hooks used: "woocommerce_order_item_add_action_buttons" for Admin Order Add Item(s) and "woocommerce_process_shop_order_meta" for Admin Order Creation/Update.
First part: Stop reducing stocks of items that added while the order has not been created.
// define the woocommerce_order_item_add_action_buttons callback
function action_woocommerce_order_item_add_action_buttons( $order ) {
$orderID = $order->ID;
//check if this is the admin manual order creation
if(get_post_status($orderID) == "auto-draft" && get_post_type($orderID) == "shop_order")
{
foreach( $order->get_items() as $item_id => $item )
{
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$product_quantity = $item->get_quantity();
if($variation_id == 0)
{
$product = wc_get_product($product_id);
wc_update_product_stock($product, $product_quantity, 'increase');
}
else
{
$variation = wc_get_product($variation_id);
wc_update_product_stock($variation, $product_quantity, 'increase' );
}
// The text for the note
$note = __("Stock incremented due to the auto draft post type. Stock for each item will be decremented when this order created.");
// Add the note
$order->add_order_note( $note );
}
}
};
// add the action
add_action( 'woocommerce_order_item_add_action_buttons', 'action_woocommerce_order_item_add_action_buttons', 10, 1 );
Second Part: Reduce stocks of items that added if order is created.
add_action( 'woocommerce_process_shop_order_meta', 'woocommerce_process_shop_order', 10, 2 );
function woocommerce_process_shop_order ( $post_id, $post ) {
$order = wc_get_order( $post_id );
//check if this is order create action, not an update action
if(get_post_status($post_id) == "draft" && get_post_type($post_id) == "shop_order")
{
foreach( $order->get_items() as $item_id => $item )
{
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$product_quantity = $item->get_quantity();
if($variation_id == 0)
{
$product = wc_get_product($product_id);
wc_update_product_stock($product, $product_quantity, 'decrease');
}
else
{
$variation = wc_get_product($variation_id);
wc_update_product_stock($variation, $product_quantity, 'decrease' );
}
// The text for the note
$note = __("Stock decremented for all items in this order.");
// Add the note
$order->add_order_note( $note );
}
}
}
Tested and works fine. I hope this will help you. Have a good day.

Save WooCommerce Order Product Name to User Meta Custom Field

Caveat: I'm a solo freelance designer not a developer ;-)
I've created a custom field in the Wordpress user meta called membership.
I've tried the following code to save the WooCommerce Product Name to the membership custom field on checkout with help from this answer.
Updated attempt:
function wascc_woocommerce_checkout_update_user_meta_membership ( $customer_id, $posted ) {
if (isset($posted['$orderid'])) {
$order = $posted['$orderid'];
}
$theorder = new WC_Order( $order );
$items = $theorder->get_items();
foreach ( $items as $item ) {
$product_name = $item['name'];
}
if (!(empty($product_name))) {
update_user_meta( $customer_id, 'membership', $product_name);
}
}
add_action( 'woocommerce_checkout_update_user_meta', 'wascc_woocommerce_checkout_update_user_meta_membership', 10, 2 );
It produces no error, but does not save the product name to membership.
Help appreciated.
Last question may be related.
as I've commented out, you should use woocommerce_checkout_update_order_meta.
Something like this:
function wascc_woocommerce_checkout_update_user_meta_membership ( $order_id ) {
$theorder = new WC_Order( $order_id );
$items = $theorder->get_items();
foreach ( $items as $item ) {
$product_name = $item['name'];
}
if (!(empty($product_name))) {
// Gets the customer/user ID associated with the order. Guests are 0.
$customer_id = $theorder->get_user_id();
update_user_meta( $customer_id, 'membership', $product_name );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'wascc_woocommerce_checkout_update_user_meta_membership' );
I have doubts with your foreach loop though... you are looping just to get the last item?

Resources