Commit c18c60faa5c for woocommerce

commit c18c60faa5cd64796b4ed23da64eacadfcb518b4
Author: Tom Cafferkey <tjcafferkey@gmail.com>
Date:   Thu Nov 5 11:18:32 2026 +0000

    Fix download permissions for linked orders (#70367)

    * Retain permissions

    * Add changefile(s) from automation for the following project(s): woocommerce

    * Remove stale user functions PHPStan baseline entries

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/70367-fix-download-permissions-account-linking b/plugins/woocommerce/changelog/70367-fix-download-permissions-account-linking
new file mode 100644
index 00000000000..a0a2adb2862
--- /dev/null
+++ b/plugins/woocommerce/changelog/70367-fix-download-permissions-account-linking
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Preserve download permissions when linking guest orders to customer accounts.
diff --git a/plugins/woocommerce/includes/wc-user-functions.php b/plugins/woocommerce/includes/wc-user-functions.php
index dc414372b00..4c8bffe0584 100644
--- a/plugins/woocommerce/includes/wc-user-functions.php
+++ b/plugins/woocommerce/includes/wc-user-functions.php
@@ -352,12 +352,6 @@ function wc_update_new_customer_past_orders( $customer_id ) {
 			$order->set_customer_id( $customer->ID );
 			$order->save();

-			if ( $order->has_downloadable_item() ) {
-				$data_store = WC_Data_Store::load( 'customer-download' );
-				$data_store->delete_by_order_id( $order->get_id() );
-				wc_downloadable_product_permissions( $order->get_id(), true );
-			}
-
 			do_action( 'woocommerce_update_new_customer_past_order', $order_id, $customer );

 			if ( $order->get_status() === OrderInternalStatus::COMPLETED ) {
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 40a0333d191..cb7eeeca08b 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -35181,12 +35181,6 @@ parameters:
 			count: 1
 			path: includes/wc-user-functions.php

-		-
-			message: '#^Call to an undefined method WC_Data_Store\:\:delete_by_order_id\(\)\.$#'
-			identifier: method.notFound
-			count: 1
-			path: includes/wc-user-functions.php
-
 		-
 			message: '#^Call to an undefined method WC_Data_Store\:\:get_downloads_for_customer\(\)\.$#'
 			identifier: method.notFound
@@ -35199,12 +35193,6 @@ parameters:
 			count: 1
 			path: includes/wc-user-functions.php

-		-
-			message: '#^Call to an undefined method WC_Order\|WC_Order_Refund\:\:has_downloadable_item\(\)\.$#'
-			identifier: method.notFound
-			count: 1
-			path: includes/wc-user-functions.php
-
 		-
 			message: '#^Call to an undefined method WC_Order\|WC_Order_Refund\:\:is_download_permitted\(\)\.$#'
 			identifier: method.notFound
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php b/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php
index ae33244281f..361d3a53d6c 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php
@@ -198,6 +198,64 @@ class WC_Tests_Customer_Functions extends WC_Unit_Test_Case {
 		$this->assertEquals( 0, $linked );
 	}

+	/**
+	 * Linking a guest order must not recreate or reset download permissions.
+	 */
+	public function test_linking_guest_order_preserves_download_permissions() {
+		$customer_id = wc_create_new_customer( 'downloads@example.com', 'downloadcustomer', 'testpassword' );
+		$files       = array();
+		foreach ( array( 'revoked', 'exhausted', 'valid' ) as $file_id ) {
+			$file = new WC_Product_Download();
+			$file->set_id( $file_id );
+			$file->set_file( 'https://example.com/' . $file_id . '.pdf' );
+			$files[] = $file;
+		}
+		$product = new WC_Product_Simple();
+		$product->set_downloadable( true );
+		$product->set_download_limit( 2 );
+		$product->set_download_expiry( 30 );
+		$product->set_downloads( $files );
+		$product->save();
+
+		$order = new WC_Order();
+		$order->set_billing_email( 'downloads@example.com' );
+		$order->add_product( $product );
+		$order->set_status( OrderStatus::COMPLETED );
+		$order->save();
+		wc_downloadable_product_permissions( $order->get_id() );
+
+		$store       = WC_Data_Store::load( 'customer-download' );
+		$permissions = $store->get_downloads( array( 'order_id' => $order->get_id() ) );
+		$this->assertCount( 3, $permissions );
+		$by_file = array();
+		foreach ( $permissions as $permission ) {
+			$by_file[ $permission->get_download_id() ] = $permission;
+		}
+		$by_file['revoked']->delete();
+		$by_file['exhausted']->set_downloads_remaining( 0 );
+		$by_file['exhausted']->set_download_count( 2 );
+		$by_file['exhausted']->save();
+		$expiry = $by_file['valid']->get_access_expires()->getTimestamp();
+
+		$this->assertSame( 1, wc_update_new_customer_past_orders( $customer_id ) );
+		$permissions = $store->get_downloads( array( 'order_id' => $order->get_id() ) );
+		$this->assertCount( 2, $permissions );
+		foreach ( $permissions as $permission ) {
+			$this->assertArrayHasKey( $permission->get_download_id(), $by_file );
+			$this->assertSame( $by_file[ $permission->get_download_id() ]->get_id(), $permission->get_id() );
+			$this->assertEquals( $customer_id, $permission->get_user_id() );
+			if ( 'exhausted' === $permission->get_download_id() ) {
+				$this->assertEquals( 0, $permission->get_downloads_remaining() );
+				$this->assertEquals( 2, $permission->get_download_count() );
+			} else {
+				$this->assertSame( 'valid', $permission->get_download_id() );
+				$this->assertEquals( 2, $permission->get_downloads_remaining() );
+				$this->assertSame( $expiry, $permission->get_access_expires()->getTimestamp() );
+			}
+		}
+		$this->assertCount( 1, wc_get_customer_available_downloads( $customer_id ) );
+	}
+
 	/**
 	 * Test wc_update_new_customer_past_orders with invalid or changed email.
 	 *