10

What I am trying to do is to be able to search by order item SKU or ID in the WooCommerce Orders Admin page.

What I have found/done till now, but with no success is the following at functions.php file.

add_filter( 'woocommerce_shop_order_search_fields', 'woocommerce_shop_order_search_sku' );

function woocommerce_shop_order_search_sku( $search_fields ) {

    $args = array( 'post_type' => 'shop_order' );

    $orders = new WP_Query( $args );

    if ( $orders->have_posts() ) {
        while( $orders->have_posts() ) {
            $post = $orders->the_post();
            $order_id = get_the_ID();
            $order = new WC_Order( $order_id );
            $items = $order->get_items();
            foreach( $items as $item ) {
                $search_order_item_sku = wp_get_post_terms( $item['product_id'], 'search_sku' );
                foreach( $search_order_item_sku as $search_sku ) {
                    add_post_meta( $order_id, "_search_sku", $search_sku->sku );
                }
            }
        }
    };

    $search_fields[] = '_search_sku';

    return $search_fields;

}

I suppose the issue is the value of $search_sku at the line with the add_post_meta.

I have also tried it with get_sku(), $item['sku'] with no luck.

nyedidikeke
  • 6,899
  • 7
  • 44
  • 59
Nikos
  • 111
  • 1
  • 7
  • I don't get it. It sounds like you are unsure if $search_sku->sku or other variations are even holding a value. Why don't you just run print_r($search_sku); die(); in your foreach loop and look at it to determine what object property or array key you need to reference? – Adam Friedman Jan 27 '15 at 16:53
  • 1
    Look at the function _shop_order_search_custom_fields()_ in /woocommerce/includes/admin/class-wc-admin-post-types.php (line ~1284). From what I can tell the SELECT statement in there only searches the wp_posts and wp_postmeta tables. SKUs are product specific. So, you'd need to somehow build your own query to UNION in (and search) product data. – Jibran Feb 19 '15 at 23:17
  • 1
    Clarification: ...only searches the wp_posts and wp_postmeta tables for post types _shop_order_. – Jibran Feb 19 '15 at 23:24
  • 1
    The select statement does look for items, but it looks for the items in the `wp_woocommerce_order_items` table. SKU is not saved in this table, so you'd have to union the `wp_woocommerce_order_itemmeta` `_product_id` field to the `wp_postmeta` `post_id` field and then search for `_sku`. Is your `WP_Query` meant to add the SKU to the order item table? You should be able to add SKU as order item meta. – helgatheviking Feb 20 '15 at 14:04
  • 1
    Or if you can find the right SQL query it might be worth sending a pull request to WooCommerce for possible inclusion. – helgatheviking Feb 20 '15 at 14:13

5 Answers5

3

You have the right idea about saving extra metadata to the order. As jbby and helgatheviking suggest, there is no built-in postmeta for product_id or sku available by default in the woocommerce orders api. Your methodology for accessing and saving the metadata wasn't quite right, however. wp_get_post_terms will access custom taxonomy information, not metadata (use get_post_meta for that). You will be able to do what you were trying to do with this filter:

add_filter( 'woocommerce_shop_order_search_fields', function ($search_fields ) {
    $posts = get_posts(array('post_type' => 'shop_order'));

    foreach ($posts as $post) {
        $order_id = $post->ID;
        $order = new WC_Order($order_id);
        $items = $order->get_items();

        foreach($items as $item) {
            $product_id = $item['product_id'];
            $search_sku = get_post_meta($product_id, "_sku", true);
            add_post_meta($order_id, "_product_sku", $search_sku);
            add_post_meta($order_id, "_product_id", $product_id);
        }
    }

    return array_merge($search_fields, array('_product_sku', '_product_id'));
});

Strictly speaking you should probably move the calls to add_post_meta into a hook that runs when the order is originally saved to the database--this will prevent unnecessary legwork whenever you search through order.

kellanburket
  • 12,250
  • 3
  • 46
  • 73
1

@blacksquare, @jibby, @helgatheviking you are the men! This is the code that works, due to your help.

    //Search by product SKU in Admin Woocommerce Orders
add_filter( 'woocommerce_shop_order_search_fields', function ($search_fields ) {
    $posts = get_posts(array('post_type' => 'shop_order'));

    foreach ($posts as $post) {
        $order_id = $post->ID;
        $order = new WC_Order($order_id);
        $items = $order->get_items();

        foreach($items as $item) {
            $product_id = $item['product_id'];
            $search_sku = get_post_meta($product_id, "_sku", true);
            add_post_meta($order_id, "_product_sku", $search_sku);
        }
    }

    return array_merge($search_fields, array('_product_sku'));
});
Nikos
  • 111
  • 1
  • 7
1

While @Nikos and @blacksquare 's answers work, new post metas are added to every order on every search. If you have 100 orders and make 10 searches, there will be at least 100*10 = 1000 _product_sku entries in the wp_postmeta table. If some orders contain multiple products, there will be even more.

As @blacksquare suggested, add_post_meta should be called when the order is saved. That said, if the site is small and backend search performance isn't too much of a concern, the following code would work without creating redundant _product_sku entries.

add_filter( 'woocommerce_shop_order_search_fields', 'my_shop_order_search_fields') );

public function my_shop_order_search_fields( $search_fields ) {
    $orders = get_posts( array(
        'post_type' => 'shop_order',
        'post_status' => wc_get_order_statuses(), //get all available order statuses in an array
        'posts_per_page' => 999999, // query all orders
        'meta_query' => array(
            array(
                'key' => '_product_sku',
                'compare' => 'NOT EXISTS'
            )
        ) // only query orders without '_product_sku' postmeta
    ) );

    foreach ($orders as $order) {
        $order_id = $order->ID;
        $wc_order = new WC_Order($order_id);
        $items = $wc_order->get_items();
        foreach($items as $item) {
            $product_id = $item['product_id'];
            $search_sku = get_post_meta($product_id, '_sku', true);
            add_post_meta( $order_id, '_product_sku', $search_sku );
        }
    }

    return array_merge($search_fields, array('_product_sku')); // make '_product_sku' one of the meta keys we are going to search for.
}

While a better solution might be calling add_post_meta when an order is created, extra efforts are needed to create _product_sku for existing orders, and you have to create the _product_sku for orders made while the code isn't activated. For simplicity sake, I'd just use the solution suggested above.

p.s. @Nikos 's solution does have one (debatable) advantage - if you change a product's SKU after orders are made, Nikos's solution will find those orders using the new SKU, while the solution above will not. That said, a product's SKU should NOT be changed anyway, and it's debatable whether searching new SKUs should show old orders.

Ming Yeung
  • 11
  • 2
0

You may also want to look into https://uk.wordpress.org/plugins/woo-search-order-by-sku/ which does not overloads post meta, but alters the where clause.

user109764
  • 576
  • 6
  • 11
0

As stated in answer by Ming Yeung, any solution which uses the add_post_meta() function will increase your database size, which might not be ideal for larger stores. That sort of approach will also potentially make the searching of Woocommerce orders in admin quite slow.

An alternative solution is to make use of one of the existing "index" post meta records that Woocommerce uses, and append your data to those records (but only append once).

The following code does exactly that. It does not add SKU (as asked in question) but instead allows for Woocommerce order search to include Country name (as opposed to country code). However this code could be easily adapted to also include SKU if needed:

// when doing a search within Woocommerce orders screen, include the Country name as part of the search criteria
// this code has been optimised to do as little DB work as possible (looks for any index values which haven't been updated yet, and updates them)
// this code also makes use of existing post meta "index" records, instead of creating additional new post meta rows (which would fill up database unnecessarily)
add_filter('woocommerce_shop_order_search_fields', function($search_fields) {
    global $wpdb;
    $records_to_update = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE (meta_key LIKE '%_address_index%') AND (meta_value NOT LIKE '%[HAS_COUNTRY]%')");
    foreach ($records_to_update as $record) {
        $order = new WC_Order($record->post_id);
        if ($record->meta_key == '_billing_address_index') {
            $country_name = WC()->countries->countries[ $order->get_billing_country() ];
        } else {
            $country_name = WC()->countries->countries[ $order->get_shipping_country() ];
        }
        update_post_meta($record->post_id, $record->meta_key, $record->meta_value . ' ' . $country_name . ' [HAS_COUNTRY]');
    }
    return $search_fields;
});
Gavin G
  • 856
  • 6
  • 6