Avoiding action Being Triggered Twice in woocommerce_thankyou hook - php

I am using the woocommerce action below to call a custom function, but for some reason it's being triggered twice on every order. Does anyone know why this could be or how to fix it so that it only calls once per order?
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
function parent_referral_for_all( $order_id ) {
....
}
UPDATE
I thought the action was being triggered twice but I'm not so sure now. I'm using this action to add another referral inside the affiliatewp plugin, which is adding twice, yet my echo of "Thank You" only appears once.
Everything is working as intended except that the referral (and it's associated order note) are being added twice.
Any help would be greatly appreciated.
That full function:
function parent_referral_for_all( $order_id ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );

To avoid this repetition, you couldadd a custom post meta data to the current order, once your "another" referral has been added the first time.
So your code will be:
function parent_referral_for_all( $order_id ) {
## HERE goes the condition to avoid the repetition
$referral_done = get_post_meta( $order_id, '_referral_done', true );
if( empty($referral_done) ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
## HERE you Create/update your custom post meta data to avoid repetition
update_post_meta( $order_id, '_referral_done', 'yes' )
}
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
I hope this will help.

You can check for the existence of the _thankyou_action_done post_meta key like so:
<?php
/**
* Allow code execution only once when the hook is fired.
*
* #param int $order_id The Woocommerce order ID.
* #return void
*/
function so41284646_trigger_thankyou_once( $order_id ) {
if ( ! get_post_meta( $order_id, '_thankyou_action_done', true ) ) {
// Your code here.
}
}
add_action( 'woocommerce_thankyou', 'so41284646_trigger_thankyou_once', 10, 1 );

Related

Change download_url in WooCommerce

I want to change the download_url on My account downloads section in WooCommerce
current url: http://localhost/i/?download_file=
new url: http://localhost/i/account/downloads/?download_file=
Therefore I changed home_url( '/' ) to home_url( '/account/downloads/' ) using the hook below:
function filter_woocommerce_customer_available_downloads( $downloads, $customer_id ) {
$downloads = array();
$_product = null;
$order = null;
$file_number = 0;
// Get results from valid orders only.
$results = wc_get_customer_download_permissions( $customer_id );
if ( $results ) {
foreach ( $results as $result ) {
$order_id = intval( $result->order_id );
if ( ! $order || $order->get_id() !== $order_id ) {
// New order.
$order = wc_get_order( $order_id );
$_product = null;
}
// Make sure the order exists for this download.
if ( ! $order ) {
continue;
}
// Check if downloads are permitted.
if ( ! $order->is_download_permitted() ) {
continue;
}
$product_id = intval( $result->product_id );
if ( ! $_product || $_product->get_id() !== $product_id ) {
// New product.
$file_number = 0;
$_product = wc_get_product( $product_id );
}
// Check product exists and has the file.
if ( ! $_product || ! $_product->exists() || ! $_product->has_file( $result->download_id ) ) {
continue;
}
$download_file = $_product->get_file( $result->download_id );
// If the downloadable file has been disabled (it may be located in an untrusted location) then do not return it.
if ( ! $download_file->get_enabled() ) {
continue;
}
// Download name will be 'Product Name' for products with a single downloadable file, and 'Product Name - File X' for products with multiple files.
$download_name = apply_filters(
'woocommerce_downloadable_product_name',
$download_file['name'],
$_product,
$result->download_id,
$file_number
);
$downloads[] = array(
'download_url' => add_query_arg(
array(
'download_file' => $product_id,
'order' => $result->order_key,
'email' => rawurlencode( $result->user_email ),
'key' => $result->download_id,
),
home_url( '/account/downloads/' )
),
'download_id' => $result->download_id,
'product_id' => $_product->get_id(),
'product_name' => $_product->get_name(),
'product_url' => $_product->is_visible() ? $_product->get_permalink() : '', // Since 3.3.0.
'download_name' => $download_name,
'order_id' => $order->get_id(),
'order_key' => $order->get_order_key(),
'downloads_remaining' => $result->downloads_remaining,
'access_expires' => $result->access_expires,
'file' => array(
'name' => $download_file->get_name(),
'file' => $download_file->get_file(),
),
);
$file_number++;
}
}
return $downloads;
} add_filter( 'woocommerce_customer_available_downloads', 'filter_woocommerce_customer_available_downloads', 10, 2 );
Code is based on the wc_get_customer_available_downloads( $customer_id ) function, which can be found in /includes/wc-user-functions.php file
Is it possible to summarize the hook?
The woocommerce_customer_available_downloads filter hook does indeed allow you to change download_url, however it is not necessary to rewrite the entire function via the hook, as you only want to change a certain part.
This can be done using the following code, which uses str_replace() in the code :
/**
* Function for `woocommerce_customer_available_downloads` filter-hook.
*
* #param $downloads
* #param $customer_id
*
* #return
*/
function filter_woocommerce_customer_available_downloads( $downloads, $customer_id ) {
// Only on my account downloads section
if ( ! is_wc_endpoint_url( 'downloads' ) )
return $downloads;
// Loop though downloads
foreach( $downloads as $key => $download ) {
// Replace
$downloads[$key]['download_url'] = str_replace( '/?download_file', '/account/downloads/?download_file', $download['download_url'] );
}
return $downloads;
}
add_filter( 'woocommerce_customer_available_downloads', 'filter_woocommerce_customer_available_downloads', 10, 2 );

how to customise order notes in view order page in woocommerce

please could you help me to find
how to edit automatic order notes generated by system (related to items changed through the order ) i couldn't find it's file
what i want to do
add quantity next to deleted item ( if i have 10 items in order and i have deleted 3 i want to see number of deleted in order notes
add total after deleted or added
Try this:
Solution 1:
add order note from checkout
add_filter( 'woocommerce_checkout_fields', 'woo_custom_order_notes_checkout_fields' );
function woo_custom_order_notes_checkout_fields( $fields )
{
$fields['order']['order_comments']['placeholder'] = 'Your Special notes';
$fields['order']['order_comments']['label'] = 'Add your special note txt';
return $fields;
}
When you place an order with custom text and after the order is placed you will be able to see custom order notes in order detail/edit orders on the dashboard.
Solution 2:
Get/ print order all notes on the order view/edit page after clicking on the update button.
// order comment/notes by id
function woo_get_comment_by_id( $comment_id ) {
$comment = get_comment( intval( $comment_id ) );
if ( ! empty( $comment ) ) {
return $comment->comment_content;
} else {
return '';
}
}
add_action( 'save_post_shop_order', 'wpo_wcol_order_notes', 10, 1 );
function wpo_wcol_order_notes ( $order_id ) {
$args = array(
'post_id' => $order_id,
'orderby' => 'comment_ID',
'order' => 'DESC',
'type' => 'order_note',
// 'number' => 1
);
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
$notes = get_comments( $args );
if($notes){
foreach($notes as $note){
$comment_id = $note->comment_ID ?:0;
if( $comment_id ){
echo woo_get_comment_by_id( $comment_id ).'<br>';
}
}
}
exit; // after test you should removed
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
}
You can also change the action "save_post_shop_order" accordingly as per requirements.

Show billing email from customer orders in WooCommerce My account Edit address

I Want to show meta "email" of a customers each order, but this is throwing an error.
add_action( 'woocommerce_before_edit_account_address_form', 'save_billing_email_to_user', 12, 1 );
function save_billing_email_to_user( $user_id ) {
$query = new WC_Order_Query(
array(
'customer_id' => $user_id
)
);
$orders = $query->get_orders();
foreach($orders as $ro => $inn){
echo $ro['email'];
}
}
What Im doing wrong here?
Use instead the following revisited code that will query one order to get the billing email:
add_action( 'woocommerce_before_edit_account_address_form', 'save_billing_email_to_user', 12 );
function save_billing_email_to_user( $user_id ) {
$orders = wc_get_orders( array( 'customer_id' => $user_id , 'limit' => '1' ) );
if ( ! empty( $orders ) ) {
$order = reset( $orders );
$order_id = $order->get_id();
$billing_email = $order->get_billing_email();
echo $billing_email;
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Or you can show the billing email address from each order of a customer like:
add_action( 'woocommerce_before_edit_account_address_form', 'save_billing_email_to_user', 12 );
function save_billing_email_to_user( $user_id ) {
$orders = wc_get_orders( array( 'customer_id' => $user_id ) );
foreach( $orders as $order ){
$order_id = $order->get_id();
echo $order->get_billing_email() . '<br>';
}
}

Process multiple Custom bulk action on Woocommerce admin Orders list

In WooCommerce using the plugin Admin Custom Order Fields, I have a added a custom fields "Rebates Status" with 3 values "No Rebates", "Unpaid" and "Paid", on WooCommerce admin Orders list.
I have also displayed that on the view order screen just like the screenshot below:
Now I would like to bulk update the Rebates Status on selected orders, just as wooCommerce allow to do it for orders status bulk change.
Based on "Process custom bulk action on admin Orders list in Woocommerce" answer thread, I successfully added the 3 Rebates statuses on the bulk edit dropdown (as you can see on the first screenshot):
add_filter( 'bulk_actions-edit-shop_order', 'decrease_meals_orders_bulk_actions' );
function decrease_meals_orders_bulk_actions( $bulk_actions ) {
$bulk_actions['mr_norebates'] = 'Mark Transactions as No Rebates';
$bulk_actions['mr_unpaid'] = 'Mark Transactions as Unpaid';
$bulk_actions['mr_paid'] = 'Mark Transactions as Paid';
return $bulk_actions;
}
But no changes are applied when I try to bulk update the Rebates status for selected orders.
The meta key for Rebates Status is _wc_acof_2
Im stuck also and don't know how to solve the problem.
Any help is appreciated.
This is the complete, compact and optimized way to make it work for each of the 3 actions, to bulk update your custom "Rebate status" displaying a summary notice:
// Your settings in a function
function custom_admin_orders_bulk_actions( $labels = false ){
$domain = 'woocommerce';
return array(
'mr_norebates' => $labels ? __('No Rebates', $domain) : 'norebates',
'mr_unpaid' => $labels ? __('Unpaid', $domain) : 'unpaid',
'mr_paid' => $labels ? __('Paid', $domain) : 'paid',
);
}
// Display the custom actions on admin Orders bulk action dropdown
add_filter( 'bulk_actions-edit-shop_order', 'set_transactions_orders_bulk_actions' );
function set_transactions_orders_bulk_actions( $bulk_actions ) {
foreach( custom_admin_orders_bulk_actions(true) as $key => $label ) {
$bulk_actions[$key] = sprintf( __('Mark Transactions as %s', 'woocommerce'), $label );
}
return $bulk_actions;
}
// Process the bulk action from selected orders
add_filter( 'handle_bulk_actions-edit-shop_order', 'set_transactions_bulk_action_edit_shop_order', 10, 3 );
function set_transactions_bulk_action_edit_shop_order( $redirect_to, $action, $post_ids ) {
$actions = custom_admin_orders_bulk_actions();
if ( in_array( $action, array_keys($actions) ) ) {
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Save the new value
update_post_meta( $post_id, '_wc_acof_2', $actions[$action] );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'rebate_action' => $action,
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
return $redirect_to;
}
// Display the results notice from bulk action on orders
add_action( 'admin_notices', 'set_transactions_bulk_action_admin_notice' );
function set_transactions_bulk_action_admin_notice() {
global $pagenow;
if ( 'edit.php' === $pagenow && isset($_GET['post_type']) && 'shop_order' === $_GET['post_type']
&& isset($_GET['rebate_action']) && isset($_GET['processed_count']) && isset($_GET['processed_ids']) ) {
foreach( custom_admin_orders_bulk_actions(true) as $key => $label ) {
if ( $_GET['rebate_action'] === $key ) {
$count = intval( $_GET['processed_count'] );
printf( '<div class="notice notice-success fade is-dismissible"><p>' .
_n( '%s selected order updated to "%s" rebate status.',
'%s selected orders updated to "%s" rebate status.',
$count, 'woocommerce' )
. '</p></div>', $count, $label );
}
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Based on: Process custom bulk action on admin Orders list in Woocommerce
actually I made it work with the help of your code, but its too long, I appreciate if you can make it simple...
add_filter( 'bulk_actions-edit-shop_order', 'decrease_meals_orders_bulk_actions' );
function decrease_meals_orders_bulk_actions( $bulk_actions ) {
$bulk_actions['mr_norebates'] = 'Mark Transactions as No Rebates';
$bulk_actions['mr_unpaid'] = 'Mark Transactions as Unpaid';
$bulk_actions['mr_paid'] = 'Mark Transactions as Paid';
return $bulk_actions;
}
// Process the bulk action from selected orders
add_filter( 'handle_bulk_actions-edit-shop_order', 'decrease_meals_bulk_action_edit_shop_order', 10, 3 );
function decrease_meals_bulk_action_edit_shop_order( $redirect_to, $action, $post_ids ) {
if ( $action === 'mr_norebates' ){
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Get number of meals
$nb_meal = get_post_meta( $post_id, '_wc_acof_2', true );
// Save the decreased number of meals ($meals - 1)
update_post_meta( $post_id, '_wc_acof_2', $nb_meal = 'norebates' );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'mr_norebates' => 'No Rebates',
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
elseif ( $action === 'mr_unpaid' ){
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Get number of meals
$nb_meal = get_post_meta( $post_id, '_wc_acof_2', true );
// Save the decreased number of meals ($meals - 1)
update_post_meta( $post_id, '_wc_acof_2', $nb_meal = 'unpaid' );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'mr_unpaid' => 'Unpaid',
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
elseif ( $action === 'mr_paid' ){
$processed_ids = array(); // Initializing
foreach ( $post_ids as $post_id ) {
// Get number of meals
$nb_meal = get_post_meta( $post_id, '_wc_acof_2', true );
// Save the decreased number of meals ($meals - 1)
update_post_meta( $post_id, '_wc_acof_2', $nb_meal = 'paid' );
$processed_ids[] = $post_id; // Adding processed order IDs to an array
}
// Adding the right query vars to the returned URL
$redirect_to = add_query_arg( array(
'mr_paid' => 'Paid',
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
return $redirect_to;
}
// Display the results notice from bulk action on orders
add_action( 'admin_notices', 'decrease_meals_bulk_action_admin_notice' );
function decrease_meals_bulk_action_admin_notice() {
if ( empty( $_REQUEST['mr_norebates'] ) ) return; // Exit
$count = intval( $_REQUEST['processed_count'] );
printf( '<div id="message" class="updated fade"><p>' .
_n( 'Selected %s transaction updated.',
'Selected %s transactions updated.',
$count,
'mr_norebates'
) . '</p></div>', $count );

Show custom field value in cart in WooCommerce

I'm using Wordpress together with WooCommerce for my shop.
I want to output the value of my custom field in my cart and in the email order confirmation.
I have created a custom field in my functions.php:
// Display Fields
add_action( 'woocommerce_product_options_general_product_data', 'woo_add_custom_general_fields' );
// Save Fields
add_action( 'woocommerce_process_product_meta', 'woo_add_custom_general_fields_save' );
function woo_add_custom_general_fields() {
global $woocommerce, $post;
echo '<div class="options_group">';
// Input
woocommerce_wp_text_input(
array(
'id' => '_gram',
'label' => __( 'somelabel', 'woocommerce' ),
'placeholder' => '',
'description' => __( 'sometext', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => 'any',
'min' => '0'
)
)
);
echo '</div>';
}
function woo_add_custom_general_fields_save( $post_id ){
// Number Field
$woocommerce_number_field = $_POST['_gram'];
if( !empty( $woocommerce_number_field ) )
update_post_meta( $post_id, '_gram', esc_attr( $woocommerce_number_field ) );
}
On my pages I'm using:
get_post_meta( get_the_ID(), '_gram', true );
To show the value and that work perfect.
Now I want to show this same value under the product name in my cart and in the email confirmations.
But I cant figure out how to do this.
Does anyone knows how to do this?
Well it's a long process, you have to implement two filters & one action to accomplish this.
Also there is a plugin for this exact purpose along with lot of other options related to woocommerce custom fields.
Here is the direct solution for your question.
/**
* Here we are trying to add your custom data as Cart Line Item
* SO that we can add this custom data on your cart, checkout, order and email later
*/
function save_custom_data( $cart_item_data, $product_id ) {
$custom_data = get_post_meta( $product_id, '_gram', true );
if( $custom_data != null && $custom_data != "" ) {
$cart_item_data["gram"] = $custom_data;
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_data', 10, 2 );
/**
* Here we are trying to display that custom data on Cart Table & Checkout Order Review Table
*/
function render_custom_data_on_cart_checkout( $cart_data, $cart_item = null ) {
$custom_items = array();
/* Woo 2.4.2 updates */
if( !empty( $cart_data ) ) {
$custom_items = $cart_data;
}
if( isset( $cart_item["gram"] ) ) {
$custom_items[] = array( "name" => "Gram", "value" => $cart_item["gram"] );
}
return $custom_items;
}
add_filter( 'woocommerce_get_item_data', 'render_custom_data_on_cart_checkout', 10, 2 );
/**
* We are adding that custom data ( gram ) as Order Item Meta,
* which will be carried over to EMail as well
*/
function save_custom_order_meta( $item_id, $values, $cart_item_key ) {
if( isset( $values["gram"] ) ) {
wc_add_order_item_meta( $item_id, "Gram", $values["gram"] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'save_custom_order_meta', 10, 3 );

Categories