Commit 06526c15bd5 for woocommerce
commit 06526c15bd55f23115c5d494a7396a4f3d193a72
Author: Saskia Teichmann <s-a-s-k-i-a@users.noreply.github.com>
Date: Tue Sep 8 12:13:44 2026 +0200
Pass include/exclude through to the downloadable product search (#68166)
diff --git a/plugins/woocommerce/changelog/68101-grant-access-search-exclude b/plugins/woocommerce/changelog/68101-grant-access-search-exclude
new file mode 100644
index 00000000000..8d2906325c2
--- /dev/null
+++ b/plugins/woocommerce/changelog/68101-grant-access-search-exclude
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Pass the include/exclude parameters through to the downloadable product search so the Grant access field on the order edit screen no longer lists products that are already selected.
diff --git a/plugins/woocommerce/includes/class-wc-ajax.php b/plugins/woocommerce/includes/class-wc-ajax.php
index b9b0396d27f..eaddf7e50a7 100644
--- a/plugins/woocommerce/includes/class-wc-ajax.php
+++ b/plugins/woocommerce/includes/class-wc-ajax.php
@@ -2181,7 +2181,7 @@ class WC_AJAX {
$term = isset( $_GET['term'] ) ? (string) wc_clean( wp_unslash( $_GET['term'] ) ) : '';
$data_store = WC_Data_Store::load( 'product' );
- $ids = $data_store->search_products( $term, 'downloadable', true, false, $limit );
+ $ids = $data_store->search_products( $term, 'downloadable', true, false, $limit, $include_ids, $exclude_ids );
_prime_post_caches( $ids );
$product_objects = array_filter( array_map( 'wc_get_product', $ids ), 'wc_products_array_filter_readable' );
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-ajax-test.php b/plugins/woocommerce/tests/php/includes/class-wc-ajax-test.php
index a650c71fd3e..24cc3dbafb9 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-ajax-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-ajax-test.php
@@ -2385,6 +2385,47 @@ class WC_AJAX_Test extends \WP_Ajax_UnitTestCase {
$this->assertEquals( $original_qty, $fresh_item->get_quantity() );
}
+ /**
+ * The Grant access product search must honor the include/exclude parameters so
+ * already-granted products do not reappear in the results.
+ *
+ * @see https://github.com/woocommerce/woocommerce/issues/68101
+ */
+ public function test_json_search_downloadable_products_honors_include_and_exclude(): void {
+ $product_one = WC_Helper_Product::create_simple_product();
+ $product_one->set_name( 'Exclusit Download One' );
+ $product_one->set_downloadable( true );
+ $product_one->save();
+
+ $product_two = WC_Helper_Product::create_simple_product();
+ $product_two->set_name( 'Exclusit Download Two' );
+ $product_two->set_downloadable( true );
+ $product_two->save();
+
+ $this->_setRole( 'administrator' );
+
+ $_GET['security'] = wp_create_nonce( 'search-products' );
+ $_GET['term'] = 'Exclusit Download';
+ $_GET['exclude'] = array( $product_one->get_id() );
+
+ $response = $this->do_ajax( 'woocommerce_json_search_downloadable_products_and_variations' );
+
+ $this->assertIsArray( $response, 'The search should return a result set.' );
+ $this->assertArrayHasKey( $product_two->get_id(), $response, 'The non-excluded product must be part of the results.' );
+ $this->assertArrayNotHasKey( $product_one->get_id(), $response, 'An excluded (already granted) product must not reappear in the results.' );
+
+ // The include allowlist must be honored as well.
+ unset( $_GET['exclude'] );
+ $_GET['security'] = wp_create_nonce( 'search-products' );
+ $_GET['include'] = array( $product_one->get_id() );
+
+ $response = $this->do_ajax( 'woocommerce_json_search_downloadable_products_and_variations' );
+
+ $this->assertIsArray( $response, 'The include search should return a result set.' );
+ $this->assertArrayHasKey( $product_one->get_id(), $response, 'The included product must be part of the results.' );
+ $this->assertArrayNotHasKey( $product_two->get_id(), $response, 'A product outside the include allowlist must not be part of the results.' );
+ }
+
/**
* Does the 'hard work' of triggering an ajax endpoint and capturing the response.
*