Automattic\WooCommerce\Internal\RestApi\Routes\V4\Refunds

DataUtils::compute_quantity_refund_total │ private │ WC 11.1.0

Compute the tax-inclusive refund total for a quantity-form line item, capped to the line's remaining refundable amount.

{@see compute_line_item_refund_total()} rounds each request independently, so a sequence of partial quantity refunds can drift from the stored line gross by up to a cent per request — round(unit × 2) + round(unit × 4) may exceed the line total that round(unit × 6) would produce. The quantity form is "server, compute the amount for me", so rather than rejecting its own arithmetic the server caps the result:

  • The unit-derived amount is always clamped down to the remaining amount, so rounding drift can never push it over the remaining-amount cap the validators enforce. Shipping and fee lines carry a single unit whose derived amount is the full line total, so partially-refunded ones always resolve to their remainder through this clamp.
  • A product quantity that consumes the line's remaining refundable units is topped up to the exact remaining amount — closing the line at currency precision with no stranded cents — but only when every prior refund on the line matches its own quantity-derived amount, i.e. the shortfall is provably accumulated rounding drift. An off-schedule prior refund (an explicit partial amount, or a dollar-only refund with no units) means the residue was deliberately withheld, and the quantity form must never silently pay it back out; the residue stays refundable through an explicit refund_total.

When no refundable amount remains, the unclamped amount is returned as-is: clamping to zero would trip the zero-refund guard with a misleading error, while the validators reject the line with line_item_already_refunded. Explicit client-supplied refund_total values are never capped — those stay strictly validated.

Method of the class: DataUtils{}

No Hooks.

Returns

float. The tax-inclusive refund total, carrying the line's sign.

Usage

// private - for code of main (parent) class only
$result = $this->compute_quantity_refund_total( $item, $quantity, $refund_data ): float;
$item(WC_Order_Item_Product|WC_Order_Item_Shipping|WC_Order_Item_Fee) (required)
The order item.
$quantity(int) (required)
The quantity to refund (>= 1).
$refund_data(array) (required)
Refund-history snapshot from {@see compute_refunded_quantities_and_totals()} (see its return shape).

Changelog

Since 11.1.0 Introduced.

DataUtils::compute_quantity_refund_total() code WC 11.1.2

private function compute_quantity_refund_total( $item, int $quantity, array $refund_data ): float {
	$computed          = $this->compute_line_item_refund_total( $item, $quantity );
	$price_decimals    = wc_get_price_decimals();
	$signed_line_total = (float) $item->get_total() + (float) $item->get_total_tax();
	$refunded_total    = abs( (float) ( $refund_data['totals'][ $item->get_id() ] ?? 0.0 ) );
	$remaining_total   = NumberUtil::round( abs( $signed_line_total ) - $refunded_total, $price_decimals );

	if ( $remaining_total <= 0 ) {
		return $computed;
	}

	$sign         = $signed_line_total < 0 ? -1.0 : 1.0;
	$abs_computed = abs( $computed );

	if ( $abs_computed > $remaining_total ) {
		return $sign * $remaining_total;
	}

	if ( $item instanceof WC_Order_Item_Product && $abs_computed < $remaining_total ) {
		$remaining_qty = $item->get_quantity() + ( $refund_data['qtys'][ $item->get_id() ] ?? 0 );
		if (
			$quantity >= $remaining_qty &&
			$this->line_refund_history_matches_quantities( $item, $refund_data['line_refunds'][ $item->get_id() ] ?? array() )
		) {
			return $sign * $remaining_total;
		}
	}

	return $computed;
}