Add HPOS compat queries for tracker. (#38293)

This commit is contained in:
Vedanshu Jain 2023-06-09 10:19:01 +05:30 committed by GitHub
commit b04376b501
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 261 additions and 88 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Add HPOS compat queries for tracker.

View File

@ -11,15 +11,19 @@
*/
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use Automattic\WooCommerce\Internal\Utilities\BlocksUtil;
use Automattic\WooCommerce\Utilities\OrderUtil;
defined( 'ABSPATH' ) || exit;
// phpcs:disable Squiz.Classes.ClassFileName.NoMatch, Squiz.Classes.ValidClassName.NotCamelCaps -- Backwards compatibility.
/**
* WooCommerce Tracker Class
*/
class WC_Tracker {
// phpcs:enable
/**
* URL to the WooThemes Tracker API endpoint.
*
@ -30,7 +34,7 @@ class WC_Tracker {
/**
* Hook into cron event.
*/
public static function init() {
public static function init() { // phpcs:ignore WooCommerce.Functions.InternalInjectionMethod.MissingFinal, WooCommerce.Functions.InternalInjectionMethod.MissingInternalTag -- Not an injection.
add_action( 'woocommerce_tracker_send_event', array( __CLASS__, 'send_tracking_data' ) );
}
@ -45,10 +49,15 @@ class WC_Tracker {
return;
}
/**
* Filter whether to send tracking data or not.
*
* @since 2.3.0
*/
if ( ! apply_filters( 'woocommerce_tracker_send_override', $override ) ) {
// Send a maximum of once per week by default.
$last_send = self::get_last_send_time();
if ( $last_send && $last_send > apply_filters( 'woocommerce_tracker_last_send_interval', strtotime( '-1 week' ) ) ) {
if ( $last_send && $last_send > apply_filters( 'woocommerce_tracker_last_send_interval', strtotime( '-1 week' ) ) ) { // phpcs:ignore
return;
}
} else {
@ -84,6 +93,11 @@ class WC_Tracker {
* @return int|bool
*/
private static function get_last_send_time() {
/**
* Filter the last time tracking data was sent.
*
* @since 2.3.0
*/
return apply_filters( 'woocommerce_tracker_last_send_time', get_option( 'woocommerce_tracker_last_send', false ) );
}
@ -118,7 +132,12 @@ class WC_Tracker {
$data = array();
// General site info.
$data['url'] = home_url();
$data['url'] = home_url();
/**
* Filter the admin email that's sent with data.
*
* @since 2.3.0
*/
$data['email'] = apply_filters( 'woocommerce_tracker_admin_email', get_option( 'admin_email' ) );
$data['theme'] = self::get_theme_info();
@ -172,12 +191,21 @@ class WC_Tracker {
$data['mini_cart_block'] = self::get_mini_cart_info();
}
// WooCommerce Admin info.
/**
* Filter whether to disable admin tracking.
*
* @since 5.2.0
*/
$data['wc_admin_disabled'] = apply_filters( 'woocommerce_admin_disabled', false ) ? 'yes' : 'no';
// Mobile info.
$data['wc_mobile_usage'] = self::get_woocommerce_mobile_usage();
/**
* Filter the data that's sent with the tracker.
*
* @since 2.3.0
*/
return apply_filters( 'woocommerce_tracker_data', $data );
}
@ -212,6 +240,7 @@ class WC_Tracker {
$memory = wc_let_to_num( WP_MEMORY_LIMIT );
if ( function_exists( 'memory_get_usage' ) ) {
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- False positive.
$system_memory = wc_let_to_num( @ini_get( 'memory_limit' ) );
$memory = max( $memory, $system_memory );
}
@ -287,20 +316,20 @@ class WC_Tracker {
foreach ( $plugins as $k => $v ) {
// Take care of formatting the data how we want it.
$formatted = array();
$formatted['name'] = strip_tags( $v['Name'] );
$formatted['name'] = wp_strip_all_tags( $v['Name'] );
if ( isset( $v['Version'] ) ) {
$formatted['version'] = strip_tags( $v['Version'] );
$formatted['version'] = wp_strip_all_tags( $v['Version'] );
}
if ( isset( $v['Author'] ) ) {
$formatted['author'] = strip_tags( $v['Author'] );
$formatted['author'] = wp_strip_all_tags( $v['Author'] );
}
if ( isset( $v['Network'] ) ) {
$formatted['network'] = strip_tags( $v['Network'] );
$formatted['network'] = wp_strip_all_tags( $v['Network'] );
}
if ( isset( $v['PluginURI'] ) ) {
$formatted['plugin_uri'] = strip_tags( $v['PluginURI'] );
$formatted['plugin_uri'] = wp_strip_all_tags( $v['PluginURI'] );
}
if ( in_array( $k, $active_plugins_keys ) ) {
if ( in_array( $k, $active_plugins_keys, true ) ) {
// Remove active plugins from list so we can show active and inactive separately.
unset( $plugins[ $k ] );
$active_plugins[ $k ] = $formatted;
@ -381,10 +410,9 @@ class WC_Tracker {
* @return array
*/
private static function get_order_counts() {
$order_count = array();
$order_count_data = wp_count_posts( 'shop_order' );
$order_count = array();
foreach ( wc_get_order_statuses() as $status_slug => $status_name ) {
$order_count[ $status_slug ] = $order_count_data->{ $status_slug };
$order_count[ $status_slug ] = wc_orders_count( $status_slug );
}
return $order_count;
}
@ -413,33 +441,59 @@ class WC_Tracker {
private static function get_order_totals() {
global $wpdb;
$gross_total = $wpdb->get_var(
$orders_table = OrdersTableDataStore::get_orders_table_name();
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$gross_total = $wpdb->get_var(
"
SELECT SUM(total_amount) AS 'gross_total'
FROM $orders_table
WHERE status in ('wc-completed', 'wc-refunded');
"
SELECT
SUM( order_meta.meta_value ) AS 'gross_total'
FROM {$wpdb->prefix}posts AS orders
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
WHERE order_meta.meta_key = '_order_total'
AND orders.post_status in ( 'wc-completed', 'wc-refunded' )
GROUP BY order_meta.meta_key
"
);
);
// phpcs:enable
} else {
$gross_total = $wpdb->get_var(
"
SELECT
SUM( order_meta.meta_value ) AS 'gross_total'
FROM {$wpdb->prefix}posts AS orders
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
WHERE order_meta.meta_key = '_order_total'
AND orders.post_status in ( 'wc-completed', 'wc-refunded' )
GROUP BY order_meta.meta_key
"
);
}
if ( is_null( $gross_total ) ) {
$gross_total = 0;
}
$processing_gross_total = $wpdb->get_var(
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$processing_gross_total = $wpdb->get_var(
"
SELECT SUM(total_amount) AS 'gross_total'
FROM $orders_table
WHERE status = 'wc-processing';
"
SELECT
SUM( order_meta.meta_value ) AS 'gross_total'
FROM {$wpdb->prefix}posts AS orders
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
WHERE order_meta.meta_key = '_order_total'
AND orders.post_status = 'wc-processing'
GROUP BY order_meta.meta_key
"
);
);
// phpcs:enable
} else {
$processing_gross_total = $wpdb->get_var(
"
SELECT
SUM( order_meta.meta_value ) AS 'gross_total'
FROM {$wpdb->prefix}posts AS orders
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
WHERE order_meta.meta_key = '_order_total'
AND orders.post_status = 'wc-processing'
GROUP BY order_meta.meta_key
"
);
}
if ( is_null( $processing_gross_total ) ) {
$processing_gross_total = 0;
@ -459,16 +513,31 @@ class WC_Tracker {
private static function get_order_dates() {
global $wpdb;
$min_max = $wpdb->get_row(
"
SELECT
MIN( post_date_gmt ) as 'first', MAX( post_date_gmt ) as 'last'
FROM {$wpdb->prefix}posts
WHERE post_type = 'shop_order'
AND post_status = 'wc-completed'
",
ARRAY_A
);
$orders_table = OrdersTableDataStore::get_orders_table_name();
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$min_max = $wpdb->get_row(
"
SELECT
MIN( date_created_gmt ) as 'first', MAX( date_created_gmt ) as 'last'
FROM $orders_table
WHERE status = 'wc-completed';
",
ARRAY_A
);
// phpcs:enable
} else {
$min_max = $wpdb->get_row(
"
SELECT
MIN( post_date_gmt ) as 'first', MAX( post_date_gmt ) as 'last'
FROM {$wpdb->prefix}posts
WHERE post_type = 'shop_order'
AND post_status = 'wc-completed'
",
ARRAY_A
);
}
if ( is_null( $min_max ) ) {
$min_max = array(
@ -477,16 +546,30 @@ class WC_Tracker {
);
}
$processing_min_max = $wpdb->get_row(
"
SELECT
MIN( post_date_gmt ) as 'processing_first', MAX( post_date_gmt ) as 'processing_last'
FROM {$wpdb->prefix}posts
WHERE post_type = 'shop_order'
AND post_status = 'wc-processing'
",
ARRAY_A
);
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$processing_min_max = $wpdb->get_row(
"
SELECT
MIN( date_created_gmt ) as 'processing_first', MAX( date_created_gmt ) as 'processing_last'
FROM $orders_table
WHERE status = 'wc-processing';
",
ARRAY_A
);
// phpcs:enable
} else {
$processing_min_max = $wpdb->get_row(
"
SELECT
MIN( post_date_gmt ) as 'processing_first', MAX( post_date_gmt ) as 'processing_last'
FROM {$wpdb->prefix}posts
WHERE post_type = 'shop_order'
AND post_status = 'wc-processing'
",
ARRAY_A
);
}
if ( is_null( $processing_min_max ) ) {
$processing_min_max = array(
@ -569,28 +652,42 @@ class WC_Tracker {
private static function get_orders_by_gateway() {
global $wpdb;
$orders_and_gateway_details = $wpdb->get_results(
"
SELECT
gateway, currency, SUM(total) AS totals, COUNT(order_id) AS counts
FROM (
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
$orders_table = OrdersTableDataStore::get_orders_table_name();
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$orders_and_gateway_details = $wpdb->get_results(
"
SELECT payment_method AS gateway, currency AS currency, SUM( total_amount ) AS totals, count( id ) AS counts
FROM $orders_table
WHERE status IN ( 'wc-completed', 'wc-processing', 'wc-refunded' )
GROUP BY gateway, currency;
"
);
// phpcs:enable
} else {
$orders_and_gateway_details = $wpdb->get_results(
"
SELECT
orders.id AS order_id,
MAX(CASE WHEN meta_key = '_payment_method' THEN meta_value END) gateway,
MAX(CASE WHEN meta_key = '_order_total' THEN meta_value END) total,
MAX(CASE WHEN meta_key = '_order_currency' THEN meta_value END) currency
FROM
{$wpdb->prefix}posts orders
LEFT JOIN
{$wpdb->prefix}postmeta order_meta ON order_meta.post_id = orders.id
WHERE orders.post_type = 'shop_order'
AND orders.post_status in ( 'wc-completed', 'wc-processing', 'wc-refunded' )
AND meta_key in( '_payment_method','_order_total','_order_currency')
GROUP BY orders.id
) order_gateways
GROUP BY gateway, currency
"
);
gateway, currency, SUM(total) AS totals, COUNT(order_id) AS counts
FROM (
SELECT
orders.id AS order_id,
MAX(CASE WHEN meta_key = '_payment_method' THEN meta_value END) gateway,
MAX(CASE WHEN meta_key = '_order_total' THEN meta_value END) total,
MAX(CASE WHEN meta_key = '_order_currency' THEN meta_value END) currency
FROM
{$wpdb->prefix}posts orders
LEFT JOIN
{$wpdb->prefix}postmeta order_meta ON order_meta.post_id = orders.id
WHERE orders.post_type = 'shop_order'
AND orders.post_status in ( 'wc-completed', 'wc-processing', 'wc-refunded' )
AND meta_key in( '_payment_method','_order_total','_order_currency')
GROUP BY orders.id
) order_gateways
GROUP BY gateway, currency
"
);
}
$orders_by_gateway_currency = array();
@ -656,20 +753,33 @@ class WC_Tracker {
private static function get_orders_origins() {
global $wpdb;
$orders_origin = $wpdb->get_results(
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
$op_table_name = OrdersTableDataStore::get_operational_data_table_name();
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$orders_origin = $wpdb->get_results(
"
SELECT created_via as origin, COUNT( order_id ) as count
FROM $op_table_name
GROUP BY created_via;
"
);
// phpcs:enable
} else {
$orders_origin = $wpdb->get_results(
"
SELECT
meta_value as origin, COUNT( DISTINCT ( orders.id ) ) as count
FROM
$wpdb->posts orders
LEFT JOIN
$wpdb->postmeta order_meta ON order_meta.post_id = orders.id
WHERE
meta_key = '_created_via'
GROUP BY
meta_value;
"
SELECT
meta_value as origin, COUNT( DISTINCT ( orders.id ) ) as count
FROM
$wpdb->posts orders
LEFT JOIN
$wpdb->postmeta order_meta ON order_meta.post_id = orders.id
WHERE
meta_key = '_created_via'
GROUP BY
meta_value;
"
);
);
}
// The associative array that is created as the result of array_reduce is passed to extract_group_key()
// This function has the logic that will remove specific identifiers that may sometimes be part of an origin.
@ -836,7 +946,12 @@ class WC_Tracker {
* @return array
*/
private static function get_all_template_overrides() {
$override_data = array();
$override_data = array();
/**
* Filter the paths to scan for template overrides.
*
* @since 2.3.0
*/
$template_paths = apply_filters( 'woocommerce_template_overrides_scan_paths', array( 'WooCommerce' => WC()->plugin_path() . '/templates/' ) );
$scanned_files = array();

View File

@ -5,10 +5,13 @@
* @package WooCommerce\Tests\WC_Tracker.
*/
// phpcs:disable Squiz.Classes.ClassFileName.NoMatch, Squiz.Classes.ValidClassName.NotCamelCaps -- Backward compatibility.
/**
* Class WC_Tracker_Test
*/
class WC_Tracker_Test extends \WC_Unit_Test_Case {
// phpcs:enable
/**
* Test the tracking of wc_admin being disabled via filter.
*/
@ -64,4 +67,55 @@ class WC_Tracker_Test extends \WC_Unit_Test_Case {
$this->assertArrayHasKey( 'wc_admin_disabled', $tracking_data );
$this->assertEquals( 'no', $tracking_data['wc_admin_disabled'] );
}
/**
* @testDox Test orders tracking data.
*/
public function test_get_tracking_data_orders() {
$dummy_product = WC_Helper_Product::create_simple_product();
$status_entries = array( 'wc-processing', 'wc-completed', 'wc-refunded', 'wc-pending' );
$created_via_entries = array( 'api', 'checkout', 'admin' );
$payment_method_entries = array( 'paypal', 'stripe', 'cod' );
$order_count = count( $status_entries ) * count( $created_via_entries ) * count( $payment_method_entries );
foreach ( $status_entries as $status_entry ) {
foreach ( $created_via_entries as $created_via_entry ) {
foreach ( $payment_method_entries as $payment_method_entry ) {
$order = wc_create_order(
array(
'status' => $status_entry,
'created_via' => $created_via_entry,
'payment_method' => $payment_method_entry,
)
);
$order->add_product( $dummy_product );
$order->save();
$order->calculate_totals();
}
}
}
$order_data = WC_Tracker::get_tracking_data()['orders'];
foreach ( $status_entries as $status_entry ) {
$this->assertEquals( $order_count / count( $status_entries ), $order_data[ $status_entry ] );
}
// Gross revenue is for wc-completed and wc-refunded status, so we calculate expected revenue per status, multiply by 2, and then multiply by 10 to account for the 10 USD per status.
$this->assertEquals( ( $order_count / count( $status_entries ) ) * 2 * 10, $order_data['gross'] );
// Gross revenue is for wc-pending status, so we calculate expected revenue per status, multiply by 1, and then multiply by 10 to account for the 10 USD per status.
$this->assertEquals( ( $order_count / count( $status_entries ) ) * 1 * 10, $order_data['processing_gross'] );
// Order count per gateway is calculated for three status (completed, processing and refunded) so we multiply order count by 3 and then divide by the number of status entries.
$this->assertEquals( ( $order_count * 3 / count( $status_entries ) ), $order_data['gateway__USD_count'] );
// Order revenue per gateway is calculated for three status (completed, processing and refunded) so we multiply order count by 3, then by 10 to account for 10 USD per order and then divide by the number of status entries.
$this->assertEquals( ( $order_count * 3 * 10 / count( $status_entries ) ), $order_data['gateway__USD_total'] );
foreach ( $created_via_entries as $created_via_entry ) {
$this->assertEquals( ( $order_count / count( $created_via_entries ) ), $order_data['created_via'][ $created_via_entry ] );
}
}
}