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:
parent
2d837ac687
commit
48f2a4c5ad
|
@ -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,
|
||||
},
|
||||
];
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add origin column in Orders Analytics report.
|
|
@ -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'],
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue