Remove customer analytics data upon order deletion (https://github.com/woocommerce/woocommerce-admin/pull/5171)

This deletes the associated customer record when an order is deleted, provided that this is the only order for the customer.
This commit is contained in:
Adrian Duffell 2020-10-01 20:09:04 +08:00 committed by GitHub
parent 4d0e062ee1
commit f08ccc57d7
3 changed files with 179 additions and 1 deletions

View File

@ -85,6 +85,7 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
public static function init() {
add_action( 'edit_user_profile_update', array( __CLASS__, 'update_registered_customer' ) );
add_action( 'updated_user_meta', array( __CLASS__, 'update_registered_customer_via_last_active' ), 10, 3 );
add_action( 'woocommerce_analytics_delete_order_stats', array( __CLASS__, 'sync_on_order_delete' ), 15, 2 );
}
/**
@ -101,6 +102,30 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
}
}
/**
* Sync customers data after an order was deleted.
*
* When an order is deleted, the customer record is deleted from the
* table if the customer has no other orders.
*
* @param int $order_id Order ID.
* @param int $customer_id Customer ID.
*/
public static function sync_on_order_delete( $order_id, $customer_id ) {
$customer_id = absint( $customer_id );
if ( 0 === $customer_id ) {
return;
}
// Calculate the amount of orders remaining for this customer.
$order_count = self::get_order_count( $customer_id );
if ( 0 === $order_count ) {
self::delete_customer( $customer_id );
}
}
/**
* Maps ordering specified by the user to columns in the database/fields in the data.
*
@ -572,6 +597,34 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
);
}
/**
* Retrieve the amount of orders made by a customer.
*
* @param int $customer_id Customer ID.
* @return int|null Amount of orders for customer or null on failure.
*/
public static function get_order_count( $customer_id ) {
global $wpdb;
$customer_id = absint( $customer_id );
if ( 0 === $customer_id ) {
return null;
}
$result = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT( order_id ) FROM {$wpdb->prefix}wc_order_stats WHERE customer_id = %d",
$customer_id
)
);
if ( is_null( $result ) ) {
return null;
}
return (int) $result;
}
/**
* Update the database with customer data.
*

View File

@ -12,6 +12,7 @@ use \Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface;
use \Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
use \Automattic\WooCommerce\Admin\API\Reports\SqlQuery;
use \Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache;
use \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore;
/**
* API\Reports\Orders\Stats\DataStore.
@ -576,13 +577,19 @@ class DataStore extends ReportsDataStore implements DataStoreInterface {
return;
}
// Retrieve customer details before the order is deleted.
$order = wc_get_order( $order_id );
$customer_id = absint( CustomersDataStore::get_existing_customer_id_from_order( $order ) );
// Delete the order.
$wpdb->delete( self::get_db_table_name(), array( 'order_id' => $order_id ) );
/**
* Fires when orders stats are deleted.
*
* @param int $order_id Order ID.
* @param int $customer_id Customer ID.
*/
do_action( 'woocommerce_analytics_delete_order_stats', $order_id );
do_action( 'woocommerce_analytics_delete_order_stats', $order_id, $customer_id );
ReportsCache::invalidate();
}

View File

@ -0,0 +1,118 @@
<?php
/**
* Reports customers tests.
*
* @package WooCommerce\Admin\Tests\Customers
*/
use \Automattic\WooCommerce\Admin\API\Reports\Customers\Stats\DataStore;
/**
* Class WC_Tests_Reports_Customers
*/
class WC_Tests_Reports_Customer extends WC_Unit_Test_Case {
/**
* Test order count caluclation for customer.
*
* @covers \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore::get_order_count
*/
public function test_customer_order_count() {
WC_Helper_Reports::reset_stats_dbs();
// Create a customer.
$customer = WC_Helper_Customer::create_customer();
// Create product.
$product = new WC_Product_Simple();
$product->set_name( 'Test Product' );
$product->set_regular_price( 25 );
$product->save();
WC_Helper_Queue::run_all_pending();
$customer_id = DataStore::get_customer_id_by_user_id( $customer->get_id() ); // This is the customer ID from lookup table.
// Create 3 orders.
foreach ( range( 1, 3 ) as $i ) {
$order = WC_Helper_Order::create_order( $customer->get_id(), $product );
$order->save();
}
WC_Helper_Queue::run_all_pending();
// Customer should have 3 orders.
$this->assertSame( 3, DataStore::get_order_count( $customer_id ) );
// Failure from bad customer IDs.
$this->assertSame( null, DataStore::get_order_count( 0 ) );
$this->assertSame( null, DataStore::get_order_count( 'ABC' ) );
$this->assertSame( null, DataStore::get_order_count( false ) );
$this->assertSame( null, DataStore::get_order_count( null ) );
}
/**
* Test customer lookup tables are cleaned after deleting an order.
*
* A customer record should only be deleted if the customer has no other orders.
*
* @covers \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore::sync_on_order_delete
*/
public function test_order_deletion_removes_customer() {
WC_Helper_Reports::reset_stats_dbs();
// Create a customer.
$customer = WC_Helper_Customer::create_customer();
// Create products.
$product1 = new WC_Product_Simple();
$product1->set_name( 'Test Product 1' );
$product1->set_regular_price( 1 );
$product1->save();
$product2 = new WC_Product_Simple();
$product2->set_name( 'Test Product 2' );
$product2->set_regular_price( 2 );
$product2->save();
WC_Helper_Queue::run_all_pending();
// Create the first order.
$order1 = WC_Helper_Order::create_order( $customer->get_id(), $product1 );
$order1->save();
// Create the second order.
$order2 = WC_Helper_Order::create_order( $customer->get_id(), $product2 );
$order2->save();
WC_Helper_Queue::run_all_pending();
$customer_id = DataStore::get_customer_id_by_user_id( $customer->get_id() ); // This is the customer ID from lookup table.
// Customer should remain in lookup table after first order deleted.
$order1->delete( true );
$this->assertCount( 1, $this->get_customer_record( $customer_id ), 'customer remains' );
// Customer should be removed in lookup table after both orders are deleted.
$order2->delete( true );
$this->assertCount( 0, $this->get_customer_record( $customer_id ), 'customer removed' );
}
/**
* Get a customer's record from the database.
*
* @param int $customer_id Analytics Customer ID (not WP User ID).
*/
private function get_customer_record( $customer_id ) {
global $wpdb;
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wc_customer_lookup WHERE customer_id = %d",
$customer_id
)
);
return $results;
}
}