Commit 0b703a574f5 for woocommerce

commit 0b703a574f5801dd93b9360fdd9af9ce41555353
Author: SH Sajal Chowdhury <72102985+shsajalchowdhury@users.noreply.github.com>
Date:   Fri May 1 20:55:55 2026 +0600

    Fix VAT exemption not applying to variable product prices (since 10.4.0) (#64406)

    * Fix VAT exemption not applying to variable product prices since 10.4.0

    The taxes_influence_price() method, introduced in 10.4.0, returns false
    when the customer is VAT exempt. This causes the opposite_price_hash
    optimization in read_price_data() to be used instead of direct cache
    key differentiation.

    The problem: when taxes_influence_price() returns false for VAT exempt
    customers, the cached prices from a non-exempt page load are served
    to exempt customers because the opposite hash check logic doesn't
    properly regenerate the exempt-specific cache entry.

    Even though the customer is VAT exempt, taxes DO influence the price
    (the displayed price changes when tax is removed). The method should
    return true so that separate cache entries are maintained for exempt
    and non-exempt customers via the price_hash (which already includes
    the is_vat_exempt status).

    Remove the VAT exempt check from taxes_influence_price() and update
    the test data provider accordingly. Add a dedicated test verifying
    that taxes_influence_price() returns true for both exempt and
    non-exempt customers.

    Closes #63716

    * Add docstring to test helper for docstring coverage threshold

    Add PHPDoc block to get_data_store_with_public_taxes_influence_price()
    to meet the 80% docstring coverage threshold.

    * Reset VAT-exempt state in test to prevent leaking into subsequent tests

    CodeRabbit flagged that test_taxes_influence_price_returns_true_for_vat_exempt
    leaves is_vat_exempt(true) set, which could cause order-dependent flakiness
    in downstream tests.

    * Add VAT-exempt state reset to test_read_prices_cache_when_taxes_dont_influence_price

    Addresses reviewer observation: the existing test was missing the
    set_is_vat_exempt(false) restoration that the new VAT-exempt test
    already includes. While currently harmless (all @testWith rows pass
    false), adding the reset prevents future data-provider additions
    from accidentally polluting later tests.

diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
index 0bf991771b1..6a40709a906 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
@@ -574,9 +574,12 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
 			return false;
 		}

-		if ( ! empty( WC()->customer ) && WC()->customer->get_is_vat_exempt() ) {
-			return false;
-		}
+		// Taxes influence the price regardless of VAT exempt status. Even when a
+		// customer is VAT exempt, the displayed prices differ from non-exempt
+		// prices, so they need separate cache entries and the opposite_price_hash
+		// optimization should not apply. Returning false here was causing cached
+		// non-exempt prices to be served to VAT exempt customers.
+		// See: https://github.com/woocommerce/woocommerce/issues/63716

 		return true;
 	}
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
index b397c6af2c7..33ce8ff95ee 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
@@ -468,7 +468,6 @@ class WC_Product_Variable_Data_Store_CPT_Test extends WC_Unit_Test_Case {
 	 * @testWith [false, true, true, false]
 	 *           [false, false, true, false]
 	 *           [true, true, false, false]
-	 *           [true, true, true, true]
 	 *
 	 * @param bool $tax_enabled Taxes enabled shop-wide or not.
 	 * @param bool $taxable_product Product is taxable or not.
@@ -504,11 +503,43 @@ class WC_Product_Variable_Data_Store_CPT_Test extends WC_Unit_Test_Case {
 		sort( $actual_hashes );
 		$this->assertEquals( $expected_hashes, $actual_hashes );

+		// Restore default state to avoid leaking into subsequent tests.
+		WC()->customer->set_is_vat_exempt( false );
+
 		remove_filter( 'wc_tax_enabled', $tax_enabled ? '__return_true' : '__return_false' );
 		remove_filter( 'woocommerce_product_is_taxable', $taxable_product ? '__return_true' : '__return_false' );
 		remove_filter( 'woocommerce_matched_rates', $tax_has_rates ? array( $this, '__return_rates' ) : '__return_empty_array' );
 	}

+	/**
+	 * @testdox taxes_influence_price returns true even when customer is VAT exempt, because exempt prices differ from non-exempt.
+	 */
+	public function test_taxes_influence_price_returns_true_for_vat_exempt() {
+		add_filter( 'wc_tax_enabled', '__return_true' );
+		add_filter( 'woocommerce_product_is_taxable', '__return_true' );
+		add_filter( 'woocommerce_matched_rates', array( $this, '__return_rates' ) );
+
+		$product = WC_Helper_Product::create_variation_product();
+
+		$extended_data_store = $this->get_data_store_with_public_taxes_influence_price();
+
+		// Non-exempt: taxes should influence price.
+		WC()->customer->set_is_vat_exempt( false );
+		$this->assertTrue( $extended_data_store->taxes_influence_price( $product ) );
+
+		// VAT exempt: taxes should STILL influence price because the displayed
+		// prices are different (tax removed), requiring separate cache entries.
+		WC()->customer->set_is_vat_exempt( true );
+		$this->assertTrue( $extended_data_store->taxes_influence_price( $product ) );
+
+		// Restore default state to avoid leaking into other tests.
+		WC()->customer->set_is_vat_exempt( false );
+
+		remove_filter( 'wc_tax_enabled', '__return_true' );
+		remove_filter( 'woocommerce_product_is_taxable', '__return_true' );
+		remove_filter( 'woocommerce_matched_rates', array( $this, '__return_rates' ) );
+	}
+
 	/**
 	 * @testdox read_prices does separate caching for prices for display and not for display when they are different.
 	 *
@@ -604,6 +635,21 @@ class WC_Product_Variable_Data_Store_CPT_Test extends WC_Unit_Test_Case {
 		// phpcs:enable Generic.CodeAnalysis, Squiz.Commenting
 	}

+	/**
+	 * Get a data store instance with taxes_influence_price() exposed as public.
+	 *
+	 * @return object Data store with public taxes_influence_price method.
+	 */
+	private function get_data_store_with_public_taxes_influence_price(): object {
+		// phpcs:disable Generic.CodeAnalysis, Squiz.Commenting
+		return new class() extends WC_Product_Variable_Data_Store_CPT {
+			public function taxes_influence_price( $product ): bool {
+				return parent::taxes_influence_price( $product );
+			}
+		};
+		// phpcs:enable Generic.CodeAnalysis, Squiz.Commenting
+	}
+
 	/**
 	 * Parse a variable product prices transient and return the hashes only.
 	 *