Commit b488e58568f for woocommerce
commit b488e58568f7b69698810810726e79555be05408
Author: Soroush Ahmadi <mrsoroushahmadi@gmail.com>
Date: Tue Sep 8 13:39:12 2026 +0330
Fix fatal generating receipt when variation parent product was deleted (#68369)
diff --git a/plugins/woocommerce/changelog/68368-fix-receipt-deleted-variation-parent b/plugins/woocommerce/changelog/68368-fix-receipt-deleted-variation-parent
new file mode 100644
index 00000000000..884683c786e
--- /dev/null
+++ b/plugins/woocommerce/changelog/68368-fix-receipt-deleted-variation-parent
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix a fatal error generating receipts for orders with a deleted variation parent.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 5bbc4147b56..c2d819931d3 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -62976,12 +62976,6 @@ parameters:
count: 1
path: src/Internal/ReceiptRendering/ReceiptRenderingEngine.php
- -
- message: '#^Cannot call method get_name\(\) on WC_Product\|false\|null\.$#'
- identifier: method.nonObject
- count: 1
- path: src/Internal/ReceiptRendering/ReceiptRenderingEngine.php
-
-
message: '#^Constant Automattic\\WooCommerce\\Internal\\ReceiptRendering\\ReceiptRenderingEngine\:\:KNOWN_CARD_TYPES is unused\.$#'
identifier: classConstant.unused
diff --git a/plugins/woocommerce/src/Internal/ReceiptRendering/ReceiptRenderingEngine.php b/plugins/woocommerce/src/Internal/ReceiptRendering/ReceiptRenderingEngine.php
index 0ba28601a94..2b19cdb4c35 100644
--- a/plugins/woocommerce/src/Internal/ReceiptRendering/ReceiptRenderingEngine.php
+++ b/plugins/woocommerce/src/Internal/ReceiptRendering/ReceiptRenderingEngine.php
@@ -301,11 +301,13 @@ class ReceiptRenderingEngine {
$line_item_product = $line_item->get_product();
if ( false === $line_item_product ) {
$line_item_title = $line_item->get_name();
+ } elseif ( $line_item_product instanceof \WC_Product_Variation ) {
+ $parent_product = wc_get_product( $line_item_product->get_parent_id() );
+ $line_item_title = $parent_product instanceof \WC_Product
+ ? $parent_product->get_name() . '. ' . $line_item_product->get_attribute_summary()
+ : $line_item->get_name();
} else {
- $line_item_title =
- ( $line_item_product instanceof \WC_Product_Variation ) ?
- ( wc_get_product( $line_item_product->get_parent_id() )->get_name() ) . '. ' . $line_item_product->get_attribute_summary() :
- $line_item_product->get_name();
+ $line_item_title = $line_item_product->get_name();
}
$line_items_info[] = array(
'type' => 'product',
diff --git a/plugins/woocommerce/tests/php/src/Internal/ReceiptRendering/ReceiptRenderingEngineTest.php b/plugins/woocommerce/tests/php/src/Internal/ReceiptRendering/ReceiptRenderingEngineTest.php
index ce76146f156..0bcc0336ba7 100644
--- a/plugins/woocommerce/tests/php/src/Internal/ReceiptRendering/ReceiptRenderingEngineTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/ReceiptRendering/ReceiptRenderingEngineTest.php
@@ -319,4 +319,29 @@ class ReceiptRenderingEngineTest extends \WC_Unit_Test_Case {
$this->assertStringNotContainsString( 'payment_method_section_title', $rendered );
}
+
+ /**
+ * @testdox 'generate_receipt' falls back to the line item name when the variation parent lookup dangles.
+ */
+ public function test_generate_receipt_falls_back_to_item_name_when_variation_parent_is_deleted() {
+ global $wpdb;
+ // Arrange - orphan the variation at the row level: deleting through the API cascades to the children, so it cannot produce this state.
+ $parent = \WC_Helper_Product::create_variation_product();
+ $variation = wc_get_product( current( $parent->get_children() ) );
+
+ $order = OrderHelper::create_order( 1, $variation );
+
+ $wpdb->delete( $wpdb->posts, array( 'ID' => $parent->get_id() ) );
+ clean_post_cache( $parent->get_id() );
+ $this->assertFalse( wc_get_product( $parent->get_id() ) );
+ $items = $order->get_items();
+ $this->assertInstanceOf( \WC_Product_Variation::class, current( $items )->get_product() );
+
+ // Act - rendering must not fatal on the dangling parent lookup.
+ $rendered = $this->render_receipt( $order );
+
+ // Assert - the line item name is used as the title fallback.
+ $item = current( $items );
+ $this->assertStringContainsString( $item->get_name(), $rendered );
+ }
}