fix(woocommerce): stop dropping Tutor LMS course orders from sales notifications#137
Open
sadmansakibnadvi wants to merge 1 commit into
Conversation
…tifications
WooCommerce Sales notifications silently discarded every order whose product
is linked to a Tutor LMS course, so on course-selling stores no sales
notifications appeared and newly placed orders were never imported. A second
defect stored the discarded orders as corrupt `data => false` entries, which
produced repeated PHP 8 "array offset on false" warnings on the frontend.
Defect 1 - hardcoded course exclusion (primary)
Course-linked items were rejected unconditionally in four places, using two
different detectors: product_belongs_with_course() in ordered_product() and
status_transition(), and the _tutor_product post meta in manual_order() and
get_orders(). All four now route through a single is_excluded_course_item()
helper gated behind a new off-by-default filter,
`nx_woocommerce_exclude_tutor_course_products`. Default behaviour imports
course orders; the old behaviour is available via:
add_filter('nx_woocommerce_exclude_tutor_course_products', '__return_true');
Because both detectors are unified in the helper, the free and Pro plugins
(Pro's WooCommerce extends this class and overrides none of these methods)
behave consistently.
Defect 2 - invalid entries stored, not skipped (secondary)
get_orders() pushed ordered_product()'s return value unconditionally, so its
false branches were persisted as `data => false` by get_notification_ready().
get_orders() now skips non-array entries, and check_order_status() guards
against a missing/non-array `data` before reading `['status']`, eliminating
the line-461 warning for every false-return branch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WooCommerce Sales notifications silently discard every order whose product is linked to a Tutor LMS course. On stores that sell courses through WooCommerce, no course orders ever appear and newly placed orders are never imported — the feature is effectively non-functional. A secondary defect stores the discarded orders as corrupt
data => falseentries, producing repeated PHP 8Trying to access array offset on falsewarnings on the frontend.Reproducible regardless of HPOS vs. legacy order storage. Observed on NotificationX 3.2.10 / Pro 3.1.4.
Root cause
Defect 1 — hardcoded course exclusion (primary)
Course-linked items were rejected unconditionally in four places, using two different detectors:
ordered_product()product_belongs_with_course()return falsestatus_transition()product_belongs_with_course()continuemanual_order()_tutor_productpost metacontinueget_orders()_tutor_productpost metacontinueBoth the live import path (
status_transition()) and the bulk rebuild path (get_orders()) applied a filter, which is why new orders were never imported and regeneration didn't help. There was no setting or filter guarding this behavior.Defect 2 — invalid entries stored, not skipped (secondary)
get_orders()pushedordered_product()'s return value unconditionally. Becauseget_orders()pre-filters by_tutor_productmeta whileordered_product()rejects byproduct_belongs_with_course(), any product that belongs to a course without_tutor_productmeta slipped past theget_orders()guard, returnedfalsefromordered_product(), and thatfalsewas persisted byget_notification_ready()as'data' => false. On the frontend,check_order_status()then evaluatedfalse['status']→ PHP 8 warning and a hidden entry.What changed (single file:
includes/Extensions/WooCommerce/WooCommerce.php)Fix A — gate the exclusion behind an off-by-default filter, at all four sites.
Introduced one helper,
is_excluded_course_item(), that unifies both Tutor detectors and is gated behind a new filter:Default (
false) imports course orders as expected. All four call sites now route through this helper, so free and Pro behave consistently (Pro'sWooCommerceextends this class and overrides none of these methods — verified — so no Pro change is needed).Fix B — never store invalid entries; harden the reader.
get_orders()now only stores non-empty array entries (skips everyfalsebranch ofordered_product(), not just the course one).check_order_status()guards against a missing/non-arraydatabefore reading['status'], eliminating the line-461 warning for allfalse-return branches.Backward compatibility
Verification
php -lclean on the changed file.data => falsereturnsfalsewith noarray offset on falsewarning.Post-deploy migration (runtime, not code)
Existing datasets already contain corrupt
data => falseentries. After deploying: regenerate each affected WooCommerce/Sales notification (rebuilds from live orders), then clear page/server cache. No DB migration required.Suggested regression tests (WP integration suite)
false).nx_woocommerce_exclude_tutor_course_products→true→ course entry excluded.false, no PHP warning on render.status_transition()(Processing/Completed) → imported consistently with regeneration.🤖 Generated with Claude Code