I'm running into a problem with a custom function in a hook of WooCommerce orders status.
All I need is to show the order id, a custom field and the items of the order in error_log
function mysite_woocommerce_order_complete( $order_id ) {
error_log( "Payment has been received for order $order_id" );
$order = wc_get_order( $order_id );
$user_acct = get_post_meta( $order_id, 'billing_acc', true );
foreach ($order->get_items() as $item_id => $item_data) {
$product = $item_data->get_product();
$product_name = $product->get_name();
$item_quantity = $item_data->get_quantity();
error_log( "Product $product_name Quantity $item_quantity User $user_acct" );
}
}
add_action( 'woocommerce_order_status_completed', 'mysite_woocommerce_order_complete', 1, 1 );
This works perfectly when a customer complete payment and the product is virtual and downloadable, the order gets the status "Completed" and print what I need in error.log
[07-Jul-2020 20:18:53 UTC] Payment has been received for order 2846
[07-Jul-2020 20:18:53 UTC] Product Pacote Jered's 2 Quantity 2 User b1n
The problem is: My store accept a method of payment (bank transfer) and after the payment is verified by the admin, he needs to manually set the order as Completed.
In this action the hook isn't called, I need it to run just like the other way.
I've tried a lot of similar hooks, all ends with the same problem, it works if the order gets completed automatically but doesn't work if the order is completed manually.
Related
Sometimes the function woocommerce_thankyou is not called, but sometimes works fine.
Our code is:
add_action(‘woocommerce_thankyou’, ‘send_order_information_for_delivery’, 999, 1);
function send_order_information_for_delivery($order_id)
{
$order = wc_get_order($order_id);
$order_items = $order->get_items();
// … …
}
Any idea why sometimes doesn’t work?
The main objective of this method is to obtain the information of the purchase order and its items and to send them to another database through an API.
The strange thing is that in some orders the method is not called.
Instead, you could try to use the following hooked function, that will work for defined order statuses only once, after the delivery data is processed.
add_action( 'woocommerce_order_status_changed', 'delivery_information_process', 20, 4 );
function delivery_information_process( $order_id, $old_status, $new_status, $order ){
// Define the order statuses to check
$statuses_to_check = array( 'on-hold', 'processing' );
// Only "On hold" order status and "Free Shipping" Shipping Method
if ( $order->get_meta( '_delivery_check', true ) && in_array( $new_status, $statuses_to_check ) )
{
// Getting all WC_emails objects
foreach($order->get_items() as $item_id => $item ){
$product = $item->get_product();
$sku = $product->get_sku();
}
## ==> Process delivery data step
// Once delivery information is send or processed ==> update '_delivery_check' to avoid repetitions
$order->update_meta_data( '_delivery_check', true );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I have a custom email that I created on New Orders. The aim is to let the client & the admin know that a new order has been finished (not paid). For that reason this order will be in pending. So this Email triggers when a New Order is created but when the administrator receives the email, products don't show up neither the correct subtotal. The total is right.
I am using a custom payment module but I think this is not the problem.
// New order notification only for "Pending" Order status
add_action( 'woocommerce_new_order', 'pending_new_order_notification', 20, 1 );
function pending_new_order_notification( $order_id ) {
//global $product;
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
$items = $order->get_items();
// Only for "pending" order status
if( ! $order->has_status( 'pending' ) ) return;
$wc_email = WC()->mailer()->get_emails()['WC_Email_New_Order'];
## -- Customizing Heading, subject (and optionally add recipients) -- ##
// Change Subject
$wc_email->settings['subject'] = __('{site_title} - New customer manual 2 order ({order_number}) - {order_date}');
// Change Heading
$wc_email->settings['heading'] = __('New customer Pending Order 2');
$wc_email->recipient .= ", $order->billing_email"; // Add email recipients (coma separated)
// Send "New Email" notification (to admin)
$wc_email->trigger( $order_id, $order );
//WC()->mailer()->emails['WC_Email_New_Order']->trigger( $order->get_id(), $order );
}
Same here.
I've tried with woocommerce_checkout_order_processed hook and it works.
Here is the action : add_action( 'woocommerce_checkout_order_processed', 'pending_new_order_notification', 20, 1 );
Hope that helps.
Is it possible to set all new orders to on hold without them going to processing first?
I need to capture the funds, but the order needs to go as on-hold rather than processing so that I can make sure the payment is received before triggering the email to our supplier.
I have found some code that allows me to change all new orders to on-hold, however when payment is authorized the order is set to processing.
pending payment --> processing (triggers email) --> on-hold
Screenshot of Order Notes that show this
This is the code that I found:
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_onhold_order' );
function custom_woocommerce_auto_onhold_order( $order_id ) {
global $woocommerce;
if ( !$order_id )
return;
$order = new WC_Order( $order_id );
$order->update_status( 'on-hold' ); //All new orders go to "on-hold"
}
I've been looking for the action which triggers the order status to be set to processing on payment authorization however I cannot find it.
All help is greatly appreciated!
Edit: I've managed to stumble upon a snippet of code that appears to work.
add_filter( 'woocommerce_payment_complete_order_status', 'custom_update_order_status', 10, 2 );
function custom_update_order_status( $order_status, $order_id ) {
return 'on-hold';
}
All orders start with a "pending" status in Woocommerce. That status is set before they go through the payment gateway...
There is no email notifications for "Pending" orders staus
You can try the following, that will set the status "on-hold" on order creation (The update status action comes once the order has been created ans saved in database):
add_action( 'woocommerce_checkout_create_order', 'force_new_order_status', 20, 1 );
function force_new_order_status( $order ) {
if( ! $order->has_status('on-hold') )
$order->set_status( 'on-hold', 'Forced status by a custom script' );
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I'm using WC with it's original membership plugin. I'm selling an online course (which is obviously a virtual product). Following should work: Someone purchases the product, order will be automatically completed, user gets an email with password and order confirmation.
I had following code in my functions.php:
/**
* Auto Complete all WooCommerce orders.
*/
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order');
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$order->update_status( 'completed' );
}
The Problem now is that the order is completed but the users won't
receive their login credentials and invoice. How do I handle this -
what's wrong?
With WooCommerce, I have the following hook in my function.php after the new order is submitted:
add_action( 'woocommerce_new_order', 'create_job_openings');
function create_job_openings($order_id) {
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach ($order->get_items() as $key => $item) {
$product_name = $item['name'];
var_dump($product_name);
}
}
The above code is not giving me any output i'e it is not entering inside the foreach loop that's why var_dump() not giving me any output, but if I mention the order_id specifically like create_job_openings($order_id=517) it works, even I tried echo $order_id before foreach loop, it is giving me the order_id, then why it is not entering the foreach loop?
note: when I try var_dump($items); before foreach loop its giving me
array(0) {
}
Why it is not able to get the product details even if there are products in it after new order is made?
Update 2 — The working solution (using an email notification hook)
The problem is when using email notification hook can fire an action 2 times, for example when a new order is made (notification for the Shop manager and notification for the customer).
You want to use this "New Order" event for Orders that are in "processing" status.
To avoid your action to be fired 2 times using New order notification WooCommerce event, we use 'customer_processing_order' instead of 'new_order' email ID (notification event).
Here we don't need to get the $order object, as we got it as an argument in this hooked function.
So here is your final functional code:
add_action( 'woocommerce_email_before_order_table', 'custom_action_on_completed_customer_email_notification', 10, 4 );
function custom_action_on_completed_customer_email_notification( $order, $sent_to_admin, $plain_text, $email ) {
if( 'customer_processing_order' == $email->id ){ // for processing order status customer notification…
foreach ($order->get_items() as $item_id => $item_values) {
$product_name = $item_values['name'];
echo $product_name;
break; // (optional) stop loop to first item
}
}
}
This is the validated and working answer to this question
Related Working Answers:
Sending an SMS for specific email notifications and order statuses
Avoid repetitive emails notification on some auto completed orders
Change the user role on purchase for specific products when order status is completed
Update 1 (hook alternative)
Trying using woocommerce_thankyou hook, that is fired on review order after order has been processed:
add_action( 'woocommerce_thankyou', 'create_job_openings', 10, 1 );
function create_job_openings( $order_id ) {
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
foreach ($order->get_items() as $item_id => $item_values) {
$product_name = $item_values['name'];
var_dump($product_name);
break; // (optional) stop loop to first item
}
}
(Not working for the OP)
You should try this instead wc_get_order() function this way and your code will be:
add_action( 'woocommerce_new_order', 'create_job_openings', 10, 1);
function create_job_openings($order_id) {
$order = wc_get_order( $order_id );
$order_items = $order->get_items();
foreach ($order_items as $item_id => $item_values) {
$product_name = $item_values['name'];
var_dump($product_name);
break; // (optional) stops on first item
}
}
You can have a look to
How to get WooCommerce order details where a lot of things are explained…
(Not working for the OP)
you can use save_post action when post is added in wordpress. for more visit: Link
function wc_order_add_action($post_id, $post, $update)
{
$post_type = get_post_type($post_id);
// If this isn't a 'shop_order' post, don't update it.
if ("shop_order" != $post_type) return;
$order = wc_get_order($post_id);
foreach($order -> get_items() as $key => $item)
{
$product_name = $item['name'];
var_dump($product_name);
}
}
add_action('save_post', 'wc_order_add_action');
To get the items and product details from the new order, you simply need to request two parameters for woocommerce_new_order. The second passed parameter will be the new order object. Example:
add_action( 'woocommerce_new_order', 'so41605256_new_order', 10, 2 );
function so41605256_new_order( $order_id, $order ) {
// Directly access items from $order variable, do not use wc_get_order...
foreach ($order->get_items() as $item_id => $item) {
//...
}
}
This works for both programmatically created and user created orders, regardless of WC email configuration.
Reference: create and update methods in WC_Order_Data_Store_CPT core class. Kudos to this post which helped me discover this.
Faced the same issue. I fixed it by changing the hook to woocommerce_checkout_order_processed. It works perfectly for both instant payment and cash on delivery since it runs as soon as an order is placed. Note that, this will run before payment completion. So, it doesn't have any relation with payment.
User clicks on proceed to checkout button and then this hook fires.