From 81aad35f47e5a92161d89e2e0cfd89745e4ffc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roc=C3=ADo=20Vega?= Date: Wed, 31 Jan 2024 08:55:49 -0300 Subject: [PATCH] [MIG] base_name_search_improved: Migration to 17.0 --- base_name_search_improved/README.rst | 6 +- base_name_search_improved/__manifest__.py | 2 +- base_name_search_improved/hooks.py | 15 +-- base_name_search_improved/models/ir_model.py | 43 +++++---- base_name_search_improved/readme/CONFIGURE.md | 2 +- .../readme/DESCRIPTION.md | 4 +- .../static/description/index.html | 95 ++++++++++--------- .../tests/test_name_search.py | 90 +++++++++++++----- .../views/ir_model_views.xml | 6 +- 9 files changed, 157 insertions(+), 106 deletions(-) diff --git a/base_name_search_improved/README.rst b/base_name_search_improved/README.rst index 4e82a868969..3dee5936f43 100644 --- a/base_name_search_improved/README.rst +++ b/base_name_search_improved/README.rst @@ -7,7 +7,7 @@ Improved Name Search !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:88daa951eef68e162381052878cb525cb0c1e6b0a72cd2a1f9d138dca31bd6f4 + !! source digest: sha256:fe0fce7aeb356dfbf982cb648994712ec1907ee6ab73a221eee4cda4039e7a33 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -40,7 +40,7 @@ relaxed search also looks up for records containing all the words, so "John M. Brown" would be a match. It also tolerates words in a different order, so searching for "brown john" also works. -|image1| +|image0| Additionally, an Administrator can configure other fields to also lookup into. For example, Customers could be additionally searched by City or @@ -64,7 +64,7 @@ tried. The specific methods used are: All results found are presented in that order, hopefully presenting them in order of relevance. -.. |image1| image:: https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image0.png +.. |image0| image:: https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image0.png .. |image2| image:: https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image2.png **Table of contents** diff --git a/base_name_search_improved/__manifest__.py b/base_name_search_improved/__manifest__.py index f110da53c7d..d2574acbb19 100644 --- a/base_name_search_improved/__manifest__.py +++ b/base_name_search_improved/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Improved Name Search", "summary": "Friendlier search when typing in relation fields", - "version": "16.0.1.0.0", + "version": "17.0.1.0.0", "category": "Uncategorized", "website": "https://github.com/OCA/server-tools", "author": "Daniel Reis, Odoo Community Association (OCA), ADHOC SA", diff --git a/base_name_search_improved/hooks.py b/base_name_search_improved/hooks.py index d302f64a211..2e266b448ec 100644 --- a/base_name_search_improved/hooks.py +++ b/base_name_search_improved/hooks.py @@ -1,14 +1,15 @@ import logging -from odoo import SUPERUSER_ID, api - _logger = logging.getLogger(__name__) -def uninstall_hook(cr, registry): +def uninstall_hook(env): _logger.info("Reverting Patches...") - env = api.Environment(cr, SUPERUSER_ID, {}) - env["ir.model.fields"].with_context(_force_unlink=True).search( - [("name", "=", "smart_search")] - ).unlink() + fields_to_unlink = ( + env["ir.model.fields"] + .with_context(_force_unlink=True) + .search([("name", "=", "smart_search")]) + ) + if fields_to_unlink: + fields_to_unlink.unlink() _logger.info("Done!") diff --git a/base_name_search_improved/models/ir_model.py b/base_name_search_improved/models/ir_model.py index 792b97080b7..4bf4d8a3971 100644 --- a/base_name_search_improved/models/ir_model.py +++ b/base_name_search_improved/models/ir_model.py @@ -3,6 +3,7 @@ import logging from ast import literal_eval +from collections import defaultdict from lxml import etree @@ -55,12 +56,13 @@ def _get_name_search_domain(self): return [] -def _extend_name_results(self, domain, results, limit, name_get_uid): +def _extend_name_results(self, domain, results, limit): result_count = len(results) if result_count < limit: domain += [("id", "not in", results)] rec_ids = self._search( - domain, limit=limit - result_count, access_rights_uid=name_get_uid + domain, + limit=limit - result_count, ) results.extend(rec_ids) return results @@ -69,16 +71,15 @@ def _extend_name_results(self, domain, results, limit, name_get_uid): def patch_name_search(): @api.model def _name_search( - self, name="", args=None, operator="ilike", limit=100, name_get_uid=None + self, name="", domain=None, operator="ilike", limit=100, order=None ): # Perform standard name search res = _name_search.origin( self, name=name, - args=args, - operator=operator, + domain=domain, limit=limit, - name_get_uid=name_get_uid, + order=order, ) if name and _get_use_smart_name_search(self.sudo()) and operator in ALLOWED_OPS: # _name_search.origin is a query, we need to convert it to a list @@ -86,7 +87,7 @@ def _name_search( limit = limit or 0 # we add domain - args = args or [] + _get_name_search_domain(self.sudo()) + args = domain or [] + _get_name_search_domain(self.sudo()) # Support a list of fields to search on all_names = _get_rec_names(self.sudo()) @@ -94,15 +95,11 @@ def _name_search( # Try regular search on each additional search field for rec_name in all_names[1:]: domain = [(rec_name, operator, name)] - res = _extend_name_results( - self, base_domain + domain, res, limit, name_get_uid - ) + res = _extend_name_results(self, base_domain + domain, res, limit) # Try ordered word search on each of the search fields for rec_name in all_names: domain = [(rec_name, operator, name.replace(" ", "%"))] - res = _extend_name_results( - self, base_domain + domain, res, limit, name_get_uid - ) + res = _extend_name_results(self, base_domain + domain, res, limit) # Try unordered word search on each of the search fields # we only perform this search if we have at least one # separator character @@ -116,9 +113,7 @@ def _name_search( word_domain and ["|"] + word_domain or word_domain ) + [(rec_name, operator, word)] domain = (domain and ["&"] + domain or domain) + word_domain - res = _extend_name_results( - self, base_domain + domain, res, limit, name_get_uid - ) + res = _extend_name_results(self, base_domain + domain, res, limit) return res @@ -130,8 +125,7 @@ class Base(models.AbstractModel): # TODO perhaps better to create only the field when enabled on the model smart_search = fields.Char( - compute="_compute_smart_search", - search="_search_smart_search", + compute="_compute_smart_search", search="_search_smart_search", translate=False ) def _compute_smart_search(self): @@ -214,7 +208,7 @@ def _compute_smart_search_warning(self): @api.constrains("name_search_ids", "name_search_domain", "add_smart_search") def update_search_wo_restart(self): - self.clear_caches() + self.env.registry.clear_cache() @api.constrains("name_search_domain") def check_name_search_domain(self): @@ -252,9 +246,16 @@ def _register_hook(self): """ _logger.info("Patching BaseModel for Smart Search") + patched_models = defaultdict(set) + + def patch(model, name, method): + if model not in patched_models[name]: + ModelClass = type(model) + method.origin = getattr(ModelClass, name) + setattr(ModelClass, name, method) + for model in self.sudo().search(self.ids or []): Model = self.env.get(model.model) if Model is not None and not Model._abstract: - Model._patch_method("_name_search", patch_name_search()) - + patch(Model, "_name_search", patch_name_search()) return super()._register_hook() diff --git a/base_name_search_improved/readme/CONFIGURE.md b/base_name_search_improved/readme/CONFIGURE.md index a4080a66f0d..8cff7627e63 100644 --- a/base_name_search_improved/readme/CONFIGURE.md +++ b/base_name_search_improved/readme/CONFIGURE.md @@ -5,4 +5,4 @@ the top right search box, is not affected. Additional search fields can be configured at Settings \> Technical \> Database \> Models, using the "Name Search Fields" field. -![](https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image1.png) +![image1](https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image1.png) diff --git a/base_name_search_improved/readme/DESCRIPTION.md b/base_name_search_improved/readme/DESCRIPTION.md index 88b7ee5448b..e117da56048 100644 --- a/base_name_search_improved/readme/DESCRIPTION.md +++ b/base_name_search_improved/readme/DESCRIPTION.md @@ -10,13 +10,13 @@ relaxed search also looks up for records containing all the words, so "John M. Brown" would be a match. It also tolerates words in a different order, so searching for "brown john" also works. -![](https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image0.png) +![image0](https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image0.png) Additionally, an Administrator can configure other fields to also lookup into. For example, Customers could be additionally searched by City or Phone number. -![](https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image2.png) +![image2](https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image2.png) How it works: diff --git a/base_name_search_improved/static/description/index.html b/base_name_search_improved/static/description/index.html index e4652116dcd..5e41a927a1c 100644 --- a/base_name_search_improved/static/description/index.html +++ b/base_name_search_improved/static/description/index.html @@ -1,4 +1,3 @@ - @@ -9,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -275,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -301,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -367,40 +367,36 @@

Improved Name Search

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:88daa951eef68e162381052878cb525cb0c1e6b0a72cd2a1f9d138dca31bd6f4 +!! source digest: sha256:fe0fce7aeb356dfbf982cb648994712ec1907ee6ab73a221eee4cda4039e7a33 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

-

Extends the name search feature to use additional, more relaxed -matching methods, and to allow searching into configurable additional -record fields.

-

The name search is the lookup feature to select a related record. -For example, selecting a Customer on a new Sales order.

-

For example, typing “john brown” doesn’t match “John M. Brown”. -The relaxed search also looks up for records containing all the words, -so “John M. Brown” would be a match. -It also tolerates words in a different order, so searching -for “brown john” also works.

-
-https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image0.png -
-

Additionally, an Administrator can configure other fields to also lookup into. -For example, Customers could be additionally searched by City or Phone number.

-
-https://raw.githubusercontent.com/OCA/server-tools/11.0/base_name_search_improved/images/image2.png -
+

Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

+

Extends the name search feature to use additional, more relaxed matching +methods, and to allow searching into configurable additional record +fields.

+

The name search is the lookup feature to select a related record. For +example, selecting a Customer on a new Sales order.

+

For example, typing “john brown” doesn’t match “John M. Brown”. The +relaxed search also looks up for records containing all the words, so +“John M. Brown” would be a match. It also tolerates words in a different +order, so searching for “brown john” also works.

+

image0

+

Additionally, an Administrator can configure other fields to also lookup +into. For example, Customers could be additionally searched by City or +Phone number.

+

image2

How it works:

-

Regular name search is performed, and the additional search logic is only -triggered if not enough results are found. -This way, no overhead is added on searches that would normally yield results.

-

But if not enough results are found, then additional search methods are tried. -The specific methods used are:

+

Regular name search is performed, and the additional search logic is +only triggered if not enough results are found. This way, no overhead is +added on searches that would normally yield results.

+

But if not enough results are found, then additional search methods are +tried. The specific methods used are:

-

All results found are presented in that order, -hopefully presenting them in order of relevance.

+

All results found are presented in that order, hopefully presenting them +in order of relevance.

Table of contents

Configuration

-

The fuzzy search is automatically enabled on all Models. -Note that this only affects typing in related fields. -The regular search(), used in the top right search box, is not affected.

-

Additional search fields can be configured at Settings > Technical > Database > Models, -using the “Name Search Fields” field.

-
-Name Search Fields -
+

The fuzzy search is automatically enabled on all Models. Note that this +only affects typing in related fields. The regular search(), used in +the top right search box, is not affected.

+

Additional search fields can be configured at Settings > Technical > +Database > Models, using the “Name Search Fields” field.

+

image1

Usage

@@ -437,9 +431,18 @@

Known issues / Roadmap

@@ -447,7 +450,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -477,11 +480,13 @@

Other credits

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/server-tools project on GitHub.

+

This module is part of the OCA/server-tools project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/base_name_search_improved/tests/test_name_search.py b/base_name_search_improved/tests/test_name_search.py index 1f4d6cd7164..2a6f9df3be2 100644 --- a/base_name_search_improved/tests/test_name_search.py +++ b/base_name_search_improved/tests/test_name_search.py @@ -6,25 +6,31 @@ @tagged("post_install", "-at_install") class NameSearchCase(TransactionCase): - def setUp(self): - super(NameSearchCase, self).setUp() - phone_field = self.env.ref("base.field_res_partner__phone") - model_partner = self.env.ref("base.model_res_partner") - model_partner.name_search_ids = phone_field - model_partner.add_smart_search = True - model_partner.use_smart_name_search = True + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.phone_field = cls.env.ref("base.field_res_partner__phone") + cls.city_field = cls.env.ref("base.field_res_partner__city") + cls.email_field = cls.env.ref("base.field_res_partner__email") + cls.address_field = cls.env.ref("base.field_res_partner__contact_address") + cls.zip_field = cls.env.ref("base.field_res_partner__zip") + + cls.model_partner = cls.env.ref("base.model_res_partner") + cls.model_partner.name_search_ids = cls.phone_field + cls.model_partner.add_smart_search = True + cls.model_partner.use_smart_name_search = True # this use does not make muche sense but with base module we dont have # much models to use for tests - model_partner.name_search_domain = "[('parent_id', '=', False)]" - self.Partner = self.env["res.partner"] - self.partner1 = self.Partner.create( + cls.model_partner.name_search_domain = "[('parent_id', '=', False)]" + cls.Partner = cls.env["res.partner"] + cls.partner1 = cls.Partner.create( {"name": "Luigi Verconti", "vat": "1111", "phone": "+351 555 777 333"} ) - self.partner2 = self.Partner.create( + cls.partner2 = cls.Partner.create( {"name": "Ken Shabby", "vat": "2222", "phone": "+351 555 333 777"} ) - self.partner3 = self.Partner.create( + cls.partner3 = cls.Partner.create( { "name": "Johann Gambolputty of Ulm", "vat": "3333", @@ -35,37 +41,75 @@ def setUp(self): def test_RelevanceOrderedResults(self): """Return results ordered by relevance""" - res = self.Partner.name_search("555 777") - self.assertEqual( - res[0][0], self.partner1.id, "Match full string honoring spaces" - ) + res = self.Partner._name_search("555 777") + self.assertEqual(res[0], self.partner1.id, "Match full string honoring spaces") self.assertEqual( - res[1][0], self.partner2.id, "Match words honoring order of appearance" + res[1], self.partner2.id, "Match words honoring order of appearance" ) self.assertEqual( - res[2][0], + res[2], self.partner3.id, "Match all words, regardless of order of appearance", ) def test_NameSearchMustMatchAllWords(self): """Must Match All Words""" - res = self.Partner.name_search("ulm aaa 555 777") + res = self.Partner._name_search("ulm aaa 555 777") self.assertFalse(res) def test_NameSearchDifferentFields(self): """Must Match All Words""" - res = self.Partner.name_search("ulm 555 777") + res = self.Partner._name_search("ulm 555 777") self.assertEqual(len(res), 1) def test_NameSearchDomain(self): """Must not return a partner with parent""" - res = self.Partner.name_search("Edward Foster") + res = self.Partner._name_search("Edward Foster") self.assertFalse(res) def test_MustHonorDomain(self): """Must also honor a provided Domain""" - res = self.Partner.name_search("+351", args=[("vat", "=", "3333")]) + res = self.Partner._name_search("+351", domain=[("vat", "=", "3333")]) gambulputty = self.partner3.id self.assertEqual(len(res), 1) - self.assertEqual(res[0][0], gambulputty) + self.assertEqual(res[0], gambulputty) + + def test_SmartSearchWarning(self): + """Must check the funtional work of _compute_smart_search_warning""" + self.model_partner.name_search_ids = [ + (4, self.city_field.id), + (4, self.phone_field.id), + (4, self.email_field.id), + (4, self.address_field.id), + ] + self.model_partner._compute_smart_search_warning() + self.assertFalse( + self.model_partner.smart_search_warning, + "There should be no warnings", + ) + + self.model_partner.name_search_ids = [(4, self.zip_field.id)] + self.model_partner._compute_smart_search_warning() + self.assertIn( + "You have selected more than 4 fields for smart search", + self.model_partner.smart_search_warning, + "There should be a warning as there are more than 4 fields", + ) + + translatable_field = self.env["ir.model.fields"].create( + { + "name": "x_translatable_field", + "field_description": "Translatable Field", + "ttype": "char", + "model_id": self.model_partner.id, + "model": self.model_partner.model, + "translate": True, + } + ) + self.model_partner.name_search_ids = [(4, translatable_field.id)] + self.model_partner._compute_smart_search_warning() + self.assertIn( + "You have selected translatable fields in the smart search", + self.model_partner.smart_search_warning, + "There should be a warning as there are translatable fields", + ) diff --git a/base_name_search_improved/views/ir_model_views.xml b/base_name_search_improved/views/ir_model_views.xml index b991f0e6510..ceb0fbb983e 100644 --- a/base_name_search_improved/views/ir_model_views.xml +++ b/base_name_search_improved/views/ir_model_views.xml @@ -65,13 +65,13 @@ @@ -97,7 +97,7 @@ ir.model - +