diff --git a/plugins/woocommerce-admin/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php b/plugins/woocommerce-admin/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php index f73c0aeed38..82247536122 100644 --- a/plugins/woocommerce-admin/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php +++ b/plugins/woocommerce-admin/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php @@ -52,8 +52,8 @@ class WC_Admin_REST_Reports_Orders_Stats_Controller extends WC_Admin_REST_Report $args['status_is_not'] = (array) $request['status_is_not']; $args['product_includes'] = (array) $request['product_includes']; $args['product_excludes'] = (array) $request['product_excludes']; - $args['coupon_includes'] = (array) $request['coupon_includes']; - $args['coupon_excludes'] = (array) $request['coupon_excludes']; + $args['coupon_includes'] = (array) $request['coupon_includes']; + $args['coupon_excludes'] = (array) $request['coupon_excludes']; $args['customer'] = $request['customer']; $args['categories'] = (array) $request['categories']; @@ -319,6 +319,7 @@ class WC_Admin_REST_Reports_Orders_Stats_Controller extends WC_Admin_REST_Report 'type' => 'array', 'sanitize_callback' => 'wp_parse_slug_list', 'validate_callback' => 'rest_validate_request_arg', + 'default' => null, 'items' => array( 'enum' => $this->get_order_statuses(), 'type' => 'string', diff --git a/plugins/woocommerce-admin/includes/class-wc-admin-reports-interval.php b/plugins/woocommerce-admin/includes/class-wc-admin-reports-interval.php index 92e8627f4de..0ee5ec2316e 100644 --- a/plugins/woocommerce-admin/includes/class-wc-admin-reports-interval.php +++ b/plugins/woocommerce-admin/includes/class-wc-admin-reports-interval.php @@ -30,28 +30,29 @@ class WC_Admin_Reports_Interval { * Returns date format to be used as grouping clause in SQL. * * @param string $time_interval Time interval. + * @param string $table_name Name of the db table relevant for the date constraint. * @return mixed */ - public static function db_datetime_format( $time_interval ) { + public static function db_datetime_format( $time_interval, $table_name ) { $first_day_of_week = absint( get_option( 'start_of_week' ) ); if ( 1 === $first_day_of_week ) { // Week begins on Monday, ISO 8601. - $week_format = "DATE_FORMAT(date_created, '%x-%v')"; + $week_format = "DATE_FORMAT({$table_name}.date_created, '%x-%v')"; } else { // Week begins on day other than specified by ISO 8601, needs to be in sync with function simple_week_number. - $week_format = "CONCAT(YEAR(date_created), '-', LPAD( FLOOR( ( DAYOFYEAR(date_created) + ( ( DATE_FORMAT(MAKEDATE(YEAR(date_created),1), '%w') - $first_day_of_week + 7 ) % 7 ) - 1 ) / 7 ) + 1 , 2, '0'))"; + $week_format = "CONCAT(YEAR({$table_name}.date_created), '-', LPAD( FLOOR( ( DAYOFYEAR({$table_name}.date_created) + ( ( DATE_FORMAT(MAKEDATE(YEAR({$table_name}.date_created),1), '%w') - $first_day_of_week + 7 ) % 7 ) - 1 ) / 7 ) + 1 , 2, '0'))"; } // Whenever this is changed, double check method time_interval_id to make sure they are in sync. $mysql_date_format_mapping = array( - 'hour' => "DATE_FORMAT(date_created, '%Y-%m-%d %H')", - 'day' => "DATE_FORMAT(date_created, '%Y-%m-%d')", + 'hour' => "DATE_FORMAT({$table_name}.date_created, '%Y-%m-%d %H')", + 'day' => "DATE_FORMAT({$table_name}.date_created, '%Y-%m-%d')", 'week' => $week_format, - 'month' => "DATE_FORMAT(date_created, '%Y-%m')", - 'quarter' => "CONCAT(YEAR(date_created), '-', QUARTER(date_created))", - 'year' => 'YEAR(date_created)', + 'month' => "DATE_FORMAT({$table_name}.date_created, '%Y-%m')", + 'quarter' => "CONCAT(YEAR({$table_name}.date_created), '-', QUARTER({$table_name}.date_created))", + 'year' => "YEAR({$table_name}.date_created)", ); diff --git a/plugins/woocommerce-admin/includes/class-wc-admin-reports-orders-stats-query.php b/plugins/woocommerce-admin/includes/class-wc-admin-reports-orders-stats-query.php index 6d3c8ff838d..6ac3e3ab3d7 100644 --- a/plugins/woocommerce-admin/includes/class-wc-admin-reports-orders-stats-query.php +++ b/plugins/woocommerce-admin/includes/class-wc-admin-reports-orders-stats-query.php @@ -9,7 +9,7 @@ * 'interval' => 'week', * 'categories' => array(15, 18), * 'coupons' => array(138), - * 'order_status' => array('completed'), + * 'status_in' => array('completed'), * ); * $report = new WC_Admin_Reports_Orders_Stats_Query( $args ); * $mydata = $report->get_data(); diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-categories-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-categories-data-store.php index 85f2d31a1d0..2d81340813f 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-categories-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-categories-data-store.php @@ -59,6 +59,16 @@ class WC_Admin_Reports_Categories_Data_Store extends WC_Admin_Reports_Data_Store 'products_count' => 'COUNT(DISTINCT product_id) as products_count', ); + /** + * Constructor + */ + public function __construct() { + global $wpdb; + $table_name = $wpdb->prefix . self::TABLE_NAME; + // Avoid ambigious column order_id in SQL query. + $this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] ); + } + /** * Return the database query with parameters used for Categories report: time span and order status. * @@ -93,7 +103,7 @@ class WC_Admin_Reports_Categories_Data_Store extends WC_Admin_Reports_Data_Store $order_status_filter = $this->get_status_subquery( $query_args ); if ( $order_status_filter ) { - $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}posts ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}posts.ID"; + $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; $sql_query_params['where_clause'] .= " AND ( {$order_status_filter} )"; } @@ -216,9 +226,6 @@ class WC_Admin_Reports_Categories_Data_Store extends WC_Admin_Reports_Data_Store 'fields' => '*', 'categories' => array(), 'extended_info' => false, - // This is not a parameter for products reports per se, but maybe we should restricts order statuses here, too? - 'order_status' => parent::get_report_order_statuses(), - ); $query_args = wp_parse_args( $query_args, $defaults ); diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-data-store.php index f72b04d0627..934ad18fcc6 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-data-store.php @@ -41,6 +41,16 @@ class WC_Admin_Reports_Coupons_Data_Store extends WC_Admin_Reports_Data_Store im 'orders_count' => 'COUNT(DISTINCT order_id) as orders_count', ); + /** + * Constructor + */ + public function __construct() { + global $wpdb; + $table_name = $wpdb->prefix . self::TABLE_NAME; + // Avoid ambigious column order_id in SQL query. + $this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] ); + } + /** * Set up all the hooks for maintaining and populating table data. */ @@ -84,10 +94,9 @@ class WC_Admin_Reports_Coupons_Data_Store extends WC_Admin_Reports_Data_Store im $sql_query_params['where_clause'] .= " AND {$order_coupon_lookup_table}.coupon_id IN ({$included_coupons})"; } - // TODO: questionable, I think we need order status filters, even though it's not specified. $order_status_filter = $this->get_status_subquery( $query_args ); if ( $order_status_filter ) { - $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}posts ON {$order_coupon_lookup_table}.order_id = {$wpdb->prefix}posts.ID"; + $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_coupon_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; $sql_query_params['where_clause'] .= " AND ( {$order_status_filter} )"; } @@ -214,9 +223,6 @@ class WC_Admin_Reports_Coupons_Data_Store extends WC_Admin_Reports_Data_Store im 'fields' => '*', 'coupons' => array(), 'extended_info' => false, - // This is not a parameter for coupons reports per se, but we want to only take into account selected order types. - 'order_status' => parent::get_report_order_statuses(), - ); $query_args = wp_parse_args( $query_args, $defaults ); @@ -319,15 +325,6 @@ class WC_Admin_Reports_Coupons_Data_Store extends WC_Admin_Reports_Data_Store im return; } - if ( ! in_array( $order->get_status(), parent::get_report_order_statuses(), true ) ) { - $wpdb->delete( - $wpdb->prefix . self::TABLE_NAME, - array( 'order_id' => $order->get_id() ), - array( '%d' ) - ); - return; - } - $coupon_items = $order->get_items( 'coupon' ); foreach ( $coupon_items as $coupon_item ) { $wpdb->replace( diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-stats-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-stats-data-store.php index ba42223ca42..395eca39504 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-stats-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-coupons-stats-data-store.php @@ -39,6 +39,16 @@ class WC_Admin_Reports_Coupons_Stats_Data_Store extends WC_Admin_Reports_Coupons 'orders_count' => 'COUNT(DISTINCT order_id) as orders_count', ); + /** + * Constructor + */ + public function __construct() { + global $wpdb; + $table_name = $wpdb->prefix . self::TABLE_NAME; + // Avoid ambigious column order_id in SQL query. + $this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] ); + } + /** * Updates the database query with parameters used for Products Stats report: categories and order status. * @@ -61,7 +71,7 @@ class WC_Admin_Reports_Coupons_Stats_Data_Store extends WC_Admin_Reports_Coupons $order_status_filter = $this->get_status_subquery( $query_args ); if ( $order_status_filter ) { - $coupons_from_clause .= " JOIN {$wpdb->prefix}posts ON {$order_coupon_lookup_table}.order_id = {$wpdb->prefix}posts.ID"; + $coupons_from_clause .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_coupon_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; $coupons_where_clause .= " AND ( {$order_status_filter} )"; } @@ -99,8 +109,6 @@ class WC_Admin_Reports_Coupons_Stats_Data_Store extends WC_Admin_Reports_Coupons 'fields' => '*', 'interval' => 'week', 'coupons' => array(), - // This is not a parameter for products reports per se, but we should probably restricts order statuses here, too. - 'order_status' => parent::get_report_order_statuses(), ); $query_args = wp_parse_args( $query_args, $defaults ); @@ -160,7 +168,7 @@ class WC_Admin_Reports_Coupons_Stats_Data_Store extends WC_Admin_Reports_Coupons $totals = (object) $this->cast_numbers( $totals[0] ); // Intervals. - $this->update_intervals_sql_params( $intervals_query, $query_args, $db_interval_count, $expected_interval_count ); + $this->update_intervals_sql_params( $intervals_query, $query_args, $db_interval_count, $expected_interval_count, $table_name ); if ( '' !== $selections ) { $selections = ', ' . $selections; @@ -168,7 +176,7 @@ class WC_Admin_Reports_Coupons_Stats_Data_Store extends WC_Admin_Reports_Coupons $intervals = $wpdb->get_results( "SELECT - MAX(date_created) AS datetime_anchor, + MAX({$table_name}.date_created) AS datetime_anchor, {$intervals_query['select_clause']} AS time_interval {$selections} FROM diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-customers-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-customers-data-store.php index 16302ada7ef..05e4c5d94b4 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-customers-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-customers-data-store.php @@ -203,11 +203,13 @@ class WC_Admin_Reports_Customers_Data_Store extends WC_Admin_Reports_Data_Store */ protected function get_sql_query_params( $query_args ) { global $wpdb; - $customer_lookup_table = $wpdb->prefix . self::TABLE_NAME; + $customer_lookup_table = $wpdb->prefix . self::TABLE_NAME; + $order_stats_table_name = $wpdb->prefix . 'wc_order_stats'; - $sql_query_params = $this->get_time_period_sql_params( $query_args, $customer_lookup_table ); - $sql_query_params = array_merge( $sql_query_params, $this->get_limit_sql_params( $query_args ) ); - $sql_query_params = array_merge( $sql_query_params, $this->get_order_by_sql_params( $query_args ) ); + $sql_query_params = $this->get_time_period_sql_params( $query_args, $customer_lookup_table ); + $sql_query_params = array_merge( $sql_query_params, $this->get_limit_sql_params( $query_args ) ); + $sql_query_params = array_merge( $sql_query_params, $this->get_order_by_sql_params( $query_args ) ); + $sql_query_params['from_clause'] = " LEFT JOIN {$order_stats_table_name} ON {$customer_lookup_table}.customer_id = {$order_stats_table_name}.customer_id"; $match_operator = $this->get_match_operator( $query_args ); $where_clauses = array(); @@ -276,6 +278,11 @@ class WC_Admin_Reports_Customers_Data_Store extends WC_Admin_Reports_Data_Store $sql_query_params['where_clause'] = $preceding_match . implode( " {$match_operator} ", $where_clauses ); } + $order_status_filter = $this->get_status_subquery( $query_args ); + if ( $order_status_filter ) { + $sql_query_params['from_clause'] .= " AND ( {$order_status_filter} )"; + } + if ( $having_clauses ) { $preceding_match = empty( $sql_query_params['having_clause'] ) ? ' AND ' : " {$match_operator} "; $sql_query_params['having_clause'] .= $preceding_match . implode( " {$match_operator} ", $having_clauses ); @@ -293,7 +300,7 @@ class WC_Admin_Reports_Customers_Data_Store extends WC_Admin_Reports_Data_Store public function get_data( $query_args ) { global $wpdb; - $customers_table_name = $wpdb->prefix . self::TABLE_NAME; + $customers_table_name = $wpdb->prefix . self::TABLE_NAME; $order_stats_table_name = $wpdb->prefix . 'wc_order_stats'; // These defaults are only partially applied when used via REST API, as that has its own defaults. @@ -325,10 +332,7 @@ class WC_Admin_Reports_Customers_Data_Store extends WC_Admin_Reports_Data_Store SELECT {$customers_table_name}.customer_id FROM {$customers_table_name} - LEFT JOIN - {$order_stats_table_name} - ON - {$customers_table_name}.customer_id = {$order_stats_table_name}.customer_id + {$sql_query_params['from_clause']} WHERE 1=1 {$sql_query_params['where_time_clause']} @@ -352,10 +356,7 @@ class WC_Admin_Reports_Customers_Data_Store extends WC_Admin_Reports_Data_Store {$selections} FROM {$customers_table_name} - LEFT JOIN - {$order_stats_table_name} - ON - {$customers_table_name}.customer_id = {$order_stats_table_name}.customer_id + {$sql_query_params['from_clause']} WHERE 1=1 {$sql_query_params['where_time_clause']} diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-data-store.php index a65d2c4e638..ef1fef5cc43 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-data-store.php @@ -251,12 +251,13 @@ class WC_Admin_Reports_Data_Store { * If there are less records in the database than time intervals, then we need to remap offset in SQL query * to fetch correct records. * - * @param array $intervals_query Array with clauses for the Intervals SQL query. - * @param array $query_args Query arguements. - * @param int $db_interval_count Database interval count. - * @param int $expected_interval_count Expected interval count on the output. + * @param array $intervals_query Array with clauses for the Intervals SQL query. + * @param array $query_args Query arguements. + * @param int $db_interval_count Database interval count. + * @param int $expected_interval_count Expected interval count on the output. + * @param string $table_name Name of the db table relevant for the date constraint. */ - protected function update_intervals_sql_params( &$intervals_query, &$query_args, $db_interval_count, $expected_interval_count ) { + protected function update_intervals_sql_params( &$intervals_query, &$query_args, $db_interval_count, $expected_interval_count, $table_name ) { if ( $db_interval_count === $expected_interval_count ) { return; } @@ -328,8 +329,8 @@ class WC_Admin_Reports_Data_Store { $query_args['adj_after'] = $new_start_date->format( WC_Admin_Reports_Interval::$iso_datetime_format ); $query_args['adj_before'] = $new_end_date->format( WC_Admin_Reports_Interval::$iso_datetime_format ); $intervals_query['where_time_clause'] = ''; - $intervals_query['where_time_clause'] .= " AND date_created <= '{$query_args['adj_before']}'"; - $intervals_query['where_time_clause'] .= " AND date_created >= '{$query_args['adj_after']}'"; + $intervals_query['where_time_clause'] .= " AND {$table_name}.date_created <= '{$query_args['adj_before']}'"; + $intervals_query['where_time_clause'] .= " AND {$table_name}.date_created >= '{$query_args['adj_after']}'"; $intervals_query['limit'] = 'LIMIT 0,' . $intervals_query['per_page']; } else { if ( 'asc' === $query_args['order'] ) { @@ -396,12 +397,12 @@ class WC_Admin_Reports_Data_Store { } /** - * Get the order statuses used when calculating reports. + * Get the excluded order statuses used when calculating reports. * * @return array */ - protected static function get_report_order_statuses() { - return apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ); + protected static function get_excluded_report_order_statuses() { + return apply_filters( 'woocommerce_reports_excluded_order_statuses', array( 'refunded', 'pending', 'failed', 'cancelled' ) ); } /** @@ -585,7 +586,7 @@ class WC_Admin_Reports_Data_Store { if ( isset( $query_args['interval'] ) && '' !== $query_args['interval'] ) { $interval = $query_args['interval']; - $intervals_query['select_clause'] = WC_Admin_Reports_Interval::db_datetime_format( $interval ); + $intervals_query['select_clause'] = WC_Admin_Reports_Interval::db_datetime_format( $interval, $table_name ); } $intervals_query = array_merge( $intervals_query, $this->get_limit_sql_params( $query_args ) ); @@ -769,7 +770,8 @@ class WC_Admin_Reports_Data_Store { protected function get_status_subquery( $query_args, $operator = 'AND' ) { global $wpdb; - $subqueries = array(); + $subqueries = array(); + $excluded_statuses = array(); if ( isset( $query_args['status_is'] ) && is_array( $query_args['status_is'] ) && count( $query_args['status_is'] ) > 0 ) { $allowed_statuses = array_map( array( $this, 'normalize_order_status' ), $query_args['status_is'] ); if ( $allowed_statuses ) { @@ -778,10 +780,17 @@ class WC_Admin_Reports_Data_Store { } if ( isset( $query_args['status_is_not'] ) && is_array( $query_args['status_is_not'] ) && count( $query_args['status_is_not'] ) > 0 ) { - $forbidden_statuses = array_map( array( $this, 'normalize_order_status' ), $query_args['status_is_not'] ); - if ( $forbidden_statuses ) { - $subqueries[] = "{$wpdb->prefix}wc_order_stats.status NOT IN ( '" . implode( "','", $forbidden_statuses ) . "' )"; - } + $excluded_statuses = array_map( array( $this, 'normalize_order_status' ), $query_args['status_is_not'] ); + } + + if ( ( ! isset( $query_args['status_is'] ) || empty( $query_args['status_is'] ) ) + && ( ! isset( $query_args['status_is_not'] ) || empty( $query_args['status_is_not'] ) ) + ) { + $excluded_statuses = array_map( array( $this, 'normalize_order_status' ), $this->get_excluded_report_order_statuses() ); + } + + if ( $excluded_statuses ) { + $subqueries[] = "{$wpdb->prefix}wc_order_stats.status NOT IN ( '" . implode( "','", $excluded_statuses ) . "' )"; } return implode( " $operator ", $subqueries ); diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-downloads-stats-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-downloads-stats-data-store.php index c08e6bc9256..69df11ac3b7 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-downloads-stats-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-downloads-stats-data-store.php @@ -107,7 +107,7 @@ class WC_Admin_Reports_Downloads_Stats_Data_Store extends WC_Admin_Reports_Downl return array(); } - $this->update_intervals_sql_params( $intervals_query, $query_args, $db_records_count, $expected_interval_count ); + $this->update_intervals_sql_params( $intervals_query, $query_args, $db_records_count, $expected_interval_count, $table_name ); $intervals_query['where_time_clause'] = str_replace( 'date_created', 'timestamp', $intervals_query['where_time_clause'] ); $totals = $wpdb->get_results( diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-data-store.php index 0888fef33ee..b97820961ca 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-data-store.php @@ -141,7 +141,7 @@ class WC_Admin_Reports_Orders_Data_Store extends WC_Admin_Reports_Data_Store imp 'coupon_includes' => array(), 'coupon_excludes' => array(), 'customer_type' => null, - 'status_is' => parent::get_report_order_statuses(), + 'status_is' => array(), 'extended_info' => false, ); $query_args = wp_parse_args( $query_args, $defaults ); @@ -228,34 +228,6 @@ class WC_Admin_Reports_Orders_Data_Store extends WC_Admin_Reports_Data_Store imp if ( 'date' === $order_by ) { return 'date_created'; } - - return $order_by; - } - - /** - * Returns order status subquery to be used in WHERE SQL query, based on query arguments from the user. - * - * @param array $query_args Parameters supplied by the user. - * @param string $operator AND or OR, based on match query argument. - * @return string - */ - protected function get_status_subquery( $query_args, $operator = 'AND' ) { - $subqueries = array(); - if ( isset( $query_args['status_is'] ) && is_array( $query_args['status_is'] ) && count( $query_args['status_is'] ) > 0 ) { - $allowed_statuses = array_map( array( $this, 'normalize_order_status' ), $query_args['status_is'] ); - if ( $allowed_statuses ) { - $subqueries[] = "status IN ( '" . implode( "','", $allowed_statuses ) . "' )"; - } - } - - if ( isset( $query_args['status_is_not'] ) && is_array( $query_args['status_is_not'] ) && count( $query_args['status_is_not'] ) > 0 ) { - $forbidden_statuses = array_map( array( $this, 'normalize_order_status' ), $query_args['status_is_not'] ); - if ( $forbidden_statuses ) { - $subqueries[] = "status NOT IN ( '" . implode( "','", $forbidden_statuses ) . "' )"; - } - } - - return implode( " $operator ", $subqueries ); } /** diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-stats-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-stats-data-store.php index be25ee82596..ddaab352d8e 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-stats-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-orders-stats-data-store.php @@ -170,11 +170,6 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto )"; } - $order_status_filter = $this->get_status_subquery( $query_args, $operator ); - if ( $order_status_filter ) { - $where_filters[] = $order_status_filter; - } - $customer_filter = $this->get_customer_subquery( $query_args ); if ( $customer_filter ) { $where_filters[] = $customer_filter; @@ -182,6 +177,15 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto $where_subclause = implode( " $operator ", $where_filters ); + // Append status filter after to avoid matching ANY on default statuses. + $order_status_filter = $this->get_status_subquery( $query_args, $operator ); + if ( $order_status_filter ) { + if ( empty( $query_args['status_is'] ) && empty( $query_args['status_is_not'] ) ) { + $operator = 'AND'; + } + $where_subclause = implode( " $operator ", array_filter( array( $where_subclause, $order_status_filter ) ) ); + } + // To avoid requesting the subqueries twice, the result is applied to all queries passed to the method. if ( $where_subclause ) { $totals_query['where_clause'] .= " AND ( $where_subclause )"; @@ -290,7 +294,7 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto return $data; } - $this->update_intervals_sql_params( $intervals_query, $query_args, $db_interval_count, $expected_interval_count ); + $this->update_intervals_sql_params( $intervals_query, $query_args, $db_interval_count, $expected_interval_count, $table_name ); if ( '' !== $selections ) { $selections = ', ' . $selections; @@ -381,7 +385,6 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto $order_ids = wc_get_orders( array( 'limit' => -1, - 'status' => parent::get_report_order_statuses(), 'type' => 'shop_order', 'return' => 'ids', ) @@ -437,16 +440,6 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto return false; } - if ( ! in_array( $order->get_status(), parent::get_report_order_statuses(), true ) ) { - $wpdb->delete( - $table_name, - array( - 'order_id' => $order->get_id(), - ) - ); - return; - } - $data = array( 'order_id' => $order->get_id(), 'date_created' => $order->get_date_created()->date( 'Y-m-d H:i:s' ), diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php index 3ab7d9a2eaf..f663d8c366c 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-data-store.php @@ -72,6 +72,16 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i 'sku', ); + /** + * Constructor + */ + public function __construct() { + global $wpdb; + $table_name = $wpdb->prefix . self::TABLE_NAME; + // Avoid ambigious column order_id in SQL query. + $this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] ); + } + /** * Set up all the hooks for maintaining and populating table data. */ @@ -134,7 +144,7 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i $order_status_filter = $this->get_status_subquery( $query_args ); if ( $order_status_filter ) { - $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}posts ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}posts.ID"; + $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; $sql_query_params['where_clause'] .= " AND ( {$order_status_filter} )"; } @@ -148,8 +158,11 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i * @return string */ protected function normalize_order_by( $order_by ) { + global $wpdb; + $order_product_lookup_table = $wpdb->prefix . self::TABLE_NAME; + if ( 'date' === $order_by ) { - return 'date_created'; + return $order_product_lookup_table . '.date_created'; } if ( 'product_name' === $order_by ) { return '_products.post_title'; @@ -214,9 +227,6 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i 'categories' => array(), 'product_includes' => array(), 'extended_info' => false, - // This is not a parameter for products reports per se, but we want to only take into account selected order types. - 'order_status' => parent::get_report_order_statuses(), - ); $query_args = wp_parse_args( $query_args, $defaults ); @@ -321,15 +331,6 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i return; } - if ( ! in_array( $order->get_status(), parent::get_report_order_statuses(), true ) ) { - $wpdb->delete( - $wpdb->prefix . self::TABLE_NAME, - array( 'order_id' => $order->get_id() ), - array( '%d' ) - ); - return; - } - $refunds = self::get_order_refund_items( $order ); foreach ( $order->get_items() as $order_item ) { diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-stats-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-stats-data-store.php index 44ec6bcb364..f310418ec61 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-stats-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-products-stats-data-store.php @@ -40,6 +40,16 @@ class WC_Admin_Reports_Products_Stats_Data_Store extends WC_Admin_Reports_Produc 'products_count' => 'COUNT(DISTINCT product_id) as products_count', ); + /** + * Constructor + */ + public function __construct() { + global $wpdb; + $table_name = $wpdb->prefix . self::TABLE_NAME; + // Avoid ambigious column order_id in SQL query. + $this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] ); + } + /** * Updates the database query with parameters used for Products Stats report: categories and order status. * @@ -62,7 +72,7 @@ class WC_Admin_Reports_Products_Stats_Data_Store extends WC_Admin_Reports_Produc $order_status_filter = $this->get_status_subquery( $query_args ); if ( $order_status_filter ) { - $products_from_clause .= " JOIN {$wpdb->prefix}posts ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}posts.ID"; + $products_from_clause .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; $products_where_clause .= " AND ( {$order_status_filter} )"; } @@ -101,8 +111,6 @@ class WC_Admin_Reports_Products_Stats_Data_Store extends WC_Admin_Reports_Produc 'categories' => array(), 'interval' => 'week', 'product_includes' => array(), - // This is not a parameter for products reports per se, but we should probably restricts order statuses here, too. - 'order_status' => parent::get_report_order_statuses(), ); $query_args = wp_parse_args( $query_args, $defaults ); @@ -136,7 +144,7 @@ class WC_Admin_Reports_Products_Stats_Data_Store extends WC_Admin_Reports_Produc return array(); } - $this->update_intervals_sql_params( $intervals_query, $query_args, $db_interval_count, $expected_interval_count ); + $this->update_intervals_sql_params( $intervals_query, $query_args, $db_interval_count, $expected_interval_count, $table_name ); $totals = $wpdb->get_results( "SELECT @@ -161,7 +169,7 @@ class WC_Admin_Reports_Products_Stats_Data_Store extends WC_Admin_Reports_Produc $intervals = $wpdb->get_results( "SELECT - MAX(date_created) AS datetime_anchor, + MAX(${table_name}.date_created) AS datetime_anchor, {$intervals_query['select_clause']} AS time_interval {$selections} FROM diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-data-store.php index c6581d3cd42..f5323f7536d 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-data-store.php @@ -61,8 +61,9 @@ class WC_Admin_Reports_Taxes_Data_Store extends WC_Admin_Reports_Data_Store impl public function __construct() { global $wpdb; $table_name = $wpdb->prefix . self::TABLE_NAME; - // Avoid ambigious column tax_rate_id in SQL query. - $this->report_columns['tax_rate_id'] = $table_name . '.' . $this->report_columns['tax_rate_id']; + // Avoid ambigious columns in SQL query. + $this->report_columns['tax_rate_id'] = $table_name . '.' . $this->report_columns['tax_rate_id']; + $this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] ); } /** @@ -94,6 +95,12 @@ class WC_Admin_Reports_Taxes_Data_Store extends WC_Admin_Reports_Data_Store impl $sql_query_params['where_clause'] .= " AND {$order_tax_lookup_table}.tax_rate_id IN ({$allowed_taxes})"; } + $order_status_filter = $this->get_status_subquery( $query_args ); + if ( $order_status_filter ) { + $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_tax_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; + $sql_query_params['where_clause'] .= " AND ( {$order_status_filter} )"; + } + $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}woocommerce_tax_rates ON {$order_tax_lookup_table}.tax_rate_id = {$wpdb->prefix}woocommerce_tax_rates.tax_rate_id"; return $sql_query_params; @@ -257,15 +264,6 @@ class WC_Admin_Reports_Taxes_Data_Store extends WC_Admin_Reports_Data_Store impl return; } - if ( ! in_array( $order->get_status(), parent::get_report_order_statuses(), true ) ) { - $wpdb->delete( - $wpdb->prefix . self::TABLE_NAME, - array( 'order_id' => $order->get_id() ), - array( '%d' ) - ); - return; - } - foreach ( $order->get_items( 'tax' ) as $tax_item ) { $wpdb->replace( $wpdb->prefix . self::TABLE_NAME, diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-stats-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-stats-data-store.php index ca50e5919f1..5dc32c70506 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-stats-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-taxes-stats-data-store.php @@ -66,6 +66,12 @@ class WC_Admin_Reports_Taxes_Stats_Data_Store extends WC_Admin_Reports_Data_Stor $sql_query_params['where_clause'] .= " AND {$order_tax_lookup_table}.tax_rate_id IN ({$allowed_taxes})"; } + $order_status_filter = $this->get_status_subquery( $query_args ); + if ( $order_status_filter ) { + $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; + $sql_query_params['where_clause'] .= " AND ( {$order_status_filter} )"; + } + return $sql_query_params; } diff --git a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-variations-data-store.php b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-variations-data-store.php index 36536bf3876..e6107fff7c6 100644 --- a/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-variations-data-store.php +++ b/plugins/woocommerce-admin/includes/data-stores/class-wc-admin-reports-variations-data-store.php @@ -66,6 +66,15 @@ class WC_Admin_Reports_Variations_Data_Store extends WC_Admin_Reports_Data_Store 'low_stock_amount', ); + /** + * Constructor + */ + public function __construct() { + global $wpdb; + $table_name = $wpdb->prefix . self::TABLE_NAME; + // Avoid ambigious column order_id in SQL query. + $this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] ); + } /** * Updates the database query with parameters used for Products report: categories and order status. @@ -91,11 +100,10 @@ class WC_Admin_Reports_Variations_Data_Store extends WC_Admin_Reports_Data_Store $sql_query_params['where_clause'] .= " AND {$order_product_lookup_table}.variation_id IN ({$allowed_variations_str})"; } - if ( is_array( $query_args['order_status'] ) && count( $query_args['order_status'] ) > 0 ) { - $statuses = array_map( array( $this, 'normalize_order_status' ), $query_args['order_status'] ); - - $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}posts ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}posts.ID"; - $sql_query_params['where_clause'] .= " AND {$wpdb->prefix}posts.post_status IN ( '" . implode( "','", $statuses ) . "' ) "; + $order_status_filter = $this->get_status_subquery( $query_args ); + if ( $order_status_filter ) { + $sql_query_params['from_clause'] .= " JOIN {$wpdb->prefix}wc_order_stats ON {$order_product_lookup_table}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; + $sql_query_params['where_clause'] .= " AND ( {$order_status_filter} )"; $sql_query_params['where_clause'] .= ' AND variation_id > 0'; } @@ -109,8 +117,11 @@ class WC_Admin_Reports_Variations_Data_Store extends WC_Admin_Reports_Data_Store * @return string */ protected function normalize_order_by( $order_by ) { + global $wpdb; + $table_name = $wpdb->prefix . self::TABLE_NAME; + if ( 'date' === $order_by ) { - return 'date_created'; + return $table_name . '.date_created'; } return $order_by; @@ -190,9 +201,6 @@ class WC_Admin_Reports_Variations_Data_Store extends WC_Admin_Reports_Data_Store 'products' => array(), 'variations' => array(), 'extended_info' => false, - // This is not a parameter for products reports per se, but we want to only take into account selected order types. - 'order_status' => parent::get_report_order_statuses(), - ); $query_args = wp_parse_args( $query_args, $defaults ); diff --git a/plugins/woocommerce-admin/tests/api/reports-taxes.php b/plugins/woocommerce-admin/tests/api/reports-taxes.php index 85b96275864..03d4568385e 100644 --- a/plugins/woocommerce-admin/tests/api/reports-taxes.php +++ b/plugins/woocommerce-admin/tests/api/reports-taxes.php @@ -82,7 +82,7 @@ class WC_Tests_API_Reports_Taxes extends WC_REST_Unit_Test_Case { $wpdb->insert( $wpdb->prefix . 'wc_order_tax_lookup', array( - 'order_id' => 1, + 'order_id' => $order->get_id(), 'tax_rate_id' => 1, 'date_created' => date( 'Y-m-d H:i:s' ), 'shipping_tax' => 2,