Skip to content

Fix fatal generating receipt when variation parent product was deleted - #68369

Merged
jorgeatorres merged 6 commits into
woocommerce:trunkfrom
soroush5:fix-receipt-deleted-variation-parent
Sep 8, 2026
Merged

jorgeatorres merged 6 commits into
woocommerce:trunkfrom
soroush5:fix-receipt-deleted-variation-parent

Conversation

@soroush5

@soroush5 soroush5 commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Submission Review Guidelines:

  • I have followed the WooCommerce Contributing Guidelines and the WordPress Coding Standards.
  • I have checked to ensure there aren't other open Pull Requests for the same update/change.
  • I have reviewed my code for security best practices.

Changes proposed in this Pull Request:

Closes #68368.

ReceiptRenderingEngine called ->get_name() on the result of wc_get_product( parent_id ) with no guard. When the parent variable product was deleted the factory returns false and receipt generation fatals. Now falls back to the line item name, mirroring the existing false branch for a missing item product.

How to test the changes in this Pull Request:

End-to-end via the REST receipt endpoint (verified live on wp-env, PHP 8.1 — not just the unit test):

  1. Build the broken state with WP-CLI (one paste via wp eval-file). This creates a variable product + variation + order, then deletes ONLY the parent row — the same shape as failed cleanups / out-of-band deletes (deleting from the UI cascades to variations, so no click path can build this):
$parent = new WC_Product_Variable();
$parent->set_name( 'Receipt Repro Shirt' );
$parent->set_status( 'publish' );
$parent_id = $parent->save();
$variation = new WC_Product_Variation();
$variation->set_parent_id( $parent_id );
$variation->set_attributes( array( 'size' => 'M' ) );
$variation->set_regular_price( '10' );
$variation->set_status( 'publish' );
$variation_id = $variation->save();
$order = wc_create_order();
$order->add_product( wc_get_product( $variation_id ), 1 );
$order->calculate_totals();
$order_id = $order->get_id();
global $wpdb;
$wpdb->delete( $wpdb->posts, array( 'ID' => $parent_id ) );
clean_post_cache( $parent_id );
echo "order_id=$order_id parent lookup: " . var_export( wc_get_product( $parent_id ), true );

It prints something like order_id=14 parent lookup: false while the variation stays readable.
2. Make an admin Application Password (Users → Profile → Application Passwords, or wp user application-password create admin repro --porcelain).
3. Generate the receipt through REST:

curl -u admin:PASS -X POST "https://<host>/wp-json/wc/v3/orders/<order_id>/receipt?force_new=1"

(If permalinks are plain, use index.php?rest_route=/wc/v3/orders/<order_id>/receipt&force_new=1 instead.)

  • Without the fix: HTTP 500, "There has been a critical error on this website" (Error: Call to a member function get_name() on bool).
  • With the fix: HTTP 200 with {"receipt_url": "...", "expiration_date": "..."}.
  1. Automated cover: pnpm test:php:env -- --filter test_generate_receipt_falls_back_to_item_name_when_variation_parent_is_deleted (errors without the fix, passes with it; full ReceiptRenderingEngineTest class 17/17 green).

Testing that has already taken place:

wp-env locally (PHP 8.1): new test ERRORS without the fix (Call to a member function get_name() on bool) and passes with it; full ReceiptRenderingEngineTest class 17/17 green; phpcs-changed on the branch diff clean; php -l clean on both files.

Milestone

@github-actions github-actions Bot added type: community contribution plugin: woocommerce Issues related to the WooCommerce Core plugin. labels Sep 4, 2026
@coderabbitai

coderabbitai Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Team

Run ID: 0b3c645d-0dab-4851-a5b1-392222394cb2

📥 Commits

Reviewing files that changed from the base of the PR and between f87c2eb and cdb7d86.

📒 Files selected for processing (1)
  • plugins/woocommerce/tests/php/src/Internal/ReceiptRendering/ReceiptRenderingEngineTest.php

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Receipt rendering now handles variation items whose parent product was deleted. It falls back to the order line item name and adds regression coverage and a patch changelog entry.

Changes

Receipt Rendering Fix

Layer / File(s) Summary
Variation parent fallback and regression coverage
plugins/woocommerce/src/Internal/ReceiptRendering/ReceiptRenderingEngine.php, plugins/woocommerce/tests/php/src/Internal/ReceiptRendering/ReceiptRenderingEngineTest.php, plugins/woocommerce/changelog/68368-fix-receipt-deleted-variation-parent
Receipt titles validate the variation parent product and fall back to the order line item name when the parent is unavailable. The test creates an orphaned variation state by deleting the parent database row, then verifies the rendered title. The changelog records the patch-level fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to cdb7d

Receipt rendering now safely uses the order line item name when a variation’s deleted parent cannot be resolved, preventing receipt-generation failures for affected historical orders. The regression coverage is in place and no current merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #68368 by guarding the missing parent product lookup and falling back to the line item name, with a regression test for the orphaned variation case.
Out of Scope Changes check ✅ Passed The changelog, receipt-rendering fix, regression test, and PHPStan baseline cleanup directly support the linked issue and stated objectives. No unrelated changes are evident.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files.
Title check ✅ Passed The title clearly summarizes the main change: preventing a fatal error when generating a receipt after a variation parent product was deleted.
Description check ✅ Passed The description directly explains the cause, fallback behavior, regression test, and validation for the receipt-generation fix.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@woocommercebot
woocommercebot requested review from a team and jorgeatorres and removed request for a team September 4, 2026 08:47
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Testing Guidelines

Hi ,

Apart from reviewing the code changes, please make sure to review the testing instructions (Guide) and verify that relevant tests (E2E, Unit, Integration, etc.) have been added or updated as needed.

Reminder: PR reviewers are required to document testing performed. This includes:

  • 🖼️ Screenshots or screen recordings.
  • 📝 List of functionality tested / steps followed.
  • 🌐 Site details (environment attributes such as hosting type, plugins, theme, store size, store age, and relevant settings).
  • 🔍 Any analysis performed, such as assessing potential impacts on environment attributes and other plugins, conducting performance profiling, or using LLM/AI-based analysis.

⚠️ Within the testing details you provide, please ensure that no sensitive information (such as API keys, passwords, user data, etc.) is included in this public issue.

@jorgeatorres jorgeatorres left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @soroush5. Even though this is an edge-case, I think it's worth fixing.

That said, there are problems here that local minimal local verification would have caught: the testing steps don't reproduce the fatal at all, the new unit test also passes without the fix, and linting has failed twice in CI rather than being caught locally. Verifying your changes before submitting is part of the contribution, and it's what keeps review time focused on the fix itself. "Leaving to CI" is not the way to go.

I've noticed you've opened a few more PRs, and we appreciate that you want to contribute. Please give all of them the same treatment before requesting review: reproduce the issue yourself, make sure the test fails without the fix and passes with it, and confirm lint and tests pass locally. We're leaving this one open so you can do that work, but if it comes back for review in the same state, we'll have to close it.

@soroush5

soroush5 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

You're right on all three, and I did the work locally this time instead of leaving it to CI.

  1. Testing steps: conceded. Trash cascades to variations, so no UI path deterministically orphans one — the old steps couldn't reach the bug. The test now builds the orphan at the row level (variation readable, parent lookup false, both asserted rather than assumed) and the steps are rewritten around the PHPUnit reproducer, with that caveat stated plainly.
  2. RED/GREEN, run locally on wp-env (PHP 8.1): without the fix the test errors with Call to a member function get_name() on bool; with the fix it passes and renders the item name. Full ReceiptRenderingEngineTest class: 17/17 green.
  3. Lint: my spacing slips, admitted. phpcs-changed on the branch diff is clean locally now; CI will confirm.

On the other PRs: point taken — same treatment (reproduce locally, red/green test, local lint) before requesting review.

@jorgeatorres
jorgeatorres self-requested a review September 4, 2026 20:22

@jorgeatorres jorgeatorres left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@soroush5: asking the reviewer to run unit tests is not the same as testing instructions. We need to be able to reproduce both the bug and ensure the fix is correct.

I assume if you reported the issue and opened the PR it's because this can indeed occur somehow, even if it's an edge case. Please provide instructions anyone can follow, even if they require modifying the database or running a PHP snippet or WP-CLI command to simulate a broken state. Similarly, indicate all necessary steps to generate the receipt via the REST endpoint to illustrate the fix.

@soroush5

soroush5 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Fair point — "run the unit test" isn't testing instructions. Rewrote the section as a real end-to-end repro, all verified live on wp-env (PHP 8.1), not assumed:

  1. A one-paste WP-CLI snippet builds the broken state (variable product + variation + order, then deletes only the parent row) and prints the order id with the parent lookup confirmed false.
  2. Then a plain curl POST to /wc/v3/orders/<id>/receipt: HTTP 500 without the fix, HTTP 200 with receipt_url with it. I ran both sides myself, including the temporary un-fix to capture the 500.

No UI click path can build the orphan (trash cascades), which is why step 1 goes straight to the row level — the same shape as failed cleanups and out-of-band deletes. The automated test stays as backup (errors without the fix, 17/17 green with it).

@jorgeatorres
jorgeatorres self-requested a review September 7, 2026 18:11

@jorgeatorres jorgeatorres left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @soroush5! Overall this looks good to me. Left some minor feedback.

I don't think the testing instructions are ideal yet but I won't block on that. Just for future reference: a snippet of code creating the products and even the order is not too different from a unit test. The idea with these steps is that anyone can easily follow those, and at least for the first part products can be created via the admin and the order as well. Deleting the parent does require WP-CLI or manipulating the database, but that can also be explained.

@jorgeatorres
jorgeatorres self-requested a review September 8, 2026 10:08

@jorgeatorres jorgeatorres left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you @soroush5!

@jorgeatorres
jorgeatorres merged commit b488e58 into woocommerce:trunk Sep 8, 2026
31 of 32 checks passed
@github-actions github-actions Bot added this to the 11.2.0 milestone Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin: woocommerce Issues related to the WooCommerce Core plugin. type: community contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fatal generating receipt when variation parent product was deleted

2 participants