Also support syncing for HPOS with stats table. (#35118)

* Also support syncing for HPOS with stats table.

* Add changelog.

* Fixup.

* More fixup.

* test commit by disabling importing admin test orders.

* Update get_items to work with HPOS.

* Modify tests to assert against invalid result.

* test commit for ci.

* Remove seperate test as its quite slow.

* Applied coding standards.

* Coding standards, part 2.
This commit is contained in:
Vedanshu Jain 2022-10-31 14:33:18 +05:30 committed by GitHub
parent 2c5d3d2acc
commit 6c551b0cd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 14 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Sync orders for stats table.

View File

@ -12,6 +12,7 @@ use \Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface;
use \Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
use \Automattic\WooCommerce\Admin\API\Reports\SqlQuery;
use \Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache;
use Automattic\WooCommerce\Utilities\OrderUtil;
/**
* Admin\API\Reports\Customers\DataStore.
@ -64,7 +65,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
'id' => "{$table_name}.customer_id as id",
'user_id' => 'user_id',
'username' => 'username',
'name' => "CONCAT_WS( ' ', first_name, last_name ) as name", // @todo What does this mean for RTL?
'name' => "CONCAT_WS( ' ', first_name, last_name ) as name", // @xxx: What does this mean for RTL?
'email' => 'email',
'country' => 'country',
'city' => 'city',
@ -122,7 +123,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
public static function sync_order_customer( $post_id ) {
global $wpdb;
if ( 'shop_order' !== get_post_type( $post_id ) && 'shop_order_refund' !== get_post_type( $post_id ) ) {
if ( ! OrderUtil::is_order( $post_id, array( 'shop_order', 'shop_order_refund' ) ) ) {
return -1;
}

View File

@ -13,6 +13,7 @@ use \Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
use \Automattic\WooCommerce\Admin\API\Reports\SqlQuery;
use \Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache;
use \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore;
use Automattic\WooCommerce\Utilities\OrderUtil;
/**
* API\Reports\Orders\Stats\DataStore.
@ -113,6 +114,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
* @param array $query_args Query arguments supplied by the user.
*/
protected function orders_stats_sql_filter( $query_args ) {
// phpcs:ignore Generic.Commenting.Todo.TaskFound
// @todo Performance of all of this?
global $wpdb;
@ -335,6 +337,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
return new \WP_Error( 'woocommerce_analytics_revenue_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) );
}
// phpcs:ignore Generic.Commenting.Todo.TaskFound
// @todo Remove these assignements when refactoring segmenter classes to use query objects.
$totals_query = array(
'from_clause' => $this->total_query->get_sql_clause( 'join' ),
@ -474,7 +477,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
* @return int|bool Returns -1 if order won't be processed, or a boolean indicating processing success.
*/
public static function sync_order( $post_id ) {
if ( 'shop_order' !== get_post_type( $post_id ) && 'shop_order_refund' !== get_post_type( $post_id ) ) {
if ( ! OrderUtil::is_order( $post_id, array( 'shop_order', 'shop_order_refund' ) ) ) {
return -1;
}
@ -505,6 +508,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
*
* @param array $data Data written to order stats lookup table.
* @param WC_Order $order Order object.
*
* @since 4.0.0
*/
$data = apply_filters(
'woocommerce_analytics_update_order_stats_data',
@ -555,6 +560,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
* Fires when order's stats reports are updated.
*
* @param int $order_id Order ID.
*
* @since 4.0.0.
*/
do_action( 'woocommerce_analytics_update_order_stats', $order->get_id() );
@ -571,7 +578,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
global $wpdb;
$order_id = (int) $post_id;
if ( 'shop_order' !== get_post_type( $order_id ) && 'shop_order_refund' !== get_post_type( $order_id ) ) {
if ( ! OrderUtil::is_order( $post_id, array( 'shop_order', 'shop_order_refund' ) ) ) {
return;
}
@ -586,6 +593,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
*
* @param int $order_id Order ID.
* @param int $customer_id Customer ID.
*
* @since 4.0.0
*/
do_action( 'woocommerce_analytics_delete_order_stats', $order_id, $customer_id );

View File

@ -13,6 +13,9 @@ use \Automattic\WooCommerce\Admin\API\Reports\Products\DataStore as ProductsData
use \Automattic\WooCommerce\Admin\API\Reports\Taxes\DataStore as TaxesDataStore;
use \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore;
use \Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache;
use Automattic\WooCommerce\Admin\Overrides\Order;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use Automattic\WooCommerce\Utilities\OrderUtil;
/**
* OrdersScheduler Class.
@ -36,7 +39,8 @@ class OrdersScheduler extends ImportScheduler {
\Automattic\WooCommerce\Admin\Overrides\OrderRefund::add_filters();
// Order and refund data must be run on these hooks to ensure meta data is set.
add_action( 'save_post', array( __CLASS__, 'possibly_schedule_import' ) );
add_action( 'woocommerce_update_order', array( __CLASS__, 'possibly_schedule_import' ) );
add_action( 'woocommerce_create_order', array( __CLASS__, 'possibly_schedule_import' ) );
add_action( 'woocommerce_refund_created', array( __CLASS__, 'possibly_schedule_import' ) );
OrdersStatsDataStore::init();
@ -69,6 +73,25 @@ class OrdersScheduler extends ImportScheduler {
* @param bool $skip_existing Skip already imported orders.
*/
public static function get_items( $limit = 10, $page = 1, $days = false, $skip_existing = false ) {
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
return self::get_items_from_orders_table( $limit, $page, $days, $skip_existing );
} else {
return self::get_items_from_posts_table( $limit, $page, $days, $skip_existing );
}
}
/**
* Helper method to ger order/refund IDS and total count that needs to be synced.
*
* @internal
* @param int $limit Number of records to retrieve.
* @param int $page Page number.
* @param int|bool $days Number of days prior to current date to limit search results.
* @param bool $skip_existing Skip already imported orders.
*
* @return object Total counts.
*/
private static function get_items_from_posts_table( $limit, $page, $days, $skip_existing ) {
global $wpdb;
$where_clause = '';
$offset = $page > 1 ? ( $page - 1 ) * $limit : 0;
@ -112,6 +135,65 @@ class OrdersScheduler extends ImportScheduler {
);
}
/**
* Helper method to ger order/refund IDS and total count that needs to be synced from HPOS.
*
* @internal
* @param int $limit Number of records to retrieve.
* @param int $page Page number.
* @param int|bool $days Number of days prior to current date to limit search results.
* @param bool $skip_existing Skip already imported orders.
*
* @return object Total counts.
*/
private static function get_items_from_orders_table( $limit, $page, $days, $skip_existing ) {
global $wpdb;
$where_clause = '';
$offset = $page > 1 ? ( $page - 1 ) * $limit : 0;
$order_table = OrdersTableDataStore::get_orders_table_name();
if ( is_int( $days ) ) {
$days_ago = gmdate( 'Y-m-d 00:00:00', time() - ( DAY_IN_SECONDS * $days ) );
$where_clause .= " AND orders.date_created_gmt >= '{$days_ago}'";
}
if ( $skip_existing ) {
$where_clause .= "AND NOT EXiSTS (
SELECT 1 FROM {$wpdb->prefix}wc_order_stats
WHERE {$wpdb->prefix}wc_order_stats.order_id = orders.id
)
";
}
$count = $wpdb->get_var(
"
SELECT COUNT(*) FROM {$order_table} AS orders
WHERE type in ( 'shop_order', 'shop_order_refund' )
AND status NOT IN ( 'wc-auto-draft', 'trash', 'auto-draft' )
{$where_clause}
"
); // phpcs:ignore unprepared SQL ok.
$order_ids = absint( $count ) > 0 ? $wpdb->get_col(
$wpdb->prepare(
"SELECT id FROM {$order_table} AS orders
WHERE type IN ( 'shop_order', 'shop_order_refund' )
AND status NOT IN ( 'wc-auto-draft', 'auto-draft', 'trash' )
{$where_clause}
ORDER BY date_created_gmt ASC
LIMIT %d
OFFSET %d",
$limit,
$offset
)
) : array(); // phpcs:ignore unprepared SQL ok.
return (object) array(
'total' => absint( $count ),
'ids' => $order_ids,
);
}
/**
* Get total number of rows imported.
*
@ -125,15 +207,16 @@ class OrdersScheduler extends ImportScheduler {
/**
* Schedule this import if the post is an order or refund.
*
* @param int $order_id Post ID.
*
* @internal
* @param int $post_id Post ID.
*/
public static function possibly_schedule_import( $post_id ) {
if ( 'shop_order' !== get_post_type( $post_id ) && 'woocommerce_refund_created' !== current_filter() ) {
public static function possibly_schedule_import( $order_id ) {
if ( ! OrderUtil::is_order( $order_id, array( 'shop_order' ) ) && 'woocommerce_refund_created' !== current_filter() ) {
return;
}
self::schedule_action( 'import', array( $post_id ) );
self::schedule_action( 'import', array( $order_id ) );
}
/**

View File

@ -122,15 +122,15 @@ class COTMigrationUtil {
}
/**
* Helper function to id from an post or order object.
* Helper function to get ID from a post or order object.
*
* @param WP_Post/WC_Order $post_or_order_object WP_Post/WC_Order object to get ID for.
*
* @return int Order or post ID.
*/
public function get_post_or_order_id( $post_or_order_object ) : int {
if ( is_int( $post_or_order_object ) ) {
return $post_or_order_object;
if ( is_numeric( $post_or_order_object ) ) {
return (int) $post_or_order_object;
} elseif ( $post_or_order_object instanceof WC_Order ) {
return $post_or_order_object->get_id();
} elseif ( $post_or_order_object instanceof WP_Post ) {

View File

@ -6,6 +6,8 @@
* @since 3.5.0
*/
// phpcs:disable Squiz.Classes.ClassFileName.NoMatch, Squiz.Classes.ValidClassName.NotCamelCaps
use \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore;
/**
@ -503,7 +505,8 @@ class WC_Admin_Tests_API_Reports_Customers extends WC_REST_Unit_Test_Case {
WC_Helper_Queue::run_all_pending();
$this->assertTrue( $result );
$this->assertNotEquals( -1, $result );
$request = new WP_REST_Request( 'GET', $this->endpoint );
$response = $this->server->dispatch( $request );
$reports = $response->get_data();
@ -599,7 +602,7 @@ class WC_Admin_Tests_API_Reports_Customers extends WC_REST_Unit_Test_Case {
WC_Helper_Queue::run_all_pending();
// Didn't update anything.
$this->assertTrue( $result );
$this->assertNotEquals( -1, $result );
$request = new WP_REST_Request( 'GET', $this->endpoint );
$response = $this->server->dispatch( $request );
$reports = $response->get_data();