Skip to content

[ADD] search_ux: search products and partners by keywords and configurable fields - #441

Open
mav-adhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-72472-mav
Open

[ADD] search_ux: search products and partners by keywords and configurable fields#441
mav-adhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-72472-mav

Conversation

@mav-adhoc

@mav-adhoc mav-adhoc commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Sustainable replacement for base_name_search_improved (OCA), on products and partners.

Task: https://www.adhoc.inc/odoo/all-tasks/12857/all-tasks/72472

What it does out of the box

  • A "Search Keywords" field (internal, trigram indexed) on product.template and res.partner, for aliases, nicknames and trade names.
  • That field already included in the search of those two models, on both surfaces. Nothing else enabled.

How it hooks

Surface Model Hook
Autocomplete (m2o) product.template, product.product name_search: calls super() and completes the suggestion list only if the native cascade did not fill limit
Autocomplete (m2o) res.partner _search_display_name: adds the fields to the same declarative domain, no extra query
Search... box of lists all three _get_view appends ('search_extended', 'ilike', self) to the filter_domain of the free text search field, composing with whatever other modules left there

No _inherit = "base", no _register_hook, no monkeypatching of the registry. Only those models inherit the mixin, explicitly.

Multi word: the term is split and all words must appear, in any order and in any configured field, built as a single domain resolved in a single query.

The seam is injected in _get_view and not in XML on purpose: partner_internal_code (75 productive databases) rewrites that filter_domain to search complete_name instead of display_name, so a static patch would either be overwritten or would drop its internal_code clause. Verified on a database with it installed: the rendered domain keeps complete_name, ref, email and internal_code, and adds the seam.

Two guards worth calling out:

  • The extension only applies to ilike / like. An exact search (=, =ilike) keeps matching exactly instead of leaking partial matches through the keywords.
  • A related source the user cannot read is skipped, not raised: stock.lot requires the Inventory/User group, so without this guard enabling the lot source would break the product autocomplete for every user without inventory rights.

What the consultant configures

Settings > Extended Search, per model: fields to include (model fields, Studio fields and forward paths like product_tmpl_id.my_field), related sources for products (lots/serials, vendor code, packaging barcodes), minimum characters before triggering (default 3), and archiving to turn it off completely. Fields are picked with the native field selector, which walks relations, so the consultant does not have to know the technical names.

It rejects on save, with an explanation: html fields, binary/attachment fields, non stored fields, wrong paths, group restricted fields and more than 5 fields per model (search_ux.max_fields).

Customization

A customer module adds its own source by inheriting a single extension point, without forking:

def _get_extra_search_domains(self, term):
    domains = super()._get_extra_search_domains(term)
    domains.append(Domain("id", "in",
        self.env["my.model"]._search([("code", "ilike", term)]).subselect("product_id")))
    return domains

Test plan

21 tests, green on a fresh 19.0 database with search_ux, stock, contacts and partner_internal_code installed:

odoo.tests.stats: search_ux: 23 tests 0.74s 614 queries
odoo.tests.result: 0 failed, 0 error(s) of 21 tests

Covered: keywords on the three models, native behaviour untouched without configuration, the extended search not running when the native one fills the limit, multi word in a single query, both surfaces returning the same records, the search views carrying the seam and composing with a rewritten filter_domain, minimum characters, turning it off, exact operators not extended, a source without access being skipped, the received domain being honoured, the extension point, and every rejected configuration.

Known deviations from the spec

  • The spec targets 20.0; this lands on 19.0 by decision, since the branch for 20.0 does not exist yet and the pain is on the versions in production.
  • The test for the extension point inherits the public method with a patch instead of shipping a separate test module. Say the word if a test-only module is preferred.
  • Performance measurement, the sibling migration task and the cross-product review live outside this PR.

Not included on purpose

Typo correction, lot reservation when the product is found by serial, and searching inside html descriptions.

@roboadhoc

Copy link
Copy Markdown
Contributor

Pull request status dashboard

@mav-adhoc
mav-adhoc force-pushed the 19.0-t-72472-mav branch 4 times, most recently from db55487 to 83980e3 Compare August 24, 2026 20:05
…rable fields

Adds a "search keywords" field on products and partners for aliases and
trade names, and a per model configuration to add fields and related
sources to the search.

It only hooks into the extension points Odoo documents, on the two search
surfaces of the backend: name_search on product.template and
product.product for the autocomplete (calling super and completing the
suggestion list), _search_display_name on res.partner, and _get_view to
add the search_extended seam to the free text search of the search views,
composing with whatever filter_domain other modules left there. Only
those models inherit the mixin, and the extra criteria are built as a
single domain resolved in a single query, which runs only when the native
search did not fill the limit.

The extended search only applies to partial match operators, so an exact
search keeps matching exactly, and a related source the user cannot read
is skipped instead of raising.

The configuration lives on its own model, restricted to the models that
inherit the mixin, with a field picker that walks relations, and rejects
on save what cannot be sustained: html, binary and non stored fields,
wrong paths, group restricted fields and more than five fields per model.

Customer specific sources are added from customer modules by inheriting
_get_extra_search_domains, without forking this module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants