[HPOS] Improve handling of "visible" statuses in orders list (#35370)

This commit is contained in:
Jorge A. Torres 2022-12-13 09:39:05 -05:00 committed by GitHub
parent e4f6c468cb
commit 4f692a51d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 35 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix handling of statuses in orders list table (HPOS).

View File

@ -338,25 +338,31 @@ function wc_processing_order_count() {
* Return the orders count of a specific order status.
*
* @param string $status Status.
* @param string $type (Optional) Order type. Leave empty to include all 'for order-count' order types. @{see wc_get_order_types()}.
* @return int
*/
function wc_orders_count( $status ) {
$count = 0;
$status = 'wc-' . $status;
$order_statuses = array_keys( wc_get_order_statuses() );
function wc_orders_count( $status, string $type = '' ) {
$count = 0;
$legacy_statuses = array( 'draft', 'trash' );
$valid_statuses = array_merge( array_keys( wc_get_order_statuses() ), $legacy_statuses );
$status = ( ! in_array( $status, $legacy_statuses, true ) && 0 !== strpos( $status, 'wc-' ) ) ? 'wc-' . $status : $status;
$valid_types = wc_get_order_types( 'order-count' );
$type = trim( $type );
if ( ! in_array( $status, $order_statuses, true ) ) {
if ( ! in_array( $status, $valid_statuses, true ) || ( $type && ! in_array( $type, $valid_types, true ) ) ) {
return 0;
}
$cache_key = WC_Cache_Helper::get_cache_prefix( 'orders' ) . $status;
$cache_key = WC_Cache_Helper::get_cache_prefix( 'orders' ) . $status . $type;
$cached_count = wp_cache_get( $cache_key, 'counts' );
if ( false !== $cached_count ) {
return $cached_count;
}
foreach ( wc_get_order_types( 'order-count' ) as $type ) {
$types_for_count = $type ? array( $type ) : $valid_types;
foreach ( $types_for_count as $type ) {
$data_store = WC_Data_Store::load( 'shop_order' === $type ? 'order' : $type );
if ( $data_store ) {
$count += $data_store->get_order_count( $status );

View File

@ -161,18 +161,19 @@ class ListTable extends WP_List_Table {
<hr class='wp-header-end'>"
);
if ( $this->has_items() || $this->has_filter ) {
$this->views();
echo '<form id="wc-orders-filter" method="get" action="' . esc_url( get_admin_url( null, 'admin.php' ) ) . '">';
$this->print_hidden_form_fields();
$this->search_box( esc_html__( 'Search orders', 'woocommerce' ), 'orders-search-input' );
parent::display();
echo '</form> </div>';
} else {
if ( $this->should_render_blank_state() ) {
$this->render_blank_state();
return;
}
$this->views();
echo '<form id="wc-orders-filter" method="get" action="' . esc_url( get_admin_url( null, 'admin.php' ) ) . '">';
$this->print_hidden_form_fields();
$this->search_box( esc_html__( 'Search orders', 'woocommerce' ), 'orders-search-input' );
parent::display();
echo '</form> </div>';
}
/**
@ -387,25 +388,22 @@ class ListTable extends WP_List_Table {
public function get_views() {
$view_counts = array();
$view_links = array();
$statuses = wc_get_order_statuses();
$statuses = $this->get_visible_statuses();
$current = isset( $_GET['status'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['status'] ?? '' ) ) : 'all';
$all_count = 0;
// Add 'draft' and 'trash' to list.
foreach ( array( 'draft', 'trash' ) as $wp_status ) {
$statuses[ $wp_status ] = ( get_post_status_object( $wp_status ) )->label;
}
$statuses_in_list = array_intersect( array_keys( $statuses ), get_post_stati( array( 'show_in_admin_status_list' => true ) ) );
foreach ( $statuses_in_list as $slug ) {
foreach ( array_keys( $statuses ) as $slug ) {
$total_in_status = $this->count_orders_by_status( $slug );
if ( $total_in_status > 0 ) {
$view_counts[ $slug ] = $total_in_status;
}
if ( ( get_post_status_object( $slug ) )->show_in_admin_all_list ) {
$all_count += $total_in_status;
}
}
$all_count = array_sum( $view_counts );
$view_links['all'] = $this->get_view_link( 'all', __( 'All', 'woocommerce' ), $all_count, '' === $current || 'all' === $current );
foreach ( $view_counts as $slug => $count ) {
@ -418,20 +416,47 @@ class ListTable extends WP_List_Table {
/**
* Count orders by status.
*
* @param string $status The order status we are interested in.
* @param string|string[] $status The order status we are interested in.
*
* @return int
*/
private function count_orders_by_status( string $status ): int {
$orders = wc_get_orders(
array(
'limit' => -1,
'return' => 'ids',
'status' => $status,
private function count_orders_by_status( $status ): int {
return array_sum(
array_map(
function( $order_status ) {
return wc_orders_count( $order_status, 'shop_order' );
},
(array) $status
)
);
}
return count( $orders );
/**
* Checks whether the blank state should be rendered or not. This depends on whether there are others with a visible
* status.
*
* @return boolean TRUE when the blank state should be rendered, FALSE otherwise.
*/
private function should_render_blank_state(): bool {
return ( ! $this->has_filter ) && 0 === $this->count_orders_by_status( array_keys( $this->get_visible_statuses() ) );
}
/**
* Returns a list of slug and labels for order statuses that should be visible in the status list.
*
* @return array slug => label array of order statuses.
*/
private function get_visible_statuses(): array {
return array_intersect_key(
array_merge(
wc_get_order_statuses(),
array(
'trash' => ( get_post_status_object( 'trash' ) )->label,
'draft' => ( get_post_status_object( 'draft' ) )->label,
)
),
array_flip( get_post_stati( array( 'show_in_admin_status_list' => true ) ) )
);
}
/**

View File

@ -80,6 +80,61 @@ class WC_Tests_Order_Functions extends WC_Unit_Test_Case {
// Invalid status returns 0.
$this->assertEquals( 0, wc_orders_count( 'unkown-status' ) );
// Invalid order type should return 0.
$this->assertEquals( 0, wc_orders_count( 'wc-pending', 'invalid-order-type' ) );
wp_cache_flush();
// Fake some datastores and order types for testing.
$test_counts = array(
'order' => array(
array( 'wc-on-hold', 2 ),
array( 'trash', 1 ),
),
'order-fake-type' => array(
array( 'wc-on-hold', 3 ),
array( 'trash', 0 ),
),
);
$mock_datastores = array();
foreach ( array( 'order', 'order-fake-type' ) as $order_type ) {
$mock_datastores[ $order_type ] = $this->getMockBuilder( 'Abstract_WC_Order_Data_Store_CPT' )
->setMethods( array( 'get_order_count' ) )
->getMock();
$mock_datastores[ $order_type ]
->method( 'get_order_count' )
->will( $this->returnValueMap( $test_counts[ $order_type ] ) );
}
$add_mock_datastores = function( $stores ) use ( $mock_datastores ) {
return array_merge( $stores, $mock_datastores );
};
$add_mock_order_type = function( $order_types ) use ( $mock_datastores ) {
return array( 'shop_order', 'order-fake-type' );
};
add_filter( 'woocommerce_data_stores', $add_mock_datastores );
add_filter( 'wc_order_types', $add_mock_order_type );
// Check counts for specific order types.
$this->assertEquals( 2, wc_orders_count( 'on-hold', 'shop_order' ) );
$this->assertEquals( 1, wc_orders_count( 'trash', 'shop_order' ) );
$this->assertEquals( 3, wc_orders_count( 'on-hold', 'order-fake-type' ) );
$this->assertEquals( 0, wc_orders_count( 'trash', 'order-fake-type' ) );
// Check that counts with no order type include all order types.
$this->assertEquals( 5, wc_orders_count( 'on-hold' ) );
$this->assertEquals( 1, wc_orders_count( 'trash' ) );
remove_filter( 'woocommerce_data_stores', $add_mock_datastores );
remove_filter( 'wc_order_types', $add_mock_order_type );
// Confirm that everything's back to normal.
wp_cache_flush();
$this->assertEquals( 0, wc_orders_count( 'on-hold' ) );
}
/**