Commit a4d8c4f145 for woocommerce

commit a4d8c4f145024ab58182f04395d6b29ee3655a74
Author: Brian <brian@brianhaas.li>
Date:   Tue Jan 27 18:52:36 2026 +0100

    Include GTIN, UPC, EAN or ISBN on the admin text search (#60052)

    * add GTIN to product admin search

    * add gtin search on variations as well

    ---------

    Co-authored-by: Brandon Kraft <public@brandonkraft.com>

diff --git a/plugins/woocommerce/changelog/pr-60052 b/plugins/woocommerce/changelog/pr-60052
new file mode 100644
index 00000000000..36a9cf1e940
--- /dev/null
+++ b/plugins/woocommerce/changelog/pr-60052
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Add global identifier (GTIN/UPC/EAN/ISBN) support to admin product search.
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
index 19e379759dd..6098c7b98e7 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
@@ -1957,13 +1957,14 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da

 				// phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- an array of placeholders is a valid arg.
 				$term_query = $wpdb->prepare(
-					'( posts.post_title LIKE %s ) OR ( posts.post_excerpt LIKE %s ) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s )',
-					array_fill( 0, 4, $like )
+					'( posts.post_title LIKE %s ) OR ( posts.post_excerpt LIKE %s ) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s ) OR ( wc_product_meta_lookup.global_unique_id LIKE %s )',
+					array_fill( 0, 5, $like )
 				);

 				// Variations should also search the parent's meta table for fallback fields.
 				if ( $include_variations ) {
 					$term_query .= $wpdb->prepare( " OR ( wc_product_meta_lookup.sku = '' AND parent_wc_product_meta_lookup.sku LIKE %s )", $like );
+					$term_query .= $wpdb->prepare( " OR ( wc_product_meta_lookup.global_unique_id = '' AND parent_wc_product_meta_lookup.global_unique_id LIKE %s )", $like );
 				}

 				$term_group_query[] = "( {$term_query} )";
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php b/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
index 4f8d9798b1b..722fe5ebf41 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
@@ -900,6 +900,116 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
 		$this->assertContains( $product4->get_id(), $results );
 	}

+	/**
+	 * @testdox Test searching products by global_unique_id (EAN/barcode).
+	 *
+	 * @return void
+	 */
+	public function test_search_products_by_global_unique_id() {
+		$product = new WC_Product();
+		$product->set_regular_price( 42 );
+		$product->set_name( 'Product A' );
+		$product->set_sku( 'PROD-A' );
+		$product->set_global_unique_id( '4521121209036' );
+		$product->save();
+
+		$product2 = new WC_Product();
+		$product2->set_regular_price( 42 );
+		$product2->set_name( 'Product B' );
+		$product2->set_sku( 'PROD-B' );
+		$product2->set_global_unique_id( '1234567890123' );
+		$product2->save();
+
+		$product3 = new WC_Product();
+		$product3->set_regular_price( 42 );
+		$product3->set_name( 'Product C' );
+		$product3->set_sku( 'PROD-C' );
+		$product3->set_global_unique_id( '9876543210987' );
+		$product3->save();
+
+		$data_store = WC_Data_Store::load( 'product' );
+
+		// Full barcode search.
+		$results = $data_store->search_products( '4521121209036', '', true, true );
+		$this->assertContains( $product->get_id(), $results );
+		$this->assertNotContains( $product2->get_id(), $results );
+		$this->assertNotContains( $product3->get_id(), $results );
+
+		// Partial barcode search.
+		$results = $data_store->search_products( '123456', '', true, true );
+		$this->assertNotContains( $product->get_id(), $results );
+		$this->assertContains( $product2->get_id(), $results );
+		$this->assertNotContains( $product3->get_id(), $results );
+
+		// Another barcode search.
+		$results = $data_store->search_products( '9876543210987', '', true, true );
+		$this->assertNotContains( $product->get_id(), $results );
+		$this->assertNotContains( $product2->get_id(), $results );
+		$this->assertContains( $product3->get_id(), $results );
+	}
+
+	/**
+	 * @testdox Test searching variations by global_unique_id.
+	 *
+	 * @return void
+	 */
+	public function test_search_products_by_global_unique_id_with_variations() {
+		$variable_product = new WC_Product_Variable();
+		$variable_product->set_name( 'Variable Product' );
+		$variable_product->set_global_unique_id( '1111111111111' );
+		$variable_product->save();
+
+		// Variation with its own barcode.
+		$variation1 = new WC_Product_Variation();
+		$variation1->set_parent_id( $variable_product->get_id() );
+		$variation1->set_global_unique_id( '2222222222222' );
+		$variation1->save();
+
+		// Variation without barcode - inherits from parent.
+		$variation2 = new WC_Product_Variation();
+		$variation2->set_parent_id( $variable_product->get_id() );
+		$variation2->set_global_unique_id( '' );
+		$variation2->save();
+
+		$data_store = WC_Data_Store::load( 'product' );
+
+		// Search parent barcode - finds parent and variation that inherits.
+		$results = $data_store->search_products( '1111111111111', '', true, true );
+		$this->assertContains( $variable_product->get_id(), $results );
+		$this->assertContains( $variation2->get_id(), $results );
+
+		// Search variation barcode - finds variation and parent (WC includes parents when variations match).
+		$results = $data_store->search_products( '2222222222222', '', true, true );
+		$this->assertContains( $variation1->get_id(), $results );
+		$this->assertContains( $variable_product->get_id(), $results );
+		$this->assertNotContains( $variation2->get_id(), $results );
+	}
+
+	/**
+	 * @testdox Test that products with NULL global_unique_id do not interfere with search results.
+	 *
+	 * @return void
+	 */
+	public function test_search_products_by_global_unique_id_with_null_value() {
+		$product_with_id = new WC_Product();
+		$product_with_id->set_regular_price( 42 );
+		$product_with_id->set_name( 'Product With ID' );
+		$product_with_id->set_global_unique_id( '5555555555555' );
+		$product_with_id->save();
+
+		$product_null_id = new WC_Product();
+		$product_null_id->set_regular_price( 42 );
+		$product_null_id->set_name( 'Product Null ID' );
+		$product_null_id->save();
+
+		$data_store = WC_Data_Store::load( 'product' );
+
+		// Search should find only the product with the matching global_unique_id.
+		$results = $data_store->search_products( '5555555555555', '', true, true );
+		$this->assertContains( $product_with_id->get_id(), $results );
+		$this->assertNotContains( $product_null_id->get_id(), $results );
+	}
+
 	/**
 	 * Test WC_Product_Data_Store_CPT::create_all_product_variations
 	 */