From 9d7e503519f96ca6229445f3e1bf1e59a3d956fa Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Fri, 17 Feb 2023 10:29:36 -0300 Subject: [PATCH] Add date sorting configuration for Analytics (#36492) * Include fields date_paid and date_completed to wp_wc_order_stats TODO: add script to create new columns in the database * Add update script and new columns in get_schema * Add new configuration in Analytics > Settings to configure type of date used in Revenue report * Change date_column_name to date_paid TODO: This will be configurable in future * Add date type config field * Use customizable date field in DataStore * Change label * Fix linter errors * Remove blank line * Put default column name back to date_created * Make date_paid and date_completed nullable to help with unit tests * Remove new table creation in update function and use query method instead of get_var * Extend stats constructor * Improve date type configuration description * Set date column name default to date_created to test if build passes * Fix phpcs issue on constructor * Remove cache bypass added by mistake * Improve changelog * Fill date_paid and date_completed for refunds to avoid problems whem they are being used to sort Fix unit tests when date_paid or date_completed are being used as sort date * Change default to date_created * Bump update script to 7.5.0 * Add prefix and postmeta variables for script --- .../client/analytics/settings/config.js | 27 +++++++++++++++++++ .../client/analytics/settings/setting.js | 19 ++++++++++++- .../add-date-sorting-options-for-reports | 4 +++ .../includes/abstracts/abstract-wc-order.php | 21 +++++++++++++++ .../woocommerce/includes/class-wc-install.php | 5 ++++ .../includes/wc-update-functions.php | 25 +++++++++++++++++ .../API/Reports/Orders/Stats/DataStore.php | 18 +++++++++++++ .../src/Internal/Admin/Settings.php | 13 +++++++++ .../class-wc-tests-reports-orders-stats.php | 17 ++++++++++++ 9 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce/changelog/add-date-sorting-options-for-reports diff --git a/plugins/woocommerce-admin/client/analytics/settings/config.js b/plugins/woocommerce-admin/client/analytics/settings/config.js index 5e829f8c3d7..1de8237aa9f 100644 --- a/plugins/woocommerce-admin/client/analytics/settings/config.js +++ b/plugins/woocommerce-admin/client/analytics/settings/config.js @@ -118,4 +118,31 @@ export const config = applyFilters( SETTINGS_FILTER, { ), defaultValue: DEFAULT_DATE_RANGE, }, + woocommerce_date_type: { + name: 'woocommerce_date_type', + label: __( 'Date type:', 'woocommerce' ), + inputType: 'select', + options: [ + { + label: __( 'Date created', 'woocommerce' ), + value: 'date_created', + key: 'date_created', + }, + { + label: __( 'Date paid', 'woocommerce' ), + value: 'date_paid', + key: 'date_paid', + }, + { + label: __( 'Date completed', 'woocommerce' ), + value: 'date_completed', + key: 'date_completed', + }, + ], + helpText: __( + 'Database date field considered for Revenue and Orders reports', + 'woocommerce' + ), + defaultValue: 'date_created', + }, } ); diff --git a/plugins/woocommerce-admin/client/analytics/settings/setting.js b/plugins/woocommerce-admin/client/analytics/settings/setting.js index 3239977830b..c0ce772ce76 100644 --- a/plugins/woocommerce-admin/client/analytics/settings/setting.js +++ b/plugins/woocommerce-admin/client/analytics/settings/setting.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { Button, CheckboxControl } from '@wordpress/components'; +import { Button, CheckboxControl, SelectControl } from '@wordpress/components'; import { Component } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import PropTypes from 'prop-types'; @@ -75,6 +75,22 @@ class Setting extends Component { { ...this.props } /> ); + case 'select': + return ( + + handleChange( { + target: { + name, + type: 'select', + value: newValue, + }, + } ) + } + /> + ); case 'text': default: const id = uniqueId( name ); @@ -188,6 +204,7 @@ Setting.propTypes = { 'checkboxGroup', 'text', 'component', + 'select', ] ), /** * Label used for describing the setting. diff --git a/plugins/woocommerce/changelog/add-date-sorting-options-for-reports b/plugins/woocommerce/changelog/add-date-sorting-options-for-reports new file mode 100644 index 00000000000..ba2e377d550 --- /dev/null +++ b/plugins/woocommerce/changelog/add-date-sorting-options-for-reports @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Add date_paid and date_completed date sorting options for Revenue and Order reports diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php index 78932ad68bc..e093a1fd3ec 100644 --- a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php +++ b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php @@ -350,6 +350,27 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { return $this->get_prop( 'date_modified', $context ); } + /** + * Get date_modified. + * + * @param string $context View or edit context. + * @return WC_DateTime|NULL object if the date is set or null if there is no date. + */ + public function get_date_paid( $context = 'view' ) { + return $this->get_prop( 'date_paid', $context ); + } + + /** + * Get date_modified. + * + * @param string $context View or edit context. + * @return WC_DateTime|NULL object if the date is set or null if there is no date. + */ + public function get_date_completed( $context = 'view' ) { + return $this->get_prop( 'date_completed', $context ); + } + + /** * Return the order statuses without wc- internal prefix. * diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php index 0460da9bbb9..cd3e80c7f4b 100644 --- a/plugins/woocommerce/includes/class-wc-install.php +++ b/plugins/woocommerce/includes/class-wc-install.php @@ -225,6 +225,9 @@ class WC_Install { 'wc_update_722_adjust_new_zealand_states', 'wc_update_722_adjust_ukraine_states', ), + '7.5.0' => array( + 'wc_update_750_add_columns_to_order_stats_table', + ), ); /** @@ -1312,6 +1315,8 @@ CREATE TABLE {$wpdb->prefix}wc_order_stats ( parent_id bigint(20) unsigned DEFAULT 0 NOT NULL, date_created datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, date_created_gmt datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, + date_paid datetime DEFAULT '0000-00-00 00:00:00', + date_completed datetime DEFAULT '0000-00-00 00:00:00', num_items_sold int(11) DEFAULT 0 NOT NULL, total_sales double DEFAULT 0 NOT NULL, tax_total double DEFAULT 0 NOT NULL, diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php index 691ba690cb9..856f23bab66 100644 --- a/plugins/woocommerce/includes/wc-update-functions.php +++ b/plugins/woocommerce/includes/wc-update-functions.php @@ -2558,3 +2558,28 @@ function wc_update_722_adjust_new_zealand_states() { function wc_update_722_adjust_ukraine_states() { return wc_update_721_adjust_ukraine_states(); } + +/** + * Add new columns date_paid and date_completed to wp_wc_order_stats table in order to provide the option + * of using the dates in the reports + */ +function wc_update_750_add_columns_to_order_stats_table() { + global $wpdb; + + $wpdb->query( + "UPDATE {$wpdb->prefix}wc_order_stats AS order_stats + INNER JOIN {$wpdb->postmeta} AS postmeta + ON postmeta.post_id = order_stats.order_id + and postmeta.meta_key = '_date_paid' + SET order_stats.date_paid = IFNULL(FROM_UNIXTIME(postmeta.meta_value), '0000-00-00 00:00:00');" + ); + + $wpdb->query( + "UPDATE {$wpdb->prefix}wc_order_stats AS order_stats + INNER JOIN {$wpdb->postmeta} AS postmeta + ON postmeta.post_id = order_stats.order_id + and postmeta.meta_key = '_date_completed' + SET order_stats.date_completed = IFNULL(FROM_UNIXTIME(postmeta.meta_value), '0000-00-00 00:00:00');" + ); + +} diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php index b2ef6217df0..08dfb74a8c0 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php @@ -69,6 +69,14 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { */ protected $context = 'orders_stats'; + /** + * Dynamically sets the date column name based on configuration + */ + public function __construct() { + parent::__construct(); + $this->date_column_name = get_option( 'woocommerce_date_type', 'date_created' ); + } + /** * Assign report columns once full table name has been assigned. */ @@ -517,6 +525,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { 'order_id' => $order->get_id(), 'parent_id' => $order->get_parent_id(), 'date_created' => $order->get_date_created()->date( 'Y-m-d H:i:s' ), + 'date_paid' => $order->get_date_paid() ? $order->get_date_paid()->date( 'Y-m-d H:i:s' ) : null, + 'date_completed' => $order->get_date_completed() ? $order->get_date_completed()->date( 'Y-m-d H:i:s' ) : null, 'date_created_gmt' => gmdate( 'Y-m-d H:i:s', $order->get_date_created()->getTimestamp() ), 'num_items_sold' => self::get_num_items_sold( $order ), 'total_sales' => $order->get_total(), @@ -535,6 +545,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { '%d', '%s', '%s', + '%s', + '%s', '%d', '%f', '%f', @@ -551,6 +563,12 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { $data['parent_id'] = $parent_order->get_id(); $data['status'] = self::normalize_order_status( $parent_order->get_status() ); } + /** + * Set date_completed and date_paid the same as date_created to avoid problems + * when they are being used to sort the data, as refunds don't have them filled + */ + $data['date_completed'] = $data['date_created']; + $data['date_paid'] = $data['date_created']; } // Update or add the information to the DB. diff --git a/plugins/woocommerce/src/Internal/Admin/Settings.php b/plugins/woocommerce/src/Internal/Admin/Settings.php index f8a3d306158..772e4383134 100644 --- a/plugins/woocommerce/src/Internal/Admin/Settings.php +++ b/plugins/woocommerce/src/Internal/Admin/Settings.php @@ -288,6 +288,19 @@ class Settings { 'default' => 'period=month&compare=previous_year', 'type' => 'text', ); + $settings[] = array( + 'id' => 'woocommerce_date_type', + 'option_key' => 'woocommerce_date_type', + 'label' => __( 'Date Type', 'woocommerce' ), + 'description' => __( 'Database date field considered for Revenue and Orders reports', 'woocommerce' ), + 'default' => 'date_created', + 'type' => 'select', + 'options' => array( + 'date_created' => 'date_created', + 'date_paid' => 'date_paid', + 'date_completed' => 'date_completed', + ), + ); return $settings; } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders-stats.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders-stats.php index f32c2984494..c21899cd703 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders-stats.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders-stats.php @@ -195,10 +195,14 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { ), ); + $time = time(); + foreach ( $order_types as $order_type ) { $order = WC_Helper_Order::create_order( 1, $product ); $order->set_status( $order_type['status'] ); $order->set_total( $order_type['total'] ); + $order->set_date_created( $time ); + $order->set_date_paid( $time ); $order->set_shipping_total( 0 ); $order->set_cart_tax( 0 ); $order->save(); @@ -352,10 +356,14 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { ), ); + $time = time(); + foreach ( $order_types as $order_type ) { $order = WC_Helper_Order::create_order( 1, $product ); $order->set_status( $order_type['status'] ); $order->set_total( $order_type['total'] ); + $order->set_date_created( $time ); + $order->set_date_paid( $time ); $order->set_shipping_total( 0 ); $order->set_cart_tax( 0 ); $order->save(); @@ -3946,6 +3954,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { // Order 3: 4 x product 1, done one hour earlier. $order_3 = WC_Helper_Order::create_order( $customer_1->get_id(), $product_1 ); $order_3->set_date_created( $order_3_time ); + $order_3->set_date_paid( $order_3_time ); $order_3->set_status( $order_status ); $order_3->calculate_totals(); $order_3->save(); @@ -4526,6 +4535,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { // Order with 1 product. $order = WC_Helper_Order::create_order( $customer->get_id(), $product ); $order->set_date_created( $order_time ); + $order->set_date_paid( $order_time ); $order->set_status( $order_status ); $order->calculate_totals(); @@ -5309,6 +5319,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { // Order with 1 product. $order = WC_Helper_Order::create_order( $customer->get_id(), $product ); $order->set_date_created( $order_time ); + $order->set_date_paid( $order_time ); $order->set_status( $order_status ); $order->calculate_totals(); @@ -6078,6 +6089,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { $order_0 = WC_Helper_Order::create_order( 0, $product ); $order_0->set_date_created( $order_0_time ); + $order_0->set_date_paid( $order_0_time ); $order_0->set_status( 'processing' ); $order_0->set_total( 100 ); $order_0->save(); @@ -6097,6 +6109,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { // Place an order 'one hour later', 2 orders, but still just one customer. $order_1 = WC_Helper_Order::create_order( 0, $product ); $order_1->set_date_created( $order_1_time ); + $order_1->set_date_paid( $order_1_time ); $order_1->set_status( 'processing' ); $order_1->set_total( 100 ); $order_1->save(); @@ -6135,6 +6148,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { $order_2 = WC_Helper_Order::create_order( 0, $product ); $order_2->set_date_created( $order_1_time ); + $order_2->set_date_paid( $order_1_time ); $order_2->set_status( 'processing' ); $order_2->set_total( 100 ); $order_2->save(); @@ -6185,6 +6199,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { $order_0 = WC_Helper_Order::create_order( $customer_1->get_id(), $product ); $order_0->set_date_created( $order_0_time ); + $order_0->set_date_paid( $order_0_time ); $order_0->set_status( 'processing' ); $order_0->set_total( 100 ); $order_0->save(); @@ -6204,6 +6219,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { // Place an order 'one hour later', 2 orders, but still just one customer. $order_1 = WC_Helper_Order::create_order( $customer_1->get_id(), $product ); $order_1->set_date_created( $order_1_time ); + $order_1->set_date_paid( $order_1_time ); $order_1->set_status( 'processing' ); $order_1->set_total( 100 ); $order_1->save(); @@ -6242,6 +6258,7 @@ class WC_Admin_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case { $order_2 = WC_Helper_Order::create_order( $customer_1->get_id(), $product ); $order_2->set_date_created( $order_1_time ); + $order_2->set_date_paid( $order_1_time ); $order_2->set_status( 'processing' ); $order_2->set_total( 100 ); $order_2->save();