I am using this code to add admin action to change bulk order statuses.
I have custom build status with name "wc-to-order".
Unfortunatelly, I got error when use code from Woo bulk action.
Can anyone help me to resolve it?
This is code:
add_action( 'admin_action_wc-to-order', 'add_new_order_status_wc_on_hold' ); // admin_action_{action name}
function add_new_order_status_wc_on_hold() {
// if an array with order IDs is not presented, exit the function
if( !isset( $_REQUEST['post'] ) && !is_array( $_REQUEST['post'] ) )
return;
foreach( $_REQUEST['post'] as $order_id ) {
$order = new WC_Order( $order_id );
$order_note = 'That\'s what happened by bulk edit:';
$order->update_status( 'wc-to-order', $order_note, true );
}
$location = add_query_arg( array(
'post_type' => 'shop_order',
'wc-to-order' => 1, // markED_awaiting_shipment=1 is just the $_GET variable for notices
'changed' => count( $_REQUEST['post'] ), // number of changed orders
'ids' => join( $_REQUEST['post'], ',' ),
'post_status' => 'all'
), 'edit.php' );
wp_redirect( admin_url( $location ) );
exit;
}
I got this in debug log
PHP Fatal error: Uncaught TypeError: join(): Argument #2 ($array) must be of type ?array, string given in /public_html/sitename/wp-content/themes/themename/functions.php:648
From the log, it appears that $_REQUEST['post'] may not be an array. In the below code we first check if it is an array. If it is not, then we cast it as array.
add_action( 'admin_action_wc-to-order', 'add_new_order_status_wc_on_hold' ); // admin_action_{action name}
function add_new_order_status_wc_on_hold() {
// if an array with order IDs is not presented, exit the function
if( !isset( $_REQUEST['post'] ) ) {
return;
}
//check if it is an array. If not, cast it as array
$orders_array = is_array($_REQUEST['post']) ? $_REQUEST['post'] : array($_REQUEST['post']);
foreach( $orders_array as $order_id ) {
$order = new WC_Order( $order_id );
$order_note = 'That\'s what happened by bulk edit:';
$order->update_status( 'wc-to-order', $order_note, true );
}
$location = add_query_arg( array(
'post_type' => 'shop_order',
'wc-to-order' => 1, // markED_awaiting_shipment=1 is just the $_GET variable for notices
'changed' => count( $orders_array ), // number of changed orders
'ids' => join( $orders_array, ',' ),
'post_status' => 'all'
), 'edit.php' );
wp_redirect( admin_url( $location ) );
exit;
}
This way we ensure that array is always provided to join function.
Related
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 );
I have created a custom search filter to show events by states and customized the code, but it in showing any results when a state is selected. Here is the code:
add_filter( 'event_manager_get_listings', 'filter_by_state_field_query_args', 10, 2 );
function filter_by_state_field_query_args( $query_args, $args ) {
if ( isset( $_POST['form_data'] ) ) {
parse_str( $_POST['form_data'], $form_data );
// If this is set, we are filtering by state
if ( ! empty( $form_data['filter_by_state'] ) ) {
$event_state = sanitize_text_field( $form_data['filter_by_state'] );
$query_args['meta_query'][] = array(
'key' => '_event_state',
'value' => $event_state,
);
}
}
return $query_args;
}
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 );
I recently migrated my Wordpress website to a hosting server and have had issues getting one function to work. When testing it on my local server it worked perfectly but now I can't seem to figure out why it will not write to my SQL table. The function is here below, any help will be very appreciated. I was also wondering if there is a way to only have the SQL query run when I receive the payment from PayPal if it would require a large change to this program it is not an issue.
add_action( 'woocommerce_order_status_changed', 'checkout_custom_table_insert', 20, 4 );
function checkout_custom_table_insert( $order_id, $old_status, $new_status, $order ){
// Only for 'processing' and 'completed' order status changes
$statuses = array( 'processing', 'completed' );
if ( ! in_array( $new_status, $statuses ) ) return;
// Check if data has been already updated (avoid repetitions)
$is_done = get_post_meta( $order_id, '_checkout_table_updated', true );
if( ! empty($is_done) ) return; // We exit if it has been already done
global $wpdb;
// Loop through order items
foreach ($order->get_items() as $item){
$product = $item->get_product(); // Get the variation product object
// Choose in the array which data you want to insert (each line is a table column)
$args = array(
'rank' => $product->get_attribute( 'pa_rank' ),
'money' => $product->get_attribute( 'pa_money' ),
'spawner' => $product->get_attribute( 'pa_spawner' ),
'permission' => $product->get_attribute( 'pa_permission' ),
'kit' => $product->get_attribute( 'pa_kit' ),
'crate' => $product->get_attribute( 'pa_crate' ),
'stag' => $product->get_attribute( 'pa_stag' ),
'duration' => $product->get_attribute( 'pa_duration' ),
'end_date' => date("Y-m-d", strtotime("+$duration Months")),
'username' => get_post_meta( $order_id, 'My Field', true ),
'executed' => "false",
);
// The SQL INSERT query
$table = "checkout"; // or "{$wpdb->prefix}checkout";
$wpdb->insert( $table, $args ); // Insert the data
}
// Mark this task as done for this order
update_post_meta( $order_id, '_checkout_table_updated', '1' );
}
Ohh, you have order status as below to check against order status:
$statuses = array( 'processing', 'completed' );
but woocommerce order status are as follow:
'wc-pending','wc-processing', 'wc-on-hold' ,'wc-completed' , 'wc-cancelled', 'wc-refunded' , 'wc-failed'
update your array to:
$statuses = array( 'wc-processing', 'wc-completed' );
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 );