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

DataUtils::convert_line_items_to_internal_formatpublicWC 1.0

Convert line items (schema format) to internal format. This keys arrays by item ID and has some different naming conventions.

111 => [ "qty" => 1, "refund_total" => 123, "refund_tax" => [

1 => 123,
2 => 456,

], ]

Method of the class: DataUtils{}

No Hooks.

Returns

array. The converted line items.

Usage

$DataUtils = new DataUtils();
$DataUtils->convert_line_items_to_internal_format( $line_items, $order );
$line_items(array) (required)
The line items to convert.
$order(WC_Order) (required)
The order being refunded.

DataUtils::convert_line_items_to_internal_format() code WC 11.1.1

public function convert_line_items_to_internal_format( $line_items, WC_Order $order ) {
	$prepared_line_items = array();

	foreach ( $line_items as $line_item ) {
		// A line item is processable when it has an ID and at least one of
		// quantity or refund_total. The legacy v3-style form may omit
		// quantity entirely; in that case qty=0 is recorded on the refund,
		// matching v3 semantics ("refunded $X of this line without consuming
		// specific units"). Dollar accounting via get_remaining_refund_amount
		// still bounds subsequent refunds, so per-unit looseness here does
		// not enable over-refunding.
		if ( ! isset( $line_item['line_item_id'] ) ) {
			continue;
		}
		if ( ! isset( $line_item['quantity'] ) && ! isset( $line_item['refund_total'] ) ) {
			continue;
		}

		// refund_tax presence is the discriminator for how refund_total is interpreted:
		// when refund_tax is absent, refund_total is tax-inclusive and the tax portion is
		// split out below; when refund_tax is present, refund_total is the tax-exclusive
		// subtotal and is stored as-is, with the supplied taxes added on top.
		//
		// If no explicit refund_tax provided, extract tax from the tax-inclusive
		// refund_total. Skip when refund_total is also missing — there's nothing
		// to extract tax from. The split is by the line's own stored total/tax
		// ratio via split_inclusive_by_stored_ratio(), the same method the preview
		// uses, so the stored refund matches what build_refund_preview() showed.
		if ( ! isset( $line_item['refund_tax'] ) && isset( $line_item['refund_total'] ) ) {
			$original_item = $order->get_item( $line_item['line_item_id'] );
			if ( $original_item instanceof WC_Order_Item_Product || $original_item instanceof WC_Order_Item_Shipping || $original_item instanceof WC_Order_Item_Fee ) {
				$split = $this->split_inclusive_by_stored_ratio( (float) $line_item['refund_total'], $original_item, wc_get_price_decimals() );

				// Leave a tax-free line untouched: refund_total stays the full
				// (tax-exclusive == tax-inclusive) amount and no refund_tax is set.
				if ( ! empty( $split['taxes'] ) ) {
					$line_item['refund_tax']   = $this->convert_proportional_taxes_to_schema_format( $split['taxes'] );
					$line_item['refund_total'] = $split['subtotal'];
				}
			}
		}

		// Default qty=0 when quantity was omitted (legacy v3-style explicit
		// refund_total path). Default refund_total=0 defensively; in practice
		// validate_line_items ensures one of them is set by this point.
		$prepared_line_items[ $line_item['line_item_id'] ] = array(
			'qty'          => $line_item['quantity'] ?? 0,
			'refund_total' => $line_item['refund_total'] ?? 0,
			'refund_tax'   => $this->convert_line_item_taxes_to_internal_format( $line_item['refund_tax'] ?? array() ),
		);
	}

	return $prepared_line_items;
}