[COT] Fix order related methods in customer data store (#34121)
This commit is contained in:
parent
5459f7e8ef
commit
e05697dfab
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Fix order related methods in customer data store
|
|
@ -5,6 +5,9 @@
|
|||
* @package WooCommerce\DataStores
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
@ -320,6 +323,15 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
|||
do_action( 'woocommerce_customer_object_updated_props', $customer, $updated_props );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the usage of the custom orders table is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_cot_in_use(): bool {
|
||||
return wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the customers last order.
|
||||
*
|
||||
|
@ -328,35 +340,59 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
|||
* @return WC_Order|false
|
||||
*/
|
||||
public function get_last_order( &$customer ) {
|
||||
$last_order = apply_filters(
|
||||
//phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
|
||||
/**
|
||||
* Filters the id of the last order from a given customer.
|
||||
*
|
||||
* @param string @last_order_id The last order id as retrieved from the database.
|
||||
* @param WC_Customer The customer whose last order id is being retrieved.
|
||||
* @return string The actual last order id to use.
|
||||
*/
|
||||
$last_order_id = apply_filters(
|
||||
'woocommerce_customer_get_last_order',
|
||||
get_user_meta( $customer->get_id(), '_last_order', true ),
|
||||
$customer
|
||||
);
|
||||
//phpcs:enable WooCommerce.Commenting.CommentHooks.MissingSinceComment
|
||||
|
||||
if ( '' === $last_order ) {
|
||||
if ( '' === $last_order_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$last_order = $wpdb->get_var(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$order_statuses_sql = "( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )";
|
||||
|
||||
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
if ( $this->is_cot_in_use() ) {
|
||||
$sql = $wpdb->prepare(
|
||||
'SELECT id FROM ' . OrdersTableDataStore::get_orders_table_name() . "
|
||||
WHERE customer_id = %d
|
||||
AND status in $order_statuses_sql
|
||||
ORDER BY id DESC
|
||||
LIMIT 1",
|
||||
$customer->get_id()
|
||||
);
|
||||
$last_order_id = $wpdb->get_var( $sql );
|
||||
} else {
|
||||
$last_order_id = $wpdb->get_var(
|
||||
"SELECT posts.ID
|
||||
FROM $wpdb->posts AS posts
|
||||
LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id
|
||||
WHERE meta.meta_key = '_customer_user'
|
||||
AND meta.meta_value = '" . esc_sql( $customer->get_id() ) . "'
|
||||
AND posts.post_type = 'shop_order'
|
||||
AND posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
|
||||
ORDER BY posts.ID DESC"
|
||||
// phpcs:enable
|
||||
AND posts.post_status IN $order_statuses_sql
|
||||
ORDER BY posts.ID DESC
|
||||
LIMIT 1"
|
||||
);
|
||||
update_user_meta( $customer->get_id(), '_last_order', $last_order );
|
||||
}
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
update_user_meta( $customer->get_id(), '_last_order', $last_order_id );
|
||||
}
|
||||
|
||||
if ( ! $last_order ) {
|
||||
if ( ! $last_order_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return wc_get_order( absint( $last_order ) );
|
||||
return wc_get_order( absint( $last_order_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,20 +409,33 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
|||
$customer
|
||||
);
|
||||
|
||||
$order_statuses_sql = "( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )";
|
||||
|
||||
if ( '' === $count ) {
|
||||
global $wpdb;
|
||||
|
||||
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
if ( $this->is_cot_in_use() ) {
|
||||
$sql = $wpdb->prepare(
|
||||
'SELECT COUNT(id) FROM ' . OrdersTableDataStore::get_orders_table_name() . "
|
||||
WHERE customer_id = %d
|
||||
AND status in $order_statuses_sql",
|
||||
$customer->get_id()
|
||||
);
|
||||
$count = $wpdb->get_var( $sql );
|
||||
} else {
|
||||
$count = $wpdb->get_var(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
"SELECT COUNT(*)
|
||||
FROM $wpdb->posts as posts
|
||||
LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
|
||||
WHERE meta.meta_key = '_customer_user'
|
||||
AND posts.post_type = 'shop_order'
|
||||
AND posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
|
||||
AND posts.post_status IN $order_statuses_sql
|
||||
AND meta_value = '" . esc_sql( $customer->get_id() ) . "'"
|
||||
// phpcs:enable
|
||||
);
|
||||
}
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
|
||||
update_user_meta( $customer->get_id(), '_order_count', $count );
|
||||
}
|
||||
|
||||
|
@ -411,23 +460,41 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
|||
global $wpdb;
|
||||
|
||||
$statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
|
||||
$spent = $wpdb->get_var(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
apply_filters(
|
||||
'woocommerce_customer_get_total_spent_query',
|
||||
"SELECT SUM(meta2.meta_value)
|
||||
$statuses_sql = "( 'wc-" . implode( "','wc-", $statuses ) . "' )";
|
||||
|
||||
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
if ( $this->is_cot_in_use() ) {
|
||||
$sql = $wpdb->prepare(
|
||||
'SELECT SUM(total_amount) FROM ' . OrdersTableDataStore::get_orders_table_name() . "
|
||||
WHERE customer_id = %d
|
||||
AND status in $statuses_sql",
|
||||
$customer->get_id()
|
||||
);
|
||||
} else {
|
||||
$sql = "SELECT SUM(meta2.meta_value)
|
||||
FROM $wpdb->posts as posts
|
||||
LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
|
||||
LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id
|
||||
WHERE meta.meta_key = '_customer_user'
|
||||
AND meta.meta_value = '" . esc_sql( $customer->get_id() ) . "'
|
||||
AND posts.post_type = 'shop_order'
|
||||
AND posts.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
|
||||
AND meta2.meta_key = '_order_total'",
|
||||
$customer
|
||||
)
|
||||
// phpcs:enable
|
||||
);
|
||||
AND posts.post_status IN $statuses_sql
|
||||
AND meta2.meta_key = '_order_total'";
|
||||
}
|
||||
|
||||
//phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
|
||||
/**
|
||||
* Filters the SQL query used to get the combined total of all the orders from a given customer.
|
||||
*
|
||||
* @param string The SQL query to use.
|
||||
* @param WC_Customer The customer to get the total spent for.
|
||||
* @return string The actual SQL query to use.
|
||||
*/
|
||||
$sql = apply_filters( 'woocommerce_customer_get_total_spent_query', $sql, $customer );
|
||||
//phpcs:enable WooCommerce.Commenting.CommentHooks.MissingSinceComment
|
||||
|
||||
$spent = $wpdb->get_var( $sql );
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
|
||||
if ( ! $spent ) {
|
||||
$spent = 0;
|
||||
|
|
|
@ -40,10 +40,11 @@ class WC_Helper_Order {
|
|||
*
|
||||
* @param int $customer_id The ID of the customer the order is for.
|
||||
* @param WC_Product $product The product to add to the order.
|
||||
* @param array $order_data Order data to be passed to wc_create_order.
|
||||
*
|
||||
* @return WC_Order
|
||||
*/
|
||||
public static function create_order( $customer_id = 1, $product = null ) {
|
||||
public static function create_order( $customer_id = 1, $product = null, $order_data = array() ) {
|
||||
|
||||
if ( ! is_a( $product, 'WC_Product' ) ) {
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
|
@ -51,12 +52,13 @@ class WC_Helper_Order {
|
|||
|
||||
WC_Helper_Shipping::create_simple_flat_rate();
|
||||
|
||||
$order_data = array(
|
||||
$default_order_data = array(
|
||||
'status' => 'pending',
|
||||
'customer_id' => $customer_id,
|
||||
'customer_note' => '',
|
||||
'total' => '',
|
||||
);
|
||||
$order_data = wp_parse_args( $order_data, $default_order_data );
|
||||
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // Required, else wc_create_order throws an exception.
|
||||
$order = wc_create_order( $order_data );
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
<?php
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper;
|
||||
|
||||
/**
|
||||
* Class WC_Customer_Data_Store_CPT_Test.
|
||||
*/
|
||||
class WC_Customer_Data_Store_CPT_Test extends WC_Unit_Test_Case {
|
||||
|
||||
/**
|
||||
* Runs before each test.
|
||||
*/
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
OrderHelper::create_order_custom_table_if_not_exist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that metadata cannot overwrite customer's column data.
|
||||
*
|
||||
|
@ -23,4 +35,176 @@ class WC_Customer_Data_Store_CPT_Test extends WC_Unit_Test_Case {
|
|||
$this->assertEquals( $customer_id, $customer->get_id() );
|
||||
$this->assertEquals( $username, $customer->get_username() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the wc_order_statuses filter, returns just 'pending" as the valid order statuses list.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_pending_only_as_order_statuses() {
|
||||
return array( 'wc-pending' => 'pending' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox 'get_last_order' works when the posts table is used for storing orders.
|
||||
*/
|
||||
public function test_get_last_customer_order_not_using_cot() {
|
||||
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'no' );
|
||||
|
||||
$customer_1 = WC_Helper_Customer::create_customer( 'test1', 'pass1', 'test1@example.com' );
|
||||
$customer_2 = WC_Helper_Customer::create_customer( 'test2', 'pass2', 'test2@example.com' );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id() );
|
||||
$last_valid_order_of_1 = WC_Helper_Order::create_order( $customer_1->get_id() );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id(), null, array( 'status' => 'completed' ) );
|
||||
WC_Helper_Order::create_order( $customer_2->get_id() );
|
||||
WC_Helper_Order::create_order( $customer_2->get_id() );
|
||||
|
||||
$sut = new WC_Customer_Data_Store();
|
||||
add_filter( 'wc_order_statuses', array( $this, 'get_pending_only_as_order_statuses' ), 10, 0 );
|
||||
$actual_order = $sut->get_last_order( $customer_1 );
|
||||
remove_filter( 'wc_order_statuses', array( $this, 'get_pending_only_as_order_statuses' ), 10 );
|
||||
|
||||
$this->assertEquals( $last_valid_order_of_1->get_id(), $actual_order->get_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox 'get_last_order' works when the custom orders table is used for storing orders.
|
||||
*/
|
||||
public function test_get_last_customer_order_using_cot() {
|
||||
global $wpdb;
|
||||
|
||||
$customer_1 = WC_Helper_Customer::create_customer( 'test1', 'pass1', 'test1@example.com' );
|
||||
$customer_2 = WC_Helper_Customer::create_customer( 'test2', 'pass2', 'test2@example.com' );
|
||||
$last_valid_order = WC_Helper_Order::create_order( $customer_1->get_id() );
|
||||
|
||||
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'yes' );
|
||||
|
||||
$sql =
|
||||
'INSERT INTO ' . OrdersTableDataStore::get_orders_table_name() . "
|
||||
( id, customer_id, status )
|
||||
VALUES
|
||||
( 1, %d, 'wc-completed' ), ( %d, %d, 'wc-completed' ), ( 3, %d, 'wc-invalid-status' ),
|
||||
( 4, %d, 'wc-completed' ), ( 5, %d, 'wc-completed' )";
|
||||
|
||||
$customer_1_id = $customer_1->get_id();
|
||||
$customer_2_id = $customer_2->get_id();
|
||||
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$query = $wpdb->prepare( $sql, $customer_1_id, $last_valid_order->get_id(), $customer_1_id, $customer_1_id, $customer_2_id, $customer_2_id );
|
||||
$wpdb->query( $query );
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
$sut = new WC_Customer_Data_Store();
|
||||
$actual_order = $sut->get_last_order( $customer_1 );
|
||||
|
||||
$this->assertEquals( $last_valid_order->get_id(), $actual_order->get_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox 'get_order_count' works when the posts table is used for storing orders.
|
||||
*/
|
||||
public function test_order_count_not_using_cot() {
|
||||
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'no' );
|
||||
|
||||
$customer_1 = WC_Helper_Customer::create_customer( 'test1', 'pass1', 'test1@example.com' );
|
||||
$customer_2 = WC_Helper_Customer::create_customer( 'test2', 'pass2', 'test2@example.com' );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id() );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id() );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id() );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id(), null, array( 'status' => 'completed' ) );
|
||||
WC_Helper_Order::create_order( $customer_2->get_id() );
|
||||
WC_Helper_Order::create_order( $customer_2->get_id() );
|
||||
|
||||
$sut = new WC_Customer_Data_Store();
|
||||
add_filter( 'wc_order_statuses', array( $this, 'get_pending_only_as_order_statuses' ), 10, 0 );
|
||||
$actual_count = $sut->get_order_count( $customer_1 );
|
||||
remove_filter( 'wc_order_statuses', array( $this, 'get_pending_only_as_order_statuses' ), 10 );
|
||||
|
||||
$this->assertEquals( 3, $actual_count );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox 'get_order_count' works when the custom orders table is used for storing orders.
|
||||
*/
|
||||
public function test_get_order_count_using_cot() {
|
||||
global $wpdb;
|
||||
|
||||
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'yes' );
|
||||
|
||||
$customer_1 = WC_Helper_Customer::create_customer( 'test1', 'pass1', 'test1@example.com' );
|
||||
$customer_2 = WC_Helper_Customer::create_customer( 'test2', 'pass2', 'test2@example.com' );
|
||||
|
||||
$sql =
|
||||
'INSERT INTO ' . OrdersTableDataStore::get_orders_table_name() . "
|
||||
( id, customer_id, status )
|
||||
VALUES
|
||||
( 1, %d, 'wc-completed' ), ( 2, %d, 'wc-completed' ), ( 3, %d, 'wc-completed' ), ( 4, %d, 'wc-invalid-status' ),
|
||||
( 5, %d, 'wc-completed' ), ( 6, %d, 'wc-completed' )";
|
||||
|
||||
$customer_1_id = $customer_1->get_id();
|
||||
$customer_2_id = $customer_2->get_id();
|
||||
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$query = $wpdb->prepare( $sql, $customer_1_id, $customer_1_id, $customer_1_id, $customer_1_id, $customer_2_id, $customer_2_id );
|
||||
$wpdb->query( $query );
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
$sut = new WC_Customer_Data_Store();
|
||||
$actual_count = $sut->get_order_count( $customer_1 );
|
||||
|
||||
$this->assertEquals( 3, $actual_count );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox 'get_total_spent' works when the posts table is used for storing orders.
|
||||
*/
|
||||
public function test_get_total_spent_not_using_cot() {
|
||||
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'no' );
|
||||
|
||||
$customer_1 = WC_Helper_Customer::create_customer( 'test1', 'pass1', 'test1@example.com' );
|
||||
$customer_2 = WC_Helper_Customer::create_customer( 'test2', 'pass2', 'test2@example.com' );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id(), null, array( 'status' => 'completed' ) );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id(), null, array( 'status' => 'completed' ) );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id(), null, array( 'status' => 'completed' ) );
|
||||
WC_Helper_Order::create_order( $customer_1->get_id(), null, array( 'status' => 'pending' ) );
|
||||
WC_Helper_Order::create_order( $customer_2->get_id() );
|
||||
WC_Helper_Order::create_order( $customer_2->get_id() );
|
||||
|
||||
$sut = new WC_Customer_Data_Store();
|
||||
add_filter( 'wc_order_statuses', array( $this, 'get_pending_only_as_order_statuses' ), 10, 0 );
|
||||
$actual_amount = $sut->get_total_spent( $customer_1 );
|
||||
remove_filter( 'wc_order_statuses', array( $this, 'get_pending_only_as_order_statuses' ), 10 );
|
||||
|
||||
// Each order created by WC_Helper_Order::create_order has a total amount of 50.
|
||||
$this->assertEquals( '150.00', $actual_amount );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox 'get_total_spent' works when the custom orders table is used for storing orders.
|
||||
*/
|
||||
public function test_get_total_spent_using_cot() {
|
||||
global $wpdb;
|
||||
|
||||
update_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION, 'yes' );
|
||||
|
||||
$customer_1 = WC_Helper_Customer::create_customer( 'test1', 'pass1', 'test1@example.com' );
|
||||
$customer_2 = WC_Helper_Customer::create_customer( 'test2', 'pass2', 'test2@example.com' );
|
||||
|
||||
$sql =
|
||||
'INSERT INTO ' . OrdersTableDataStore::get_orders_table_name() . "
|
||||
( id, customer_id, status, total_amount )
|
||||
VALUES
|
||||
( 1, %d, 'wc-completed', 10 ), ( 2, %d, 'wc-completed', 20 ), ( 3, %d, 'wc-completed', 30 ), ( 4, %d, 'wc-invalid-status', 40 ),
|
||||
( 5, %d, 'wc-completed', 200 ), ( 6, %d, 'wc-completed', 300 )";
|
||||
|
||||
$customer_1_id = $customer_1->get_id();
|
||||
$customer_2_id = $customer_2->get_id();
|
||||
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$query = $wpdb->prepare( $sql, $customer_1_id, $customer_1_id, $customer_1_id, $customer_1_id, $customer_2_id, $customer_2_id );
|
||||
$wpdb->query( $query );
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
$sut = new WC_Customer_Data_Store();
|
||||
$actual_spent = $sut->get_total_spent( $customer_1 );
|
||||
|
||||
$this->assertEquals( '60.00', $actual_spent );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue