Commit 802793aad80 for woocommerce
commit 802793aad80465bc4efb61667b919cab5bf0f6e7
Author: Peter Petrov <peter.petrov89@gmail.com>
Date: Fri Aug 14 16:47:59 2026 +0300
Match truncated order statuses in the Customer History metabox (#67700)
* Match truncated order statuses in the Customer History metabox excluded-statuses filter
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Assert average order value in long custom status tests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-67697-customer-history-excluded-long-statuses b/plugins/woocommerce/changelog/fix-67697-customer-history-excluded-long-statuses
new file mode 100644
index 00000000000..894f73fd16f
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-67697-customer-history-excluded-long-statuses
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Respect excluded order statuses in the Customer History metabox when a custom status slug exceeds the 20-character storage limit
diff --git a/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomerHistory.php b/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomerHistory.php
index a22a2651ee6..f8721a53d53 100644
--- a/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomerHistory.php
+++ b/plugins/woocommerce/src/Internal/Admin/Orders/MetaBoxes/CustomerHistory.php
@@ -283,7 +283,10 @@ class CustomerHistory {
$prefixed = array_map(
function ( $status ) {
$status = sanitize_title( $status );
- return 'auto-draft' === $status || 'trash' === $status ? $status : 'wc-' . $status;
+ $status = 'auto-draft' === $status || 'trash' === $status ? $status : 'wc-' . $status;
+ // Status columns are varchar(20) and longer values are silently truncated
+ // on write, so truncate the same way or comparisons never match long slugs.
+ return mb_substr( $status, 0, 20 );
},
$excluded_statuses
);
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/MetaBoxes/CustomerHistoryTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/MetaBoxes/CustomerHistoryTest.php
index cfc33c8b632..891a412e037 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/MetaBoxes/CustomerHistoryTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/MetaBoxes/CustomerHistoryTest.php
@@ -30,6 +30,13 @@ class CustomerHistoryTest extends WC_Unit_Test_Case {
*/
private bool $restore_hpos_after_test = false;
+ /**
+ * Callback registering the long custom status, kept for removal in tearDown.
+ *
+ * @var callable|null
+ */
+ private $add_long_status_callback = null;
+
/**
* Previous HPOS state.
*
@@ -87,6 +94,11 @@ class CustomerHistoryTest extends WC_Unit_Test_Case {
}
delete_option( 'woocommerce_excluded_report_order_statuses' );
+ if ( null !== $this->add_long_status_callback ) {
+ remove_filter( 'wc_order_statuses', $this->add_long_status_callback );
+ unset( $GLOBALS['wp_post_statuses']['wc-competition-completed'] );
+ $this->add_long_status_callback = null;
+ }
remove_filter( 'woocommerce_order_class', array( AdminOrder::class, 'order_class_name' ) );
remove_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );
parent::tearDown();
@@ -229,6 +241,71 @@ class CustomerHistoryTest extends WC_Unit_Test_Case {
$this->assertMatchesRegularExpression( '/order-attribution-total-orders">\s*0\s*</', $output, 'Should show 0 orders when all are excluded' );
}
+ /**
+ * @testdox Should not count orders in an excluded custom status whose slug exceeds the 20-char storage limit (HPOS).
+ */
+ public function test_excluded_long_custom_status_not_counted(): void {
+ list( $order_good, $long_status ) = $this->create_orders_with_long_custom_status();
+
+ update_option( 'woocommerce_excluded_report_order_statuses', array( 'pending', 'failed', 'cancelled', $long_status ) );
+
+ ob_start();
+ $this->sut->output( $order_good );
+ $output = ob_get_clean();
+
+ $this->assertMatchesRegularExpression( '/order-attribution-total-orders">\s*1\s*</', $output, 'Should only count the completed order' );
+ $this->assertMatchesRegularExpression( '/order-attribution-total-spend">\s*.*100\.00/', $output, 'Should only sum spend from the completed order' );
+ $this->assertMatchesRegularExpression( '/order-attribution-average-order-value">\s*.*100\.00/', $output, 'Should show average order value of 100' );
+ }
+
+ /**
+ * @testdox Should count orders in a non-excluded custom status whose slug exceeds the 20-char storage limit (HPOS).
+ */
+ public function test_non_excluded_long_custom_status_counted(): void {
+ list( $order_good ) = $this->create_orders_with_long_custom_status();
+
+ ob_start();
+ $this->sut->output( $order_good );
+ $output = ob_get_clean();
+
+ $this->assertMatchesRegularExpression( '/order-attribution-total-orders">\s*2\s*</', $output, 'Should count both orders' );
+ $this->assertMatchesRegularExpression( '/order-attribution-total-spend">\s*.*150\.00/', $output, 'Should sum spend from both orders' );
+ $this->assertMatchesRegularExpression( '/order-attribution-average-order-value">\s*.*75\.00/', $output, 'Should show average order value of 75' );
+ }
+
+ /**
+ * Creates a completed order and an order in a custom status whose slug is stored truncated.
+ *
+ * @return array{0: \WC_Order, 1: string} The completed order and the custom status slug.
+ */
+ private function create_orders_with_long_custom_status(): array {
+ // Core warns when saving a status longer than the 20-char column; that
+ // truncated storage is the exact scenario under test.
+ $this->setExpectedIncorrectUsage( 'Abstract_WC_Order_Data_Store_CPT::get_post_status' );
+
+ $long_status = 'competition-completed';
+ register_post_status( 'wc-' . $long_status, array( 'public' => true ) );
+ $this->add_long_status_callback = function ( $statuses ) use ( $long_status ) {
+ $statuses[ 'wc-' . $long_status ] = 'Competition Completed';
+ return $statuses;
+ };
+ add_filter( 'wc_order_statuses', $this->add_long_status_callback );
+
+ $customer_id = $this->factory->user->create();
+
+ $order_good = WC_Helper_Order::create_order( $customer_id );
+ $order_good->set_status( 'completed' );
+ $order_good->set_total( 100 );
+ $order_good->save();
+
+ $order_custom = WC_Helper_Order::create_order( $customer_id );
+ $order_custom->set_status( $long_status );
+ $order_custom->set_total( 50 );
+ $order_custom->save();
+
+ return array( $order_good, $long_status );
+ }
+
/**
* @testdox Should deduct partial refund from total spend (HPOS).
*/