Commit cf90bd0e601 for woocommerce

commit cf90bd0e6018f75d78fe666b22ed8f463469a30b
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date:   Wed Sep 30 12:42:35 2026 +0200

    Fix coupons losing minimum spend when the stored maximum is lower (#69237)

    Keep coupon minimum spend enforced when stored maximum is lower

    Loading a coupon passes both stored amounts to set_props() together, so the
    min/max pair check rejected both and the coupon loaded with no minimum spend.
    Skip the pair check while reading from the database and let the individual
    setters run, which keeps the minimum and drops the lower maximum as before.

    Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/fix-coupon-spend-limits-on-load b/plugins/woocommerce/changelog/fix-coupon-spend-limits-on-load
new file mode 100644
index 00000000000..a257e47394c
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-coupon-spend-limits-on-load
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Keep enforcing a coupon's minimum spend when its saved maximum spend is lower, as earlier versions did.
diff --git a/plugins/woocommerce/includes/class-wc-coupon.php b/plugins/woocommerce/includes/class-wc-coupon.php
index a991b4dfce2..5cdc77398b0 100644
--- a/plugins/woocommerce/includes/class-wc-coupon.php
+++ b/plugins/woocommerce/includes/class-wc-coupon.php
@@ -1482,7 +1482,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
 	 * guard still applies.  When neither is supplied, this delegates entirely
 	 * to the parent.
 	 *
-	 * @since 11.1.0
+	 * @since 11.2.0
 	 * @param array  $props   Key/value pairs of properties to set.
 	 * @param string $context Operation context ('set', 'edit', 'view').
 	 * @return bool|WP_Error True on success, WP_Error on failure.
@@ -1493,7 +1493,8 @@ class WC_Coupon extends WC_Legacy_Coupon {

 		// When only one amount or neither is supplied, the parent sequential
 		// setter calls (and their individual guards) handle everything.
-		if ( ! $has_minimum || ! $has_maximum ) {
+		// While loading from the database, skip the pair check so a stored minimum above the maximum is kept rather than dropped.
+		if ( ! $has_minimum || ! $has_maximum || ! $this->get_object_read() ) {
 			return parent::set_props( $props, $context );
 		}

diff --git a/plugins/woocommerce/tests/php/includes/class-wc-coupon-test.php b/plugins/woocommerce/tests/php/includes/class-wc-coupon-test.php
index 8bf2a6da214..b796332de8c 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-coupon-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-coupon-test.php
@@ -552,4 +552,44 @@ class WC_Coupon_Tests extends WC_Unit_Test_Case {
 		$this->assertSame( '50.00', $coupon->get_minimum_amount() );
 		$this->assertSame( '100.00', $coupon->get_maximum_amount() );
 	}
+
+	/**
+	 * @testdox Loading a coupon whose stored minimum exceeds its stored maximum keeps the minimum and drops the maximum.
+	 */
+	public function test_loading_inverted_stored_amounts_keeps_minimum_spend(): void {
+		$coupon = WC_Helper_Coupon::create_coupon(
+			'inverted-spend-limits',
+			array(
+				'minimum_amount' => '150',
+				'maximum_amount' => '100',
+			)
+		);
+
+		$this->assertSame( '150', $coupon->get_minimum_amount() );
+		$this->assertSame( '0', $coupon->get_maximum_amount() );
+	}
+
+	/**
+	 * @testdox set_props still rejects an inverted min/max pair on a coupon loaded from the database.
+	 */
+	public function test_set_props_rejects_inverted_pair_on_loaded_coupon(): void {
+		$coupon = WC_Helper_Coupon::create_coupon(
+			'loaded-spend-limits',
+			array(
+				'minimum_amount' => '50',
+				'maximum_amount' => '100',
+			)
+		);
+
+		$result = $coupon->set_props(
+			array(
+				'minimum_amount' => '150',
+				'maximum_amount' => '100',
+			)
+		);
+
+		$this->assertWPError( $result );
+		$this->assertSame( '50', $coupon->get_minimum_amount() );
+		$this->assertSame( '100', $coupon->get_maximum_amount() );
+	}
 }