Display "Origin" column in Orders table in Orders Analytics (#46424)

* Display channel column in Orders Analytics UI.

* Set $data to false to not use cache.

For development purpose.

* Get channel info from order meta and put into extended_info.

* Set channel in order item level.

* Replace channel with get_origin_label in extended_info.

* Remove unnneeded channel in orders_data.

* Display origin instead of channel in table.

* Fix lint errors.

* Query order meta table based on HPOS.

* Remove code for development purpose.

* Add changelog.

* Fix code comment.

* Guard against null values.

`_wc_order_attribution_utm_source` can be null when the order is created via web admin.

* Set default origin label to "Unknown".

This default is the same as found in `output_origin_column` function in plugins/woocommerce/src/Internal/Orders/OrderAttributionController.php.

* Support server side report download.

This is needed when there are more than 25 records in the Orders Analytics report.

* Fix failed test.

* Fix lint error.

* Simplify code.

We loop through $orders_data instead of $order_attributions data. This is because $order_attributions may not have attributions data for some orders.

By looping through $orders_data, it would automatically cater for "Unknown" data from get_origin_label function call, and we don't need to specify "Unknown" as default value for origin.

* Change "channel" to "attribution" object in controller.

This is because we need more than one piece of information, so we need it to be an object with properties like `origin`, `device` etc.

Co-authored-by: Kader Ibrahim S <kader.ibrahim.s@a8c.com>

* Change `origin` string to `attribution` array in Orders DataStore.

Co-authored-by: Kader Ibrahim S <kader.ibrahim.s@a8c.com>

* Change origin string to attribution object.

* Fix indexing after changing from origin string to attribution object.

* Change from origin string to attribution object in table.js.

* Simplify code.

To make code shorter and easier to read, instead of long horizontal code.

* Fix lint errors.

* Fix failed test.

* Fix lint error.

* Fix retrieving origin in CSV export.

* Use $wpdb->postmeta; cosmetic change.

Co-authored-by: Kader Ibrahim S <kader.ibrahim.s@a8c.com>

* Cosmetic change.

Co-authored-by: Kader Ibrahim S <kader.ibrahim.s@a8c.com>

* Sanitize order IDs by using absint.

Co-authored-by: Kader Ibrahim S <kader.ibrahim.s@a8c.com>

---------

Co-authored-by: Kader Ibrahim S <kader.ibrahim.s@a8c.com>
This commit is contained in:
Gan Eng Chin 2024-04-19 00:16:26 +08:00 committed by GitHub
parent 2d837ac687
commit 48f2a4c5ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 102 additions and 18 deletions

View File

@ -91,6 +91,13 @@ class OrdersReportTable extends Component {
isSortable: true,
isNumeric: true,
},
{
label: __( 'Origin', 'woocommerce' ),
screenReaderLabel: __( 'Origin', 'woocommerce' ),
key: 'origin',
required: false,
isSortable: false,
},
];
}
@ -241,6 +248,10 @@ class OrdersReportTable extends Component {
display: renderCurrency( netTotal, currency ),
value: netTotal,
},
{
display: extendedInfo.attribution.origin,
value: extendedInfo.attribution.origin,
},
];
} );
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add origin column in Orders Analytics report.

View File

@ -211,24 +211,30 @@ class Controller extends ReportsController implements ExportableInterface {
'readonly' => true,
),
'extended_info' => array(
'products' => array(
'products' => array(
'type' => 'array',
'readonly' => true,
'context' => array( 'view', 'edit' ),
'description' => __( 'List of order product IDs, names, quantities.', 'woocommerce' ),
),
'coupons' => array(
'coupons' => array(
'type' => 'array',
'readonly' => true,
'context' => array( 'view', 'edit' ),
'description' => __( 'List of order coupons.', 'woocommerce' ),
),
'customer' => array(
'customer' => array(
'type' => 'object',
'readonly' => true,
'context' => array( 'view', 'edit' ),
'description' => __( 'Order customer information.', 'woocommerce' ),
),
'attribution' => array(
'type' => 'object',
'readonly' => true,
'context' => array( 'view', 'edit' ),
'description' => __( 'Order attribution information.', 'woocommerce' ),
),
),
),
);
@ -526,6 +532,7 @@ class Controller extends ReportsController implements ExportableInterface {
'num_items_sold' => __( 'Items sold', 'woocommerce' ),
'coupons' => __( 'Coupon(s)', 'woocommerce' ),
'net_total' => __( 'N. Revenue', 'woocommerce' ),
'origin' => __( 'Origin', 'woocommerce' ),
);
/**
@ -558,6 +565,7 @@ class Controller extends ReportsController implements ExportableInterface {
'num_items_sold' => $item['num_items_sold'],
'coupons' => isset( $item['extended_info']['coupons'] ) ? $this->get_coupons( $item['extended_info']['coupons'] ) : null,
'net_total' => $item['net_total'],
'origin' => $item['extended_info']['attribution']['origin'],
);
/**

View File

@ -7,6 +7,9 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Orders;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Internal\Traits\OrderAttributionMeta;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use Automattic\WooCommerce\Utilities\OrderUtil;
use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore;
use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface;
use Automattic\WooCommerce\Admin\API\Reports\SqlQuery;
@ -18,6 +21,7 @@ use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
* API\Reports\Orders\DataStore.
*/
class DataStore extends ReportsDataStore implements DataStoreInterface {
use OrderAttributionMeta;
/**
* Dynamically sets the date column name based on configuration
@ -338,13 +342,14 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
* @param array $query_args Query parameters.
*/
protected function include_extended_info( &$orders_data, $query_args ) {
$mapped_orders = $this->map_array_by_key( $orders_data, 'order_id' );
$related_orders = $this->get_orders_with_parent_id( $mapped_orders );
$order_ids = array_merge( array_keys( $mapped_orders ), array_keys( $related_orders ) );
$products = $this->get_products_by_order_ids( $order_ids );
$coupons = $this->get_coupons_by_order_ids( array_keys( $mapped_orders ) );
$customers = $this->get_customers_by_orders( $orders_data );
$mapped_customers = $this->map_array_by_key( $customers, 'customer_id' );
$mapped_orders = $this->map_array_by_key( $orders_data, 'order_id' );
$related_orders = $this->get_orders_with_parent_id( $mapped_orders );
$order_ids = array_merge( array_keys( $mapped_orders ), array_keys( $related_orders ) );
$products = $this->get_products_by_order_ids( $order_ids );
$coupons = $this->get_coupons_by_order_ids( array_keys( $mapped_orders ) );
$order_attributions = $this->get_order_attributions_by_order_ids( array_keys( $mapped_orders ) );
$customers = $this->get_customers_by_orders( $orders_data );
$mapped_customers = $this->map_array_by_key( $customers, 'customer_id' );
$mapped_data = array();
foreach ( $products as $product ) {
@ -394,15 +399,22 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
}
foreach ( $orders_data as $key => $order_data ) {
$defaults = array(
'products' => array(),
'coupons' => array(),
'customer' => array(),
$defaults = array(
'products' => array(),
'coupons' => array(),
'customer' => array(),
'attribution' => array(),
);
$orders_data[ $key ]['extended_info'] = isset( $mapped_data[ $order_data['order_id'] ] ) ? array_merge( $defaults, $mapped_data[ $order_data['order_id'] ] ) : $defaults;
$order_id = $order_data['order_id'];
$orders_data[ $key ]['extended_info'] = isset( $mapped_data[ $order_id ] ) ? array_merge( $defaults, $mapped_data[ $order_id ] ) : $defaults;
if ( $order_data['customer_id'] && isset( $mapped_customers[ $order_data['customer_id'] ] ) ) {
$orders_data[ $key ]['extended_info']['customer'] = $mapped_customers[ $order_data['customer_id'] ];
}
$source_type = $order_attributions[ $order_id ]['_wc_order_attribution_source_type'] ?? '';
$utm_source = $order_attributions[ $order_id ]['_wc_order_attribution_utm_source'] ?? '';
$orders_data[ $key ]['extended_info']['attribution']['origin'] = $this->get_origin_label( $source_type, $utm_source );
}
}
@ -534,6 +546,52 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
return $coupons;
}
/**
* Get order attributions data from order IDs.
*
* @param array $order_ids Array of order IDs.
* @return array
*/
protected function get_order_attributions_by_order_ids( $order_ids ) {
global $wpdb;
$order_meta_table = OrdersTableDataStore::get_meta_table_name();
$included_order_ids = implode( ',', array_map( 'absint', $order_ids ) );
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
/* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */
$order_attributions_meta = $wpdb->get_results(
"SELECT order_id, meta_key, meta_value
FROM $order_meta_table
WHERE order_id IN ({$included_order_ids})
AND meta_key IN ( '_wc_order_attribution_source_type', '_wc_order_attribution_utm_source' )
",
ARRAY_A
);
/* phpcs:enable */
} else {
/* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */
$order_attributions_meta = $wpdb->get_results(
"SELECT post_id as order_id, meta_key, meta_value
FROM $wpdb->postmeta
WHERE post_id IN ({$included_order_ids})
AND meta_key IN ( '_wc_order_attribution_source_type', '_wc_order_attribution_utm_source' )
",
ARRAY_A
);
/* phpcs:enable */
}
$order_attributions = array();
foreach ( $order_attributions_meta as $meta ) {
if ( ! isset( $order_attributions[ $meta['order_id'] ] ) ) {
$order_attributions[ $meta['order_id'] ] = array();
}
$order_attributions[ $meta['order_id'] ][ $meta['meta_key'] ] = $meta['meta_value'];
}
return $order_attributions;
}
/**
* Get all statuses that have been synced.
*

View File

@ -96,7 +96,7 @@ class WC_Admin_Tests_Reports_Orders extends WC_Unit_Test_Case {
'date_created_gmt' => $data->data[0]['date_created_gmt'], // Not under test.
'date' => $data->data[0]['date'], // Not under test.
'extended_info' => array(
'products' => array(
'products' => array(
array(
'id' => $variation->get_id(),
'name' => $variation->get_name(),
@ -108,8 +108,11 @@ class WC_Admin_Tests_Reports_Orders extends WC_Unit_Test_Case {
'quantity' => 1,
),
),
'coupons' => array(),
'customer' => $data->data[0]['extended_info']['customer'], // Not under test.
'coupons' => array(),
'customer' => $data->data[0]['extended_info']['customer'], // Not under test.
'attribution' => array(
'origin' => 'Unknown',
),
),
),
),