Search code examples
phpwordpresswoocommerceordershighperformance

Filter orders that have products from their author on WooCommerce admin order list (+ HPOS)


I used this solution to filter users product list, now I'm looking for a solution to filter the orders list of WooCommerce in admin dashboard. That means, a logged-in user can only see the orders for products which he/she published, and can not see orders for other managers products.

I tried this code, with help of ChatGPT but didn't work:

// Filter orders by products authored by the logged-in user in admin
function filter_orders_by_author_in_admin($query) {
    global $pagenow, $post_type;

    // Check if it's the orders page in admin and the user is not an administrator
    if (is_admin() && $pagenow == 'edit.php' && $post_type == 'shop_order' && !current_user_can('administrator')) {
        // Get current user ID
        $current_user_id = get_current_user_id();

        // Get products authored by the current user
        $args = array(
            'post_type'      => 'product',
            'author'         => $current_user_id,
            'posts_per_page' => -1, // Get all products authored by the user
        );
        $products_query = new WP_Query($args);

        // Extract product IDs
        $product_ids = wp_list_pluck($products_query->posts, 'ID');

        // Modify the query to only include orders related to products authored by the current user
        $meta_query = array(
            array(
                'key'     => '_product_id',
                'value'   => $product_ids,
                'compare' => 'IN',
            ),
        );

        // Combine with existing meta queries, if any
        $meta_query_combined = $query->get('meta_query');
        $meta_query_combined[] = $meta_query;
        $query->set('meta_query', $meta_query_combined);
    }
}
add_action('pre_get_posts', 'filter_orders_by_author_in_admin');

How can I achieve that?


Solution

  • The following will display orders based on the product author, the following will display related orders only for the author, in admin orders list

    It restricts orders filtering to "event_manager" user role.

    1). For legacy "shop_order" posts:

    // Conditional function: Check if user has specific user role
    function has_targeted_user_role( $targeted_role = 'event_manager' ) {
        global $current_user;
        return in_array($targeted_role, $current_user->roles);
    }
    
    // Custom function to get orders IDs from product author
    function get_orders_by_product_author( $user_id ) {
        global $wpdb;
        return $wpdb->get_col("
            SELECT DISTINCT o.order_id
            FROM {$wpdb->prefix}wc_order_product_lookup o
            LEFT JOIN {$wpdb->prefix}posts p
            ON o.product_id = p.ID
            WHERE p.post_author = '{$user_id}'
        " );
    }
    
    // For classic shop_order posts (doesn't work with High-Performance Order Storage)
    add_action( 'pre_get_posts', 'filter_shop_order_posts_by_product_author' );
    function filter_shop_order_posts_by_product_author( $query ) {
        global $pagenow, $post_type;
        if ( is_admin() && has_targeted_user_role() && 'edit.php' === $pagenow && 'shop_order' === $post_type ) {
            $query->set('post__in', get_orders_by_product_author( get_current_user_id() ) );
        }
    }
    

    2). For High-Performance Order Storage (HPOS):

    When HPOS is enabled, the only way to restrict/filter orders, is to add the product author as custom order metadata on new orders to filter them.

    // Conditional function: Check if user has specific user role
    function has_targeted_user_role( $targeted_role = 'event_manager' ) {
        global $current_user;
        return in_array($targeted_role, $current_user->roles);
    }
    
    // Add product author metadata when placing a new order
    add_action( 'woocommerce_checkout_create_order', 'wc_checkout_create_order_callback', 10, 2 );
    function wc_checkout_create_order_callback( $order, $data ) {
        // Loop though cart items
        foreach( WC()->cart->get_cart() as $item ) {
            if ( $author_id = get_post_field( 'post_author', $item['product_id'] ) ) {
                $order->add_meta_data('_product_author_id', $author_id );
            }
        }
    }
    
    // Add  product author metadata on status changed if not yet set
    add_action( 'woocommerce_order_status_changed', 'woocommerce_order_status_changed_callback', 10, 4 );
    function woocommerce_order_status_changed_callback( $order_id, $old_status, $new_status, $order ) {
        if ( ! $order->get_meta('_product_author_id') ) {
            $save = false; // Initializing
    
             // Loop though cart items
            foreach( $order->get_items() as $item ) {
                if ( $author_id = get_post_field( 'post_author', $item['product_id'] ) ) {
                    $order->add_meta_data('_product_author_id', $author_id );
                    $save = true;
                }
            }
            if ( $save ) $order->save();
        }
    }
    
    // Filter order by specific metadata and user role (HPOS)
    add_filter( 'woocommerce_order_query_args', 'filter_wc_order_by_product_author' );
    function filter_wc_order_by_product_author( $query_args ) {
        if ( is_admin() && has_targeted_user_role() && isset($_GET['page']) 
        && $_GET['page'] === 'wc-orders' ) {
            $query_args['meta_query'][] = array(
                'key'     => '_product_author_id',
                'value'   => get_current_user_id(),
            );
        }
        return $query_args;
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). Tested and works.