Skip to content

Commit

Permalink
Add simple products to the Analytics orders query for the attributes …
Browse files Browse the repository at this point in the history
…filter (#44901)

* Add subquery for simple products

* Update query to make use of product lookup instead

* Add changelog

* Revert change

* Update PHP tests and fix lint errors

* Move subquery out to its own variable to make it more readable
  • Loading branch information
louwie17 authored and Konamiman committed Mar 13, 2024
1 parent 0f2ca3d commit f1c2e31
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: fix

Include simple product support in the attributes filter within the analytics orders view.
24 changes: 22 additions & 2 deletions plugins/woocommerce/src/Admin/API/Reports/DataStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -1345,6 +1345,7 @@ protected function get_attribute_subqueries( $query_args ) {
continue;
}

$term_id = '';
// If the tuple is numeric, assume these are IDs.
if ( is_numeric( $attribute_term[0] ) && is_numeric( $attribute_term[1] ) ) {
$attribute_id = intval( $attribute_term[0] );
Expand Down Expand Up @@ -1374,6 +1375,10 @@ protected function get_attribute_subqueries( $query_args ) {
// Assume these are a custom attribute slug/value pair.
$meta_key = esc_sql( $attribute_term[0] );
$meta_value = esc_sql( $attribute_term[1] );
$attr_term = get_term_by( 'slug', $meta_value, $meta_key );
if ( false !== $attr_term ) {
$term_id = $attr_term->term_id;
}
}

$join_alias = 'orderitemmeta1';
Expand All @@ -1391,8 +1396,23 @@ protected function get_attribute_subqueries( $query_args ) {
$sql_clauses['join'][] = "JOIN {$wpdb->prefix}woocommerce_order_itemmeta as {$join_alias} ON {$join_alias}.order_item_id = {$table_to_join_on}.order_item_id";
}

// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$sql_clauses['where'][] = $wpdb->prepare( "( {$join_alias}.meta_key = %s AND {$join_alias}.meta_value {$comparator} %s )", $meta_key, $meta_value );
$in_comparator = '=' === $comparator ? 'in' : 'not in';

// Add subquery for products ordered using attributes not used in variations.
$term_attribute_subquery = "select product_id from {$wpdb->prefix}wc_product_attributes_lookup where is_variation_attribute=0 and term_id = %s";
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
$sql_clauses['where'][] = $wpdb->prepare(
"
( ( {$join_alias}.meta_key = %s AND {$join_alias}.meta_value {$comparator} %s ) or (
{$wpdb->prefix}wc_order_product_lookup.variation_id = 0 and {$wpdb->prefix}wc_order_product_lookup.product_id {$in_comparator} ({$term_attribute_subquery})
) )",
$meta_key,
$meta_value,
$term_id,
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:enable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ public function test_product_attributes_filter() {
$order_variation_1 = wc_get_product( $product_variations[1] ); // Variation: size = large.
$order_variation_2 = wc_get_product( $product_variations[3] ); // Variation: size = huge, colour = red, number = 2.

// Create a simple product.
$size_attr_id = wc_attribute_taxonomy_id_by_name( 'pa_size' );
$large_term = get_term_by( 'slug', 'large', 'pa_size' );

$global_attribute = new WC_Product_Attribute();
$global_attribute->set_id( $size_attr_id );
$global_attribute->set_name( 'pa_size' );
$global_attribute->set_options( array( $large_term->term_id ) ); // Set to small.
$global_attribute->set_position( 1 );
$global_attribute->set_visible( true );
$global_attribute->set_variation( false );
$attributes['global-size'] = $global_attribute;

$simple_product = WC_Helper_Product::create_simple_product(
true,
array(
'attributes' => $attributes,
)
);

// Create orders for variations.
$variation_order_1 = WC_Helper_Order::create_order( $this->user, $order_variation_1 );
$variation_order_1->set_status( 'completed' );
Expand All @@ -156,6 +176,10 @@ public function test_product_attributes_filter() {
$variation_order_2->set_status( 'completed' );
$variation_order_2->save();

$simple_product_order_1 = WC_Helper_Order::create_order( $this->user, $simple_product );
$simple_product_order_1->set_status( 'completed' );
$simple_product_order_1->save();

// Create more orders for simple products.
for ( $i = 0; $i < 10; $i++ ) {
$order = WC_Helper_Order::create_order( $this->user );
Expand All @@ -172,25 +196,23 @@ public function test_product_attributes_filter() {

// Sanity check before filtering by attribute.
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 12, $response_orders['totals']['orders_count'] );
$this->assertEquals( 13, $response_orders['totals']['orders_count'] );

// Filter by the "size" attribute, with value "large".
$size_attr_id = wc_attribute_taxonomy_id_by_name( 'pa_size' );
$small_term = get_term_by( 'slug', 'large', 'pa_size' );
$request = new WP_REST_Request( 'GET', $this->endpoint );
$request = new WP_REST_Request( 'GET', $this->endpoint );
$request->set_query_params(
array(
'attribute_is' => array(
array( $size_attr_id, $small_term->term_id ),
array( $size_attr_id, $large_term->term_id ),
),
)
);
$response = $this->server->dispatch( $request );
$response_orders = $response->get_data();

$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 1, $response_orders['totals']['orders_count'] );
$this->assertEquals( $variation_order_1->get_total(), $response_orders['totals']['total_sales'] );
$this->assertEquals( 2, $response_orders['totals']['orders_count'] );
$this->assertEquals( $variation_order_1->get_total() + $simple_product_order_1->get_total(), $response_orders['totals']['total_sales'] );

// Filter by excluding the "size" attribute, with value "large".
$size_attr_id = wc_attribute_taxonomy_id_by_name( 'pa_size' );
Expand All @@ -207,8 +229,8 @@ public function test_product_attributes_filter() {
$response_orders = $response->get_data();

$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 1, $response_orders['totals']['orders_count'] );
$this->assertEquals( 11, $response_orders['totals']['orders_count'] );
// This should be the second variation order.
$this->assertEquals( $variation_order_2->get_total(), $response_orders['totals']['total_sales'] );
$this->assertEquals( 11 * $variation_order_2->get_total(), $response_orders['totals']['total_sales'] );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,26 @@ public function test_product_attributes_filter() {
$order_variation_1 = wc_get_product( $product_variations[0] ); // Variation: size = small.
$order_variation_2 = wc_get_product( $product_variations[2] ); // Variation: size = huge, colour = red, number = 0.

// Create a simple product.
$size_attr_id = wc_attribute_taxonomy_id_by_name( 'pa_size' );
$small_term = get_term_by( 'slug', 'small', 'pa_size' );

$global_attribute = new WC_Product_Attribute();
$global_attribute->set_id( $size_attr_id );
$global_attribute->set_name( 'pa_size' );
$global_attribute->set_options( array( $small_term->term_id ) ); // Set to small.
$global_attribute->set_position( 1 );
$global_attribute->set_visible( true );
$global_attribute->set_variation( false );
$attributes['global-size'] = $global_attribute;

$simple_product = WC_Helper_Product::create_simple_product(
true,
array(
'attributes' => $attributes,
)
);

// Create orders for variations.
$variation_order_1 = WC_Helper_Order::create_order( $this->user, $order_variation_1 );
$variation_order_1->set_status( 'completed' );
Expand All @@ -158,6 +178,10 @@ public function test_product_attributes_filter() {
$variation_order_2->set_status( 'completed' );
$variation_order_2->save();

$simple_product_order_1 = WC_Helper_Order::create_order( $this->user, $simple_product );
$simple_product_order_1->set_status( 'completed' );
$simple_product_order_1->save();

// Create more orders for simple products.
for ( $i = 0; $i < 10; $i++ ) {
$order = WC_Helper_Order::create_order( $this->user );
Expand All @@ -174,11 +198,10 @@ public function test_product_attributes_filter() {

// Sanity check before filtering by attribute.
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 12, count( $response_orders ) );
$this->assertEquals( 13, count( $response_orders ) );

// To filter by later.
$size_attr_id = wc_attribute_taxonomy_id_by_name( 'pa_size' );
$small_term = get_term_by( 'slug', 'small', 'pa_size' );

// Test bad values to filter parameter.
$bad_args = array(
Expand All @@ -201,7 +224,7 @@ public function test_product_attributes_filter() {

$this->assertEquals( 200, $response->get_status() );
// We expect all results since the attribute param is malformed.
$this->assertEquals( 12, count( $response_orders ) );
$this->assertEquals( 13, count( $response_orders ) );
}

// Filter by the "size" attribute, with value "small".
Expand All @@ -217,8 +240,15 @@ public function test_product_attributes_filter() {
$response_orders = $response->get_data();

$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 1, count( $response_orders ) );
$this->assertEquals( $response_orders[0]['order_id'], $variation_order_1->get_id() );
$this->assertEquals( 2, count( $response_orders ) );
$order_ids = array_map(
function( $order ) {
return $order['order_id'];
},
$response_orders
);
$this->assertContains( $simple_product_order_1->get_id(), $order_ids );
$this->assertContains( $variation_order_1->get_id(), $order_ids );

// Verify the opposite result set.
$request = new WP_REST_Request( 'GET', $this->endpoint );
Expand All @@ -234,8 +264,7 @@ public function test_product_attributes_filter() {
$response_orders = $response->get_data();

$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 1, count( $response_orders ) );
$this->assertEquals( $response_orders[0]['order_id'], $variation_order_2->get_id() );
$this->assertEquals( 11, count( $response_orders ) );
}

/**
Expand Down Expand Up @@ -427,7 +456,7 @@ public function test_order_price_formatting_with_different_base_currency() {

// Create another simple order with another currency.
$currencies = get_woocommerce_currencies();
// prevent base currency to be selected again
// prevent base currency to be selected again.
unset( $currencies[ get_woocommerce_currency() ] );
$second_currency = array_rand( $currencies );

Expand All @@ -439,7 +468,7 @@ public function test_order_price_formatting_with_different_base_currency() {

WC_Helper_Queue::run_all_pending();

// Get the created orders from REST API
// Get the created orders from REST API.
$request = new WP_REST_Request( 'GET', $this->endpoint );
$request->set_query_params(
array(
Expand Down

0 comments on commit f1c2e31

Please sign in to comment.