Commit ca9c108df58 for woocommerce

commit ca9c108df582ec362169b345e10e2f5f35807abc
Author: Néstor Soriano <konamiman@konamiman.com>
Date:   Thu Jun 18 11:17:52 2026 +0200

    Fix admin order items subtotal subtracting fee tax for fixed end-prices (#65535)

    Follow-up to #63744, which added get_subtotal_amount_to_display() to render
    the ex-tax "Items Subtotal" on the admin order screen for fixed end-price
    stores (prices include tax with woocommerce_adjust_non_base_location_prices
    disabled). It computed get_subtotal() - get_cart_tax(), but those have
    mismatched scopes: get_subtotal() covers line items only, while get_cart_tax()
    also includes fee and shipping taxes (update_taxes() sums them into cart_tax).

diff --git a/plugins/woocommerce/changelog/fix-line-item-subtotal-display b/plugins/woocommerce/changelog/fix-line-item-subtotal-display
new file mode 100644
index 00000000000..3b6835f5043
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-line-item-subtotal-display
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix the order admin Items Subtotal incorrectly subtracting fee tax for stores with fixed end-prices.
diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php
index 8dd3e01c12b..617e9ad7b1c 100644
--- a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php
+++ b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php
@@ -2282,15 +2282,26 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
 	 * Get the items subtotal amount to display in the admin order screen.
 	 *
 	 * For stores with fixed end-prices (prices entered including tax with the
-	 * woocommerce_adjust_non_base_location_prices adjustment disabled), the cart
-	 * tax is removed so the displayed subtotal matches the ex-tax cart display.
+	 * woocommerce_adjust_non_base_location_prices adjustment disabled), the
+	 * line-item subtotal tax is removed so the displayed subtotal matches the
+	 * ex-tax cart display. Only line-item taxes are subtracted, not the fee tax
+	 * that get_cart_tax() would also include.
 	 *
 	 * @return float
 	 */
 	public function get_subtotal_amount_to_display() {
-		return $this->has_fixed_end_prices()
-			? (float) $this->get_subtotal() - (float) $this->get_cart_tax()
-			: (float) $this->get_subtotal();
+		if ( ! $this->has_fixed_end_prices() ) {
+			return (float) $this->get_subtotal();
+		}
+
+		$subtotal_tax = 0;
+		foreach ( $this->get_items() as $item ) {
+			if ( $item instanceof WC_Order_Item_Product ) {
+				$subtotal_tax += self::round_line_tax( (float) $item->get_subtotal_tax(), false );
+			}
+		}
+
+		return (float) $this->get_subtotal() - wc_round_tax_total( $subtotal_tax );
 	}

 	/**
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php
index c4a46a9a868..1ab8e8a9276 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php
@@ -2332,6 +2332,47 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case {
 		}
 	}

+	/**
+	 * @testdox get_subtotal_amount_to_display() excludes fee tax from the items subtotal in fixed end-price mode.
+	 */
+	public function test_get_subtotal_amount_to_display_excludes_fee_tax_in_fixed_price_mode(): void {
+		$context = $this->create_manual_order_tax_context();
+		add_filter( 'woocommerce_adjust_non_base_location_prices', '__return_false' );
+		$order = $this->create_manual_order_for_tax_context( $context, '24' );
+
+		$fee = new WC_Order_Item_Fee();
+		$fee->set_props(
+			array(
+				'name'       => 'Handling',
+				'tax_status' => ProductTaxStatus::TAXABLE,
+				'tax_class'  => $context['tax_class_slug'],
+				'total'      => 10,
+			)
+		);
+		$order->add_item( $fee );
+
+		try {
+			$order->calculate_totals();
+
+			$line_item = current( $order->get_items() );
+
+			// Guard the precondition: the fee must be taxed, otherwise get_cart_tax()
+			// would equal the item tax and the buggy code would also yield €22.02.
+			$this->assertEquals( 0.90, round( (float) $fee->get_total_tax(), 2 ), 'Test setup must include taxable fee tax' );
+
+			// The €24 tax-inclusive item carries €1.98 tax, so the ex-tax items subtotal is €22.02.
+			// The €0.90 taxable-fee tax (also part of get_cart_tax()) must not be subtracted from it.
+			$this->assertEquals( 22.02, round( (float) $order->get_subtotal_amount_to_display(), 2 ), 'Displayed items subtotal must exclude fee tax' );
+			$this->assertEquals(
+				round( (float) $order->get_item_subtotal_to_display( $line_item ), 2 ),
+				round( (float) $order->get_subtotal_amount_to_display(), 2 ),
+				'Displayed items subtotal must match the per-item displayed subtotal'
+			);
+		} finally {
+			$this->cleanup_manual_order_tax_context( $context, $order );
+		}
+	}
+
 	/**
 	 * Creates tax rates and a product for manual order tax tests.
 	 *