Woocommerce Get the order_id from one of the item_id? - woocommerce

I try to get the related order (the order id) from one of the line items id.
One case for example:
When editing an order, and deleting a line item (done by Ajax) the Woocommerce only provide the hook "woocommerce_before_delete_order_item" and passes $item_id only. (The woo function performs sql and not WP enviroment). I need the "belonging" order id to make some action!
My solution so far, is to loop through all orders and compare the Items ID, and break and return order ID when the match occurs.
This is way to slow, or "bulky clumsy", when keeping 10000 orders and more alive in the current shop.
This does NOT work:
wp_get_post_parent_id ( $item_id )
But Im hoping there is a similar call, or do I need to make a DB SQL search? Any answer involving mySQL, please make it "copy and paste ready". Im great at PHP but dont handle or never write my own SQL.
Thanks for any help! Below is my solution so far:
$order_id = my_wc_get_order_from_item_id($item_id);
function my_wc_get_order_from_item_id($id) {
$orders = get_posts('post_type=shop_order&numberposts=-1&post_status=publish');
foreach($orders as $obj) {
$order = new WC_Order($obj->ID);
if ( count( $order->get_items('line_item') ) > 0 ) {
foreach($order->get_items('line_item') as $item_id => $item ) {
if($item_id == $id) $return_value = $obj->ID;
if(isset($return_value)) break;
}
}
unset($order);
if(isset($return_value)) break;
}
if(isset($return_value)) return $return_value;
else return 0;
}

Your approach didn't work because order items are not posts in the wp_posts table, so they can't have a post_parent.
However, all is not lost and this was easier than I expected considering that my SQL isn't that strong. Looking at the woocommerce_order_item table in the database you can see that order_id and order_item_id are both there:
so I borrowed a little SQL statement from WooCommerce and modified it to find the order_id given a particular order_item_id row. Let me know how that works.
function so_38286531_get_order_item_order_id( $item_id ) {
global $wpdb;
$order_id = $wpdb->get_var( $wpdb->prepare(
"SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items
WHERE order_item_id = %d",
$item_id
) );
return $order_id;
}

Use the built-in woocommerce function wc_get_order_id_by_order_item_id($item_get_id) to get order ID
// define the woocommerce_before_delete_order_item callback
function my_func_before_delete_order_item( $item_get_id ) {
$order_id = wc_get_order_id_by_order_item_id($item_get_id);
// do what you need...
};
// add the action
add_action( 'woocommerce_before_delete_order_item', 'my_func_before_delete_order_item', 10, 1 );

Related

Hook "wp_trash_post" executing multiple time in case of bulk trash products

I am currently using hook wp_trash_post to trigger my custom API when a WooCommerce Product is trashed.
This is working fine for the case of single trash.
But, in case of Bulk trash this event is executing multiple times. for example if 3 products are selected to trash then this event will trigger 3 times.
I need different event for Bulk Trash which will execute one time but having all of product ids.
Hopefully it is clear to understand.
Please Help!
Your API call must be withing the foreach loop and is therefore being run foreach post ID. You can capture the IDs in a variable and execute your API call after the if and foreach statements are complete:
function wpdocs_trash_multiple_posts( $post_id = '' ) {
$trashed_ids = [];
if ( isset( $_GET['post'] ) && is_array( $_GET['post'] ) ) {
foreach ( $_GET['post'] as $post_id ) {
$trashed_ids[] = $post_id;
}
} else {
$trashed_ids[] = $post_id;
}
// Do something here, $trashed_ids array
// contains either one, or several IDs
}
add_action( 'wp_trash_post', 'wpdocs_trash_multiple_posts' );

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;
}

Wordpress search optimization: limit search result (not post_per_page)

I have searched stackoverflow. none of them apply to my question. Most of the answer just about limit per page (how many posts to show per page, not how many result to search)
I'm trying to speed up wordpress search.
For example I have 10millions post to search. and user type "a". It will take very long to search all of that because wordpress is using SQL_CALC_FOUND_ROWS.
The fact is that there is no need to search milions of rows (posts) and the answer "1000+" is enough for users. So we need to stop search after we found 1000 results.
The only way i can think of is to use:
'no_found_rows' => true
But then we dont have pagination functionality.
Or maybe we can use a separate query to get the result and to count how many posts found (with limiter)?
I dont understand how can wordpress doesnt have this functionality. Even big site like amazon limit the search result. So if you search "a" or "usb", it will return just the first 20 pages.
Try this code, its works on my wordpress with 1M posts
If you use search for custom post type, change
post_type = 'post'
to your post type name
add_filter('pre_get_posts', 'fix_search_pagination_with_nfr', 1000);
function fix_search_pagination_with_nfr() {
global $wp_query, $wpdb;
if ( ! is_admin() && $wp_query->is_main_query() && $wp_query->is_search() ) {
$wp_query->query_vars['no_found_rows'] = 1;
$wp_query->found_posts = $wpdb->get_var( "SELECT COUNT(*) FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')" );
$wp_query->found_posts = apply_filters_ref_array( 'found_posts', array( $wp_query->found_posts, &$wp_query ) );
if($wp_query->query_vars['posts_per_page'] > 0) {
$wp_query->max_num_pages = ceil($wp_query->found_posts / $wp_query->query_vars['posts_per_page']);
} else {
$wp_query->max_num_pages = 0;
}
}
return $wp_query;
}

Regenerating WooCommerce Download Permissions on older orders

I am trying to add some download permissions to all previous orders via a script to do them in batch. The script seems to work fine expect for one thing. Here is the script…
function update_download_permissions(){
$orders = get_posts( array(
'post_type' => 'shop_order',
'post_status' => 'wc-completed',
'posts_per_page' => -1
) );
foreach ( $orders as $order ) {
wc_downloadable_product_permissions( $order->ID, true );
}
}
The problem is the wc_downloadable_product_permissions function is producing duplicate entries in the wp_woocommerce_downloadable_product_permissions table.
I tried to set the second argument to false (the default) but that resulted in no permissions being created.
Does anybody have any ideas as to why duplicate download permissions are being set?
Cheers!
I came across your question after digging through some of the WooCommerce source code, while attempting to add an item to an existing order and then regenerate the permissions.
The reason wc_downloadable_product_permissions() will create duplicate permission entries is because it does not check for any existing permissions. It simply inserts another entry into the permissions table for every item in the order, which is no good because this will then show up as another download in both the admin and user account frontend.
The second force parameter (poorly documented), is related to a boolean flag that indicates whether wc_downloadable_product_permissions() has run before. The boolean is set to true at the end of the function via the set_download_permissions_granted method. If force is true, it will ignore the boolean. If force is false, and the boolean is true, the function will return near the start.
I created this function which uses the same functions as used by the admin Order action "Regenerate download permissions":
/**
* Regenerate the WooCommerce download permissions for an order
* #param Integer $order_id
*/
function regen_woo_downloadable_product_permissions( $order_id ){
// Remove all existing download permissions for this order.
// This uses the same code as the "regenerate download permissions" action in the WP admin (https://github.com/woocommerce/woocommerce/blob/3.5.2/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php#L129-L131)
// An instance of the download's Data Store (WC_Customer_Download_Data_Store) is created and
// uses its method to delete a download permission from the database by order ID.
$data_store = WC_Data_Store::load( 'customer-download' );
$data_store->delete_by_order_id( $order_id );
// Run WooCommerce's built in function to create the permissions for an order (https://docs.woocommerce.com/wc-apidocs/function-wc_downloadable_product_permissions.html)
// Setting the second "force" argument to true makes sure that this ignores the fact that permissions
// have already been generated on the order.
wc_downloadable_product_permissions( $order_id, true );
}
I found the best way to update order download is to hook into the save_post action hook and check if it's a product that's being updated
there you can get order ids by product id and update just orders that relate to that specific product.
it's more efficient
function get_orders_ids_by_product_id($product_id) {
global $wpdb;
$orders_statuses = "'wc-completed', 'wc-processing', 'wc-on-hold'";
return $wpdb->get_col(
"
SELECT DISTINCT woi.order_id
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim,
{$wpdb->prefix}woocommerce_order_items as woi,
{$wpdb->prefix}posts as p
WHERE woi.order_item_id = woim.order_item_id
AND woi.order_id = p.ID
AND p.post_status IN ( $orders_statuses )
AND woim.meta_key IN ( '_product_id', '_variation_id' )
AND woim.meta_value LIKE '$product_id'
ORDER BY woi.order_item_id DESC"
);
}
// if you don't add 3 as as 4th argument, this will not work as expected
add_action('save_post', 'prefix_on_post_update', 10, 3);
function prefix_on_post_update($post_id, $post, $update) {
if ($post->post_type == 'product') {
$orders_ids = get_orders_ids_by_product_id($post_id);
foreach ($orders_ids as $order_id) {
$data_store = WC_Data_Store::load('customer-download');
$data_store->delete_by_order_id($order_id);
wc_downloadable_product_permissions($order_id, true);
}
}
}

Assign orders from guest customers to particular user

I am using the following code to solve this but this is not working for orders from guest customers . However this is working fine for the orders belonging to some registered user/customer but not for the orders belonging to guest customers.
Solution credit to LoicTheAztec for answer
function cristmas_bulk_editing_orders(){
if(!is_admin()) return; // Will work only from Admin Backed.
else {
$order_id = 9458;
$new_customer_id = 479;
// Getting the postmeta customer ID for 'order' post-type
$customer_id = get_post_meta( $order_id, '_customer_user', true );
var_dump($customer_id);
// If it's an existing order and doesn't have already this user ID
// It update the customer ID
if( !empty($customer_id) && $new_customer_id != $customer_id )
update_post_meta($order_id, '_customer_user', $new_customer_id,0);
echo 'order updated';
}
}
cristmas_bulk_editing_orders();
ORIGINAL ISSUE
We imported the orders via woocommerce order export & import plugin from woocommerce team ..
But in the process something went wrong.. Most of the orders were not assigned any customer ..
So now when ever a new customer registers he/she is assigned 1 of these orders automatically ..
So basicallly all of them see 1 order in their recent orders which belongs to some other guest cusotmer , then they have all the information about other customer . their email etc..
So one option is I find out all the orders(with issues that is no customer assisned to them ) and I assign them to admin ..but this also have some issuses.....
SO is there any other option that these new registered users don't get old orders assigned..
Please help
May be you could try this other similar function based on SQL queries, that should do the trick. Before starting, make a database backup. I have add controls (or limitations) like :
A max date (in YYYY-MM-DD 00:00:00 date time format)
Order number range
So here is that code:
function easter_bulk_editing_orders(){
if( ! is_admin() ) return; // Will work only from Admin Backed.
else {
$processed_orders = array();
// Define the replacement user ID
$replacement_user_id = '2500';
// Define the DATE LIMIT for guest orders max date limit
$max_date = '2017-12-09 00:00:00';
// Define an order range
$order_id_min = '0';
$order_id_max = '100000';
global $wpdb;
// Get all guest orders below a defined max date
$old_guest_orders = $wpdb->get_results( "
SELECT pm.*, p.post_date
FROM {$wpdb->prefix}postmeta AS pm
LEFT JOIN {$wpdb->prefix}posts AS p ON pm.post_id = p.ID
WHERE pm.post_id BETWEEN $order_id_min AND $order_id_max
AND pm.meta_key LIKE '_customer_user'
AND ( pm.meta_value LIKE '0' OR pm.meta_value LIKE '' )
AND p.post_date <= '$max_date'
" );
foreach( $old_guest_orders as $guest_order ){
$meta_id = $guest_order->meta_id;
$wpdb->query( "
UPDATE {$wpdb->prefix}postmeta as pm
SET pm.meta_value = '$replacement_user_id'
WHERE pm.meta_id = '$meta_id'
" );
// Set each order ID in an array
$processed_orders[] = $guest_order->post_id;
}
// Testing ( raw output of processed orders IDS )
var_dump($processed_orders);
}
}
// Run the function
easter_bulk_editing_orders();
Code goes in function.php file of your active child theme (active theme or in any plugin file).
You have to use this function only once and to remove it afterwards (see below).
USAGE:
Once this code is pasted and saved on function.php file, display or reload any Admin page from backend within your browser.
Now you can comment the function this way and save:
// easter_bulk_editing_orders();
Check that the orders have been changed as you want, and remove all this code.
Code is tested and works.
There is error in your importing so try to re-import. Have you tried WP All Import Premium. It will import anything with drag and drop titles, categories, and meta fields. you can also assign new meta name to post meta while importing. If you need more help then share more details.

Resources