Commit 8c91802c96 for woocommerce

commit 8c91802c9606a4fb46d6f1692636c8a3fded1277
Author: Pavel Dohnal <pavel.dohnal@automattic.com>
Date:   Fri Apr 25 15:15:49 2025 +0200

    Hide backorder meta on completed orders (#57450)

    * Add changelog

    [WOOPLUG-266]

    * Hide backorder meta on completed orders

    Products previously on backorder continued showing the backorder status
    in emails after the order was fulfilled.

    [WOOPLUG-266]

    * Use an enum instead of hardcoded string

    [WOOPLUG-266]

    * Use an enum instead of hardcoded string in test

    [WOOPLUG-266]

    * Remove redundant code

    [WOOPLUG-266]

    ---------

    Co-authored-by: Vladimir Reznichenko <kalessil@gmail.com>

diff --git a/plugins/woocommerce/changelog/wooplug-266-product-variation-backordered-product-bought-still-shows-as b/plugins/woocommerce/changelog/wooplug-266-product-variation-backordered-product-bought-still-shows-as
new file mode 100644
index 0000000000..5306d98218
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-266-product-variation-backordered-product-bought-still-shows-as
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Hide backorder meta on completed orders
diff --git a/plugins/woocommerce/includes/class-wc-order-item-product.php b/plugins/woocommerce/includes/class-wc-order-item-product.php
index e0ac7a6506..ec4c4af155 100644
--- a/plugins/woocommerce/includes/class-wc-order-item-product.php
+++ b/plugins/woocommerce/includes/class-wc-order-item-product.php
@@ -7,6 +7,7 @@
  * @since   3.0.0
  */

+use Automattic\WooCommerce\Enums\OrderStatus;
 use Automattic\WooCommerce\Enums\ProductTaxStatus;
 use Automattic\WooCommerce\Enums\ProductType;
 use Automattic\WooCommerce\Utilities\NumberUtil;
@@ -435,6 +436,43 @@ class WC_Order_Item_Product extends WC_Order_Item {
 		return $product ? $product->get_tax_status() : ProductTaxStatus::TAXABLE;
 	}

+	/**
+	 * Get formatted meta data for the item.
+	 *
+	 * This overrides the parent method to conditionally remove backorder
+	 * meta data when the order is marked as completed.
+	 *
+	 * @param string $hideprefix  Meta data prefix, (default: _).
+	 * @param bool   $include_all Include all meta data, this stop skip items with values already in the product name.
+	 * @return array
+	 */
+	public function get_formatted_meta_data( $hideprefix = '_', $include_all = false ) {
+		$formatted_meta = parent::get_formatted_meta_data( $hideprefix, $include_all );
+
+		$order = $this->get_order();
+
+		if ( $order && $order->has_status( OrderStatus::COMPLETED ) ) {
+			/**
+			 * Filter the backorder meta key.
+			 * Make sure to use the same filter as used in set_backorder_meta().
+			 *
+			 * @param string $backorder_meta_key The backorder meta key.
+			 * @param WC_Order_Item_Product $item The order item product.
+			 * @since 9.9.0
+			 * @return string
+			 */
+			$backorder_meta_key = apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ), $this );
+
+			foreach ( $formatted_meta as $meta_id => $meta ) {
+				if ( isset( $meta->key ) && $meta->key === $backorder_meta_key ) {
+					unset( $formatted_meta[ $meta_id ] );
+				}
+			}
+		}
+
+		return $formatted_meta;
+	}
+
 	/*
 	|--------------------------------------------------------------------------
 	| Array Access Methods
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-order-item-product-test.php b/plugins/woocommerce/tests/php/includes/class-wc-order-item-product-test.php
new file mode 100644
index 0000000000..ac5ed212df
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/class-wc-order-item-product-test.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Unit tests for the WC_Order_Item_Product class functionalities.
+ *
+ * @package WooCommerce\Tests
+ */
+
+declare( strict_types=1 );
+
+use Automattic\WooCommerce\Enums\OrderStatus;
+
+/**
+ * WC_Order_Item_Product unit tests.
+ */
+class WC_Order_Item_Product_Test extends WC_Unit_Test_Case {
+
+	/**
+	 * Test that backorder meta is excluded from formatted meta data
+	 * for completed orders.
+	 */
+	public function test_get_formatted_meta_data_excludes_backorder_on_completed() {
+		// 1. Setup: Create product and set backorders allowed.
+		$product = WC_Helper_Product::create_simple_product();
+		$product->set_manage_stock( true );
+		$product->set_stock_quantity( 0 );
+		$product->set_backorders( 'notify' ); // Allow backorders with notification.
+		$product->save();
+
+		// 2. Create Order and add product item.
+		$order = WC_Helper_Order::create_order();
+		$item  = new WC_Order_Item_Product();
+		$item->set_product( $product );
+		$item->set_quantity( 1 );
+		$item->set_order_id( $order->get_id() );
+
+		// 3. Add backorder meta.
+		$backorder_meta_key = 'Backordered';
+		$item->add_meta_data( $backorder_meta_key, 1, true ); // Value typically is the backordered quantity.
+		$item->save();
+		$order->add_item( $item );
+		$order->save();
+
+		// 4. Assert: Check meta exists before completion.
+		$item_id                = $item->get_id();
+		$order_item             = $order->get_item( $item_id ); // Re-fetch item from order.
+		$formatted_meta_before  = $order_item->get_formatted_meta_data();
+		$found_backorder_before = false;
+		foreach ( $formatted_meta_before as $meta ) {
+			if ( $meta->key === $backorder_meta_key ) {
+				$found_backorder_before = true;
+				break;
+			}
+		}
+		$this->assertTrue( $found_backorder_before, 'Backorder meta should exist before order completion.' );
+
+		// 5. Complete the order.
+		$order->update_status( OrderStatus::COMPLETED );
+		$order->save();
+
+		// 6. Assert: Check meta is excluded after completion.
+		$order_item_after_complete = $order->get_item( $item_id ); // Re-fetch item again after status change.
+		$formatted_meta_after      = $order_item_after_complete->get_formatted_meta_data();
+		$found_backorder_after     = false;
+		foreach ( $formatted_meta_after as $meta ) {
+			if ( $meta->key === $backorder_meta_key ) {
+				$found_backorder_after = true;
+				break;
+			}
+		}
+		$this->assertFalse( $found_backorder_after, 'Backorder meta should be excluded after order completion.' );
+
+		// Clean up.
+		WC_Helper_Product::delete_product( $product->get_id() );
+		WC_Helper_Order::delete_order( $order->get_id() );
+	}
+}