Make dashboard status widget compatible with HPOS (#44734)
* Remove unnecessary import * Make top seller query compatible with HPOS * Add compat `get_count_for_type()` method * Make dashboard widget compatible with HPOS * Add changelog * PHPCS fixes * Add unit test * PHPCS — we meet again.
This commit is contained in:
parent
b95c15fc2f
commit
c83b030834
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Fixes order counts in WooCommerce Status dashboard widget.
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
|
@ -81,23 +82,49 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
|
|||
private function get_top_seller() {
|
||||
global $wpdb;
|
||||
|
||||
$query = array();
|
||||
$query['fields'] = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id
|
||||
FROM {$wpdb->posts} as posts";
|
||||
$query['join'] = "INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id ";
|
||||
$query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id ";
|
||||
$query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id ";
|
||||
$query['where'] = "WHERE posts.post_type IN ( '" . implode( "','", wc_get_order_types( 'order-count' ) ) . "' ) ";
|
||||
$query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) ";
|
||||
$hpos_enabled = OrderUtil::custom_orders_table_usage_is_enabled();
|
||||
$orders_table = OrderUtil::get_table_for_orders();
|
||||
$orders_column_id = $hpos_enabled ? 'id' : 'ID';
|
||||
$orders_column_type = $hpos_enabled ? 'type' : 'post_type';
|
||||
$orders_column_status = $hpos_enabled ? 'status' : 'post_status';
|
||||
$orders_column_date = $hpos_enabled ? 'date_created_gmt' : 'post_date_gmt';
|
||||
|
||||
$query = array();
|
||||
$query['fields'] = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id FROM {$orders_table} AS orders";
|
||||
$query['join'] = "INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON orders.{$orders_column_id} = order_id ";
|
||||
$query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id ";
|
||||
$query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id ";
|
||||
$query['where'] = "WHERE orders.{$orders_column_type} IN ( '" . implode( "','", wc_get_order_types( 'order-count' ) ) . "' ) ";
|
||||
|
||||
/**
|
||||
* Allows modifying the order statuses used in the top seller query inside the dashboard status widget.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param string[] $order_statuses Order statuses.
|
||||
*/
|
||||
$order_statuses = apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) );
|
||||
$query['where'] .= "AND orders.{$orders_column_status} IN ( 'wc-" . implode( "','wc-", $order_statuses ) . "' ) ";
|
||||
|
||||
$query['where'] .= "AND order_item_meta.meta_key = '_qty' ";
|
||||
$query['where'] .= "AND order_item_meta_2.meta_key = '_product_id' ";
|
||||
$query['where'] .= "AND posts.post_date >= '" . gmdate( 'Y-m-01', current_time( 'timestamp' ) ) . "' ";
|
||||
$query['where'] .= "AND posts.post_date <= '" . gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' ";
|
||||
$query['where'] .= "AND orders.{$orders_column_date} >= '" . gmdate( 'Y-m-01', current_time( 'timestamp' ) ) . "' "; // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
|
||||
$query['where'] .= "AND orders.{$orders_column_date} <= '" . gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' "; // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
|
||||
$query['groupby'] = 'GROUP BY product_id';
|
||||
$query['orderby'] = 'ORDER BY qty DESC';
|
||||
$query['limits'] = 'LIMIT 1';
|
||||
|
||||
return $wpdb->get_row( implode( ' ', apply_filters( 'woocommerce_dashboard_status_widget_top_seller_query', $query ) ) ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
/**
|
||||
* Allows modification of the query to determine the top seller product in the dashboard status widget.
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @param array $query SQL query parts.
|
||||
*/
|
||||
$query = apply_filters( 'woocommerce_dashboard_status_widget_top_seller_query', $query );
|
||||
|
||||
$sql = implode( ' ', $query );
|
||||
return $wpdb->get_row( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,9 +229,9 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
|
|||
$processing_count = 0;
|
||||
|
||||
foreach ( wc_get_order_types( 'order-count' ) as $type ) {
|
||||
$counts = (array) wp_count_posts( $type );
|
||||
$on_hold_count += isset( $counts['wc-on-hold'] ) ? $counts['wc-on-hold'] : 0;
|
||||
$processing_count += isset( $counts['wc-processing'] ) ? $counts['wc-processing'] : 0;
|
||||
$counts = OrderUtil::get_count_for_type( $type );
|
||||
$on_hold_count += $counts['wc-on-hold'];
|
||||
$processing_count += $counts['wc-processing'];
|
||||
}
|
||||
?>
|
||||
<li class="processing-orders">
|
||||
|
|
|
@ -8,7 +8,6 @@ namespace Automattic\WooCommerce\Utilities;
|
|||
use Automattic\WooCommerce\Caches\OrderCacheController;
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\PageController;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||
use Automattic\WooCommerce\Internal\Utilities\COTMigrationUtil;
|
||||
use WC_Order;
|
||||
use WP_Post;
|
||||
|
@ -185,4 +184,48 @@ final class OrderUtil {
|
|||
public static function get_table_for_order_meta() {
|
||||
return wc_get_container()->get( COTMigrationUtil::class )->get_table_for_order_meta();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts number of orders of a given type.
|
||||
*
|
||||
* @since 8.7.0
|
||||
*
|
||||
* @param string $order_type Order type.
|
||||
* @return array<string,int> Array of order counts indexed by order type.
|
||||
*/
|
||||
public static function get_count_for_type( $order_type ) {
|
||||
global $wpdb;
|
||||
|
||||
$cache_key = 'order-count-' . $order_type;
|
||||
$count_per_status = wp_cache_get( $cache_key, 'orders' );
|
||||
|
||||
if ( false === $count_per_status ) {
|
||||
if ( self::custom_orders_table_usage_is_enabled() ) {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
'SELECT `status`, COUNT(*) AS `count` FROM ' . self::get_table_for_orders() . ' WHERE `type` = %s GROUP BY `status`',
|
||||
$order_type
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
$count_per_status = array_map( 'absint', array_column( $results, 'count', 'status' ) );
|
||||
} else {
|
||||
$count_per_status = (array) wp_count_posts( $order_type );
|
||||
}
|
||||
|
||||
// Make sure all order statuses are included just in case.
|
||||
$count_per_status = array_merge(
|
||||
array_fill_keys( array_keys( wc_get_order_statuses() ), 0 ),
|
||||
$count_per_status
|
||||
);
|
||||
|
||||
wp_cache_set( $cache_key, $count_per_status, 'orders' );
|
||||
}
|
||||
|
||||
return $count_per_status;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3387,4 +3387,46 @@ class OrdersTableDataStoreTests extends HposTestCase {
|
|||
$this->assertEquals( 'encountered an order meta value of type __PHP_Incomplete_Class during `delete_meta` in order with ID ' . $order->get_id() . ': "\'O:11:"geoiprecord":14:{s:12:"country_code";s:2:"BE";s:13:"country_code3";s:3:"BEL";s:12:"country_name";s:7:"Belgium";s:6:"region";s:3:"BRU";s:4:"city";s:8:"Brussels";s:11:"postal_code";s:4:"1000";s:8:"latitude";d:50.8333;s:9:"longitude";d:4.3333;s:9:"area_code";N;s:8:"dma_code";N;s:10:"metro_code";N;s:14:"continent_code";s:2:"EU";s:11:"region_name";s:16:"Brussels Capital";s:8:"timezone";s:15:"Europe/Brussels";}\'"', end( $fake_logger->warnings )['message'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that OrderUtil::get_count_for_type() counts orders correctly.
|
||||
*
|
||||
* @testWith ["hpos"]
|
||||
* ["posts"]
|
||||
*
|
||||
* @param string $datastore_to_use Which datastore to use. Either 'hpos' or 'posts'.
|
||||
*/
|
||||
public function test_order_util_get_count_for_type( $datastore_to_use ) {
|
||||
$this->disable_cot_sync();
|
||||
|
||||
if ( 'hpos' === $datastore_to_use ) {
|
||||
$this->toggle_cot_authoritative( true );
|
||||
} else {
|
||||
$this->toggle_cot_authoritative( false );
|
||||
}
|
||||
|
||||
// Create a few orders in various states.
|
||||
$order_statuses = array_keys( wc_get_order_statuses() );
|
||||
|
||||
$expected_counts = array_combine( $order_statuses, array_fill( 0, count( $order_statuses ), 0 ) );
|
||||
foreach ( $order_statuses as $i => $status ) {
|
||||
foreach ( range( 0, $i ) as $_ ) {
|
||||
$expected_counts[ $status ] = $i + 1;
|
||||
|
||||
$order = WC_Helper_Order::create_order();
|
||||
$order->set_status( $status );
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
$real_counts = OrderUtil::get_count_for_type( 'shop_order' );
|
||||
foreach ( $expected_counts as $status => $count ) {
|
||||
$this->assertArrayHasKey( $status, $real_counts );
|
||||
$this->assertEquals( $count, $real_counts[ $status ] );
|
||||
}
|
||||
|
||||
$other_counts = OrderUtil::get_count_for_type( 'shop_something' );
|
||||
$this->assertEquals( 0, array_pop( $other_counts ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue