Commit 997332ce106 for woocommerce

commit 997332ce1062f87e742a834b756ee0ec7d966e29
Author: Samuel Urbanowicz <samiuelson@gmail.com>
Date:   Thu Jun 11 15:36:35 2026 +0200

    Fix can_be_refunded tax basis for v4 fee and shipping lines (#65665)

    * Fix can_be_refunded tax basis for v4 fee and shipping lines

    * Add changelog entry for can_be_refunded tax basis fix

    * Remove changelog entry

diff --git a/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Orders/Schema/OrderSchema.php b/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Orders/Schema/OrderSchema.php
index b7c09913e8c..0ae3cc336ec 100644
--- a/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Orders/Schema/OrderSchema.php
+++ b/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Orders/Schema/OrderSchema.php
@@ -710,7 +710,7 @@ class OrderSchema extends AbstractSchema {
 				$item_data = $this->order_shipping_schema->get_item_response( $shipping_line, $request );
 				$refunded  = $refund_data['totals'][ $shipping_line->get_id() ] ?? 0.0;

-				$item_data['can_be_refunded'] = ( (float) $shipping_line->get_total() - $refunded ) > 0;
+				$item_data['can_be_refunded'] = ( (float) $shipping_line->get_total() + (float) $shipping_line->get_total_tax() - $refunded ) > 0;

 				$data['shipping_lines'][] = $item_data;
 			}
@@ -736,7 +736,7 @@ class OrderSchema extends AbstractSchema {
 				$item_data = $this->order_fee_schema->get_item_response( $fee_line, $request );
 				$refunded  = $refund_data['totals'][ $fee_line->get_id() ] ?? 0.0;

-				$item_data['can_be_refunded'] = ( (float) $fee_line->get_total() - $refunded ) > 0;
+				$item_data['can_be_refunded'] = ( (float) $fee_line->get_total() + (float) $fee_line->get_total_tax() - $refunded ) > 0;

 				$data['fee_lines'][] = $item_data;
 			}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/Orders/class-wc-rest-orders-v4-can-be-refunded-test.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/Orders/class-wc-rest-orders-v4-can-be-refunded-test.php
index cc514c5651f..83698fdad48 100644
--- a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/Orders/class-wc-rest-orders-v4-can-be-refunded-test.php
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/Orders/class-wc-rest-orders-v4-can-be-refunded-test.php
@@ -530,6 +530,109 @@ class WC_REST_Orders_V4_Can_Be_Refunded_Test extends WC_REST_Unit_Test_Case {
 		);
 	}

+	/**
+	 * @testdox Partially refunded shipping line with tax keeps can_be_refunded true.
+	 *
+	 * Regression test for WOOPLUG-6819: refunded fee/shipping totals from
+	 * DataUtils::compute_refunded_quantities_and_totals() are tax-inclusive,
+	 * so OrderSchema::can_be_refunded must compare against tax-inclusive line totals.
+	 */
+	public function test_partially_refunded_shipping_line_with_tax(): void {
+		$order = wc_create_order( array( 'customer_id' => $this->user_id ) );
+
+		$shipping_item = new WC_Order_Item_Shipping();
+		$shipping_item->set_props(
+			array(
+				'method_title' => 'Flat Rate',
+				'total'        => '10.00',
+			)
+		);
+		$shipping_item->set_taxes(
+			array(
+				'total' => array( 1 => '1.50' ),
+			)
+		);
+		$shipping_item->save();
+		$order->add_item( $shipping_item );
+		$order->set_status( 'completed' );
+		$order->save();
+		$order->update_taxes();
+		$order->calculate_totals( false );
+
+		wc_create_refund(
+			array(
+				'order_id'   => $order->get_id(),
+				'amount'     => 10.35,
+				'line_items' => array(
+					$shipping_item->get_id() => array(
+						'qty'          => 0,
+						'refund_total' => 9.00,
+						'refund_tax'   => array( 1 => 1.35 ),
+					),
+				),
+			)
+		);
+
+		$data = $this->get_order_response( $order->get_id() );
+
+		$this->assertTrue(
+			$data['shipping_lines'][0]['can_be_refunded'],
+			'Partially refunded shipping line with tax should remain refundable'
+		);
+	}
+
+	/**
+	 * @testdox Partially refunded fee line with tax keeps can_be_refunded true.
+	 *
+	 * Regression test for WOOPLUG-6819.
+	 */
+	public function test_partially_refunded_fee_line_with_tax(): void {
+		$order = wc_create_order( array( 'customer_id' => $this->user_id ) );
+
+		$fee_item = new WC_Order_Item_Fee();
+		$fee_item->set_props(
+			array(
+				'name'  => 'Test Fee',
+				'total' => '20.00',
+			)
+		);
+		$fee_item->set_taxes(
+			array(
+				'total' => array( 1 => '3.00' ),
+			)
+		);
+		$fee_item->save();
+		$order->add_item( $fee_item );
+		$order->set_status( 'completed' );
+		$order->save();
+		$order->update_taxes();
+		$order->calculate_totals( false );
+
+		// Refunded tax-inclusive total (20.50) exceeds the tax-exclusive line total (20.00) but
+		// not the tax-inclusive total (23.00). Under the old buggy comparison this flipped
+		// can_be_refunded to false even though $2.50 of fee + tax remains refundable.
+		wc_create_refund(
+			array(
+				'order_id'   => $order->get_id(),
+				'amount'     => 20.50,
+				'line_items' => array(
+					$fee_item->get_id() => array(
+						'qty'          => 0,
+						'refund_total' => 18.00,
+						'refund_tax'   => array( 1 => 2.50 ),
+					),
+				),
+			)
+		);
+
+		$data = $this->get_order_response( $order->get_id() );
+
+		$this->assertTrue(
+			$data['fee_lines'][0]['can_be_refunded'],
+			'Partially refunded fee line with tax should remain refundable'
+		);
+	}
+
 	/**
 	 * @testdox Zero-priced product line item with quantity follows quantity logic.
 	 */