Lokasi ngalangkungan proxy:   [ UP ]  
[Ngawartoskeun bug]   [Panyetelan cookie]                
Skip to content

Fix customers report includes/excludes params incorrectly filtering by customer_id#63232

Merged
senadir merged 7 commits intotrunkfrom
fix-customer-reports-datastore-include-exclude-params
Mar 6, 2026
Merged

Fix customers report includes/excludes params incorrectly filtering by customer_id#63232
senadir merged 7 commits intotrunkfrom
fix-customer-reports-datastore-include-exclude-params

Conversation

@mikejolley
Copy link
Copy Markdown
Member

@mikejolley mikejolley commented Feb 10, 2026

Submission Review Guidelines

Changes proposed in this Pull Request

This PR fixes two related bugs in the Customers Reports advanced filters:

  1. DataStore column mapping bug (commit 4cda1bfd6c): The exact_match_params in the DataStore were previously mapping email_includes, username_includes, and name_includes to the customer_id column instead of their respective columns (email, username, CONCAT_WS(' ', first_name, last_name)). This was fixed by changing the mapping to an associative array keyed by column expression.

  2. Frontend autocompleters send customer IDs, not strings: The frontend autocompleters for email, username, and name filters send customer IDs (integers) via params like email_includes=5,12. After fixing the DataStore to match against actual column values, these numeric IDs would be matched against strings (e.g., WHERE email IN ('5','12')), which returns nothing.

The fix: Controller-level ID consolidation

Rather than changing the frontend autocompleters, the Controller now consolidates numeric customer IDs from name_includes, email_includes, and username_includes (and their _excludes variants) into the customers/customers_exclude params before they reach the DataStore. This way:

  • Numeric values (from autocompleters) are consolidated into customers/customers_exclude and filtered by customer_id
  • String values (e.g., actual email addresses) pass through unchanged to the DataStore's exact-match filtering

The consolidation respects the match parameter:

  • match=all: include sets are intersected, exclude sets are unioned
  • match=any: include sets are unioned, exclude sets are intersected

New customers_exclude param: Added to both the Customers and Stats controllers/DataStore to support exclusion by customer ID.

Files changed:

File Change
src/Admin/API/Reports/Customers/Controller.php Added consolidate_customer_id_filters(), is_id_list(), customers_exclude REST param and mapping
src/Admin/API/Reports/Customers/Stats/Controller.php Reuses consolidation from parent Controller, added customers_exclude REST param and mapping
src/Admin/API/Reports/Customers/DataStore.php Added customers_exclude NOT IN clause
tests/.../class-wc-admin-reports-customers-controller-test.php 8 new tests for consolidation logic and customers_exclude

Screenshots or screen recordings

N/A - No visual UI changes.

How to test the changes in this Pull Request

Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:

  1. This PR includes tests. Confirm they pass:
    pnpm run test:php:env -- --filter WC_Admin_Reports_Customers_Controller_Test
    
  2. REST API testing:
    • Ensure you have at least two customers with different email addresses and completed orders.
    • GET /wc-analytics/reports/customers?email_includes=<known-email> — should return only the matching customer.
    • GET /wc-analytics/reports/customers?email_excludes=<known-email> — should exclude the matching customer.
    • Repeat with username_includes/username_excludes and name_includes/name_excludes.
    • GET /wc-analytics/reports/customers?customers_exclude=<customer-id> — should exclude the customer with that ID.
  3. UI testing:
    • Go to WooCommerce > Customers.
    • Click advanced filters from the dropdown.
    • Select email/username/name fields and enter values using the autocompleter.
    • Use a combination of include and exclude filters.
    • Confirm the results match your input — the correct customers should be filtered.

Testing that has already taken place

  • PHPUnit tests run via wp-env Docker environment (PHP 8.1, PHPUnit 9.6.29).
  • All 31 tests in WC_Admin_Reports_Customers_Controller_Test pass (23 existing + 8 new).
  • PHP linting clean (lint:php:changes).
  • PHPStan: no errors on all modified files.

Milestone

Note: Check the box above to have the milestone automatically assigned when merged.
Alternatively (e.g. for point releases), manually assign the appropriate milestone.

Changelog entry

  • Automatically create a changelog entry from the details below.
  • This Pull Request does not require a changelog entry. (Comment required below)
Changelog Entry Details

Significance

  • Patch
  • Minor
  • Major

Type

  • Fix - Fixes an existing bug
  • Add - Adds functionality
  • Update - Update existing functionality
  • Dev - Development related task
  • Tweak - A minor adjustment to the codebase
  • Performance - Address performance issues
  • Enhancement - Improvement to existing functionality

Message

Fix Customers Reports advanced filters (email, username, name) by consolidating autocompleter customer IDs into customers/customers_exclude params in the Controller, and add customers_exclude support.

Changelog Entry Comment

Comment

@mikejolley mikejolley self-assigned this Feb 10, 2026
@woocommercebot woocommercebot requested a review from a team February 10, 2026 16:40
@github-actions github-actions Bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Feb 10, 2026
@woocommercebot woocommercebot requested review from senadir and removed request for a team February 10, 2026 16:40
@github-actions
Copy link
Copy Markdown
Contributor

Testing Guidelines

Hi @senadir ,

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.

@mikejolley mikejolley added Reports/Analytics Issues related to analytics section. API labels Feb 10, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

This patch consolidates numeric customer ID filters across name/email/username into unified include/exclude params, adds a new customers_exclude query parameter, updates DataStore exact-match handling to use SQL expressions, and expands REST API tests covering include/exclude and consolidation behavior.

Changes

Cohort / File(s) Summary
Changelog
plugins/woocommerce/changelog/63232-fix-customer-reports-datastore-include-exclude-params
Added patch-level changelog entry documenting the fix for customer reports advanced filters and customers_exclude support.
DataStore
plugins/woocommerce/src/Admin/API/Reports/Customers/DataStore.php
Reworked exact-match filters from a list to a map of parameter => SQL expression; use the expression directly in IN/NOT IN clauses. Added handling for customers_exclude (customer_id NOT IN ...).
Controller
plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php
Added public static consolidate_customer_id_filters($args) to merge numeric IDs from name/email/username includes/excludes into customers / customers_exclude; added private static is_id_list($value); integrated consolidation into prepare_reports_query.
Stats Controller
plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Controller.php
Introduced customers_exclude collection param (sanitization and validation) and wired request arg through prepare_reports_query; calls consolidation via CustomersController::consolidate_customer_id_filters.
Test Coverage
plugins/woocommerce/tests/php/includes/admin/class-wc-admin-reports-customers-controller-test.php
Added ~13 new REST API tests covering include/exclude filters for email/username/name/country/location, numeric ID consolidation paths, customers_exclude behavior, and consolidation modes (match=all vs match=any).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant StatsController as "Stats Controller"
participant CustomersController as "Customers Controller"
participant DataStore
participant DB as "Database"

Client->>StatsController: GET /reports/customers?...(includes/excludes)... 
StatsController->>CustomersController: prepare_reports_query(args)
CustomersController->>CustomersController: consolidate_customer_id_filters(args)
CustomersController->>DataStore: build query(using consolidated args & SQL expressions)
DataStore->>DB: SELECT ... WHERE (... IN/NOT IN ...) 
DB-->>DataStore: rows
DataStore-->>CustomersController: result set
CustomersController-->>StatsController: formatted results
StatsController-->>Client: HTTP 200 + response

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the primary fix: correcting the customer report filters that were incorrectly filtering by customer_id instead of the appropriate columns.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the bugs fixed, the solution approach, files changed, and testing instructions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-customer-reports-datastore-include-exclude-params

No actionable comments were generated in the recent review. 🎉


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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 10, 2026

Test using WordPress Playground

The changes in this pull request can be previewed and tested using a WordPress Playground instance.
WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Test this pull request with WordPress Playground.

Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a new commit.

@senadir
Copy link
Copy Markdown
Member

senadir commented Feb 10, 2026

Wondering why this went undiscovered for so long? was it not being used anywhere in the UI?

@mikejolley
Copy link
Copy Markdown
Member Author

@senadir after seeing the test fail and further investigation, it looks like at some point the UI switched to an ID based approach, so email_includes=42 etc. This doesn't really make logical sense and goes against the defined schema. Im seeing if this can be resolved cleanly in the client without changing the API implementation.

@senadir
Copy link
Copy Markdown
Member

senadir commented Feb 10, 2026

fix it in v4

@mikejolley
Copy link
Copy Markdown
Member Author

@senadir This isn't part of the v4 API, its analytics. Claude was able to fix the client pretty easily and tests are passing. I think we should see if anyone is more familiar with admin/analytics to make the final call on this. I'm updating the PR description if the client changes now.

@github-actions
Copy link
Copy Markdown
Contributor

Size Change: -11 B (0%)

Total Size: 5.98 MB

compressed-size-action

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/js/components/src/search/autocompleters/customers.tsx`:
- Around line 82-87: Customer names with commas can be split by the backend's
explode(',', ...) on the name_includes param; update getOptionCompletion to stop
emitting raw names as the key used in filtering and instead return a stable
identifier (e.g., customer.id as the key) while keeping customer.name as the
label, and then ensure the code that builds the name_includes filter uses the
option.key (id) rather than option.label (name); this avoids sending
comma-containing names to the backend — alternatively, if you must send names,
encode/quote commas consistently before building name_includes, but prefer
switching to IDs via getOptionCompletion and downstream consumers.
🧹 Nitpick comments (1)
plugins/woocommerce/client/admin/client/analytics/report/customers/config.js (1)

128-137: Consider extracting the repeated getLabels pattern into a shared helper.

The same comma-split-trim-filter-map logic is duplicated three times (for name, username, and email). A small helper would reduce duplication:

♻️ Suggested refactor
const getStringLabels = async ( value ) =>
    value
        .split( ',' )
        .map( ( v ) => v.trim() )
        .filter( Boolean )
        .map( ( item ) => ( { key: item, label: item } ) );

Then reference getLabels: getStringLabels in each input config.

Also applies to: 236-245, 287-296

Comment thread packages/js/components/src/search/autocompleters/customers.tsx
@mikejolley
Copy link
Copy Markdown
Member Author

I don't want to invest too much time in this now that I know we can use IDs but there were some other options here:

  1. Supporting both ID and string format in API
  2. Query by string in client (this PR)
  3. Query by ID in client but use customers param instead of the email/username/name fields

@mikejolley mikejolley force-pushed the fix-customer-reports-datastore-include-exclude-params branch from 6404260 to 5904ba2 Compare February 11, 2026 11:03
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php`:
- Around line 120-154: consolidate_customer_id_filters currently overwrites any
pre-existing customers/customers_exclude and treats an empty include
intersection as "no filter" (which returns all customers); update
consolidate_customer_id_filters to merge consolidated IDs with any existing
$args['customers'] and $args['customers_exclude'] instead of overwriting: parse
any existing $args['customers']/$args['customers_exclude'] via wp_parse_id_list
and when merging apply the same semantics as the consolidation (if $match ===
'all' use array_intersect between the consolidated ids and the existing ids,
otherwise array_unique(array_merge(...))); additionally, when $match === 'all'
and the computed include intersection is empty, set $args['customers'] to a
single invalid ID (e.g., [0]) to force a no-results filter rather than leaving
an empty array that DataStore treats as "no filter".
🧹 Nitpick comments (3)
plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php (2)

156-176: is_numeric() is broader than integer IDs — consider tightening.

is_numeric() accepts floats (1.5), negative numbers (-1), and scientific notation (1e2). While wp_parse_id_list downstream will coerce these via absint, this makes the detection less precise. For example, email_includes=1e2 would be treated as customer ID 100.

A stricter check like ctype_digit(trim((string) $v)) would only match non-negative integer strings, which is the actual intent.

Suggested tightening
 	foreach ( $values as $v ) {
-		if ( ! is_numeric( trim( $v ) ) ) {
+		if ( ! ctype_digit( trim( (string) $v ) ) ) {
 			return false;
 		}
 	}

106-119: Missing @since annotation on public method.

consolidate_customer_id_filters is a public static method and should include a @since tag with the current WooCommerce version. As per coding guidelines: "Use @since annotations in PHP code with the current version from includes/class-woocommerce.php, removing any -dev suffix."

plugins/woocommerce/tests/php/includes/admin/class-wc-admin-reports-customers-controller-test.php (1)

643-981: Good coverage of the new functionality.

The tests cover string-based exact-match filtering, numeric ID consolidation, and match semantics well. Two test gaps worth noting — both relate to the bugs flagged in consolidate_customer_id_filters:

  1. No test for match=all intersection producing an empty set (e.g., name_includes=1&email_includes=2 where 1 and 2 are disjoint). This is the scenario where the current code would return all customers instead of none.
  2. No test for when the customers param is already populated alongside numeric email_includes/name_includes, verifying the values are merged rather than overwritten.

Adding these would lock down the edge cases.

Comment thread plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php
@senadir senadir merged commit cdf2cc6 into trunk Mar 6, 2026
35 checks passed
@senadir senadir deleted the fix-customer-reports-datastore-include-exclude-params branch March 6, 2026 15:58
@github-actions github-actions Bot added this to the 10.7.0 milestone Mar 6, 2026
@github-actions github-actions Bot added the needs: documentation The issue/PR requires documentation to be added. label Mar 6, 2026
samnajian pushed a commit that referenced this pull request Mar 11, 2026
…y `customer_id` (#63232)

* Update datastore to look at correct columns

* Expand test coverage

* Add changefile(s) from automation for the following project(s): woocommerce

* Make client search by strings rather than client ids

* Allow for either strings or IDs when using includes/excludes filter

* Add changefile(s) from automation for the following project(s): woocommerce

* Fix customer ID merging

---------

Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
jamesckemp pushed a commit that referenced this pull request Mar 11, 2026
…y `customer_id` (#63232)

* Update datastore to look at correct columns

* Expand test coverage

* Add changefile(s) from automation for the following project(s): woocommerce

* Make client search by strings rather than client ids

* Allow for either strings or IDs when using includes/excludes filter

* Add changefile(s) from automation for the following project(s): woocommerce

* Fix customer ID merging

---------

Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API needs: documentation The issue/PR requires documentation to be added. plugin: woocommerce Issues related to the WooCommerce Core plugin. Reports/Analytics Issues related to analytics section.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants