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.
*