Environment
XStore 9.7.8
WooCommerce 10.7.0
WordPress 7.0.4
Theme option outofstock_products_at_end turned on
What goes wrong
With “Out of stock products at the end” turned on, sortable columns in the wp-admin Products list (edit.php?post_type=product) don’t sort properly. The products are first grouped by stock status in alphabetical order (insufficientstock, instock, onbackorder, outofstock), and the chosen column is only sorted within each group.
For example, sorting by the WooCommerce Waitlist column in descending order shows products with 15, 6 and 5 waitlist signups on page 1. An out-of-stock product with 152 signups only shows up near the end of the list.
Expected
The option should only change the storefront (shop, category, tag and brand archives). Sorting in the admin product list should be left alone.
Cause
In framework/woo.php (around lines 5171–5183), the posts_clauses filter at priority 2000 checks:
if (is_shop() || is_product_category() || is_product_tag() || is_tax(‘brand’)) {
is_shop() also returns true for the main query on the admin Products list, because it counts as a product post-type archive. So the filter adds this to the admin query too:
INNER JOIN wp_postmeta istockstatus ON (…)
WHERE istockstatus.meta_key = ‘_stock_status’ AND istockstatus.meta_value <> ”
ORDER BY istockstatus.meta_value ASC,
Steps to reproduce
Turn on “Out of stock products at the end” in XStore Theme Options.
In wp-admin, go to Products and sort by any sortable column (Price, SKU, or a plugin column such as Waitlist).
The results are grouped by stock status first, so they’re not in the requested column order.
Suggested fix
Skip the filter in admin, and ideally limit it to the main query. The commented-out line already suggests limiting it to the main query:
add_filter( ‘posts_clauses’, function ( $posts_clauses, $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return $posts_clauses;
}
// …existing logic
}, 2000, 2 );
Separately, the filter is an anonymous function, so a child theme can’t remove it with remove_filter(). Using a named callback would let sites work around problems like this one.