diff --git a/setup/web_search_with_and/odoo/addons/web_search_with_and b/setup/web_search_with_and/odoo/addons/web_search_with_and new file mode 120000 index 000000000..d32981712 --- /dev/null +++ b/setup/web_search_with_and/odoo/addons/web_search_with_and @@ -0,0 +1 @@ +../../../../web_search_with_and \ No newline at end of file diff --git a/setup/web_search_with_and/setup.py b/setup/web_search_with_and/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/web_search_with_and/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/web_search_with_and/README.rst b/web_search_with_and/README.rst new file mode 100644 index 000000000..84d27b3bb --- /dev/null +++ b/web_search_with_and/README.rst @@ -0,0 +1,99 @@ +==================================== +Use AND conditions on omnibar search +==================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/13.0/web_search_with_and + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-13-0/web-13-0-web_search_with_and + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/162/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +When searching for records on same field Odoo joins multiple queries with OR. +For example: + +* Perform a search for customer "John" on field Name +* Odoo displays customers containing "John" +* Search for "Smith" on same field Name +* Odoo displays customers containing "John" OR "Smith" + +With this module installed you can press Shift key before searching for "Smith" +and Odoo finds customers containing "John" AND "Smith" + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +* Enter your value in omnibar search field +* Press and hold Shift key +* Select field with mouse or keyboard to perform search on + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/162/11.0 + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Versada UAB +* ACSONE SA/NV + +Contributors +~~~~~~~~~~~~ + +* Andrius Preimantas +* Adrien Didenot +* Francesco Apruzzese +* Numigi (tm) and all its contributors (https://bit.ly/numigiens) +* Souheil Bejaoui +* Pedro Guirao + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_search_with_and/__init__.py b/web_search_with_and/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/web_search_with_and/__manifest__.py b/web_search_with_and/__manifest__.py new file mode 100644 index 000000000..53c101755 --- /dev/null +++ b/web_search_with_and/__manifest__.py @@ -0,0 +1,14 @@ +# Copyright 2015 Andrius Preimantas +# Copyright 2020 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Use AND conditions on omnibar search", + "version": "14.0.1.0.0", + "author": "Versada UAB, ACSONE SA/NV, Serincloud, Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "web", + "website": "https://github.com/OCA/web", + "depends": ["web"], + "data": ["views/assets.xml"], +} diff --git a/web_search_with_and/i18n/es.po b/web_search_with_and/i18n/es.po new file mode 100644 index 000000000..7461f0845 --- /dev/null +++ b/web_search_with_and/i18n/es.po @@ -0,0 +1,14 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" diff --git a/web_search_with_and/i18n/it.po b/web_search_with_and/i18n/it.po new file mode 100644 index 000000000..44b9b5f8e --- /dev/null +++ b/web_search_with_and/i18n/it.po @@ -0,0 +1,14 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" diff --git a/web_search_with_and/i18n/pt.po b/web_search_with_and/i18n/pt.po new file mode 100644 index 000000000..a46ffdd37 --- /dev/null +++ b/web_search_with_and/i18n/pt.po @@ -0,0 +1,14 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" diff --git a/web_search_with_and/i18n/web_search_with_and.pot b/web_search_with_and/i18n/web_search_with_and.pot new file mode 100644 index 000000000..cc93d01ee --- /dev/null +++ b/web_search_with_and/i18n/web_search_with_and.pot @@ -0,0 +1,13 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/web_search_with_and/readme/CONTRIBUTORS.rst b/web_search_with_and/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..fad3aabbb --- /dev/null +++ b/web_search_with_and/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* Andrius Preimantas +* Adrien Didenot +* Francesco Apruzzese +* Numigi (tm) and all its contributors (https://bit.ly/numigiens) +* Souheil Bejaoui +* Pedro Guirao diff --git a/web_search_with_and/readme/DESCRIPTION.rst b/web_search_with_and/readme/DESCRIPTION.rst new file mode 100644 index 000000000..88cd4e8e8 --- /dev/null +++ b/web_search_with_and/readme/DESCRIPTION.rst @@ -0,0 +1,10 @@ +When searching for records on same field Odoo joins multiple queries with OR. +For example: + +* Perform a search for customer "John" on field Name +* Odoo displays customers containing "John" +* Search for "Smith" on same field Name +* Odoo displays customers containing "John" OR "Smith" + +With this module installed you can press Shift key before searching for "Smith" +and Odoo finds customers containing "John" AND "Smith" diff --git a/web_search_with_and/readme/USAGE.rst b/web_search_with_and/readme/USAGE.rst new file mode 100644 index 000000000..469f9f74e --- /dev/null +++ b/web_search_with_and/readme/USAGE.rst @@ -0,0 +1,7 @@ +* Enter your value in omnibar search field +* Press and hold Shift key +* Select field with mouse or keyboard to perform search on + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/162/11.0 diff --git a/web_search_with_and/static/description/icon.png b/web_search_with_and/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/web_search_with_and/static/description/icon.png differ diff --git a/web_search_with_and/static/description/index.html b/web_search_with_and/static/description/index.html new file mode 100644 index 000000000..1621adbb9 --- /dev/null +++ b/web_search_with_and/static/description/index.html @@ -0,0 +1,443 @@ + + + + + + +Use AND conditions on omnibar search + + + + + + diff --git a/web_search_with_and/static/src/js/control_panel_model_extension.js b/web_search_with_and/static/src/js/control_panel_model_extension.js new file mode 100644 index 000000000..67f2d7db5 --- /dev/null +++ b/web_search_with_and/static/src/js/control_panel_model_extension.js @@ -0,0 +1,55 @@ +odoo.define( + "web_search_with_and/static/src/js/control_panel_model_extension.js", + function (require) { + "use strict"; + + const {patch} = require("web.utils"); + const components = { + ControlPanelModelExtension: require("web/static/src/js/control_panel/control_panel_model_extension.js"), + }; + + patch( + components.ControlPanelModelExtension, + "web_search_with_and/static/src/js/control_panel_model_extension.js", + { + addAutoCompletionValues({ + filterId, + label, + value, + operator, + isShiftKey, + }) { + const queryElem = this.state.query.find( + (queryElem) => + queryElem.filterId === filterId && + queryElem.value === value && + queryElem.operator === operator + ); + if (!queryElem) { + if (isShiftKey) { + const groupId = Math.random(); + this.state.query.push({ + filterId, + groupId, + label, + value, + operator, + }); + } else { + const {groupId} = this.state.filters[filterId]; + this.state.query.push({ + filterId, + groupId, + label, + value, + operator, + }); + } + } else { + queryElem.label = label; + } + }, + } + ); + } +); diff --git a/web_search_with_and/static/src/js/search_bar.js b/web_search_with_and/static/src/js/search_bar.js new file mode 100644 index 000000000..a12912057 --- /dev/null +++ b/web_search_with_and/static/src/js/search_bar.js @@ -0,0 +1,139 @@ +odoo.define("web_search_with_and/static/src/js/search_bar.js", function (require) { + "use strict"; + + const {patch} = require("web.utils"); + const components = { + SearchBar: require("web.SearchBar"), + }; + + patch(components.SearchBar, "web_search_with_and/static/src/js/search_bar.js", { + /** + * @private + * @param {Object} source + */ + _selectSource(source) { + // Inactive sources are: + // - Selection sources + // - "no result" items + if (source.active) { + const labelValue = source.label || this.state.inputValue; + console.log( + "------------- addAutoCompletionValues11 -------------", + source, + this.isShiftKey + ); + this.model.dispatch("addAutoCompletionValues", { + filterId: source.filterId, + value: + "value" in source + ? source.value + : this._parseWithSource(labelValue, source), + label: labelValue, + operator: source.filterOperator || source.operator, + isShiftKey: this.isShiftKey, + }); + } + this._closeAutoComplete(); + }, + + /** + * @private + * @param {KeyboardEvent} ev + */ + _onSearchKeydown(ev) { + if (ev.isComposing) { + // This case happens with an IME for example: we let it handle all key events. + return; + } + if (ev.shiftKey) { + this.isShiftKey = true; + } else { + this.isShiftKey = false; + } + const currentItem = this.state.sources[this.state.focusedItem] || {}; + switch (ev.key) { + case "ArrowDown": + ev.preventDefault(); + if (Object.keys(this.state.sources).length) { + let nextIndex = this.state.focusedItem + 1; + if (nextIndex >= this.state.sources.length) { + nextIndex = 0; + } + this.state.focusedItem = nextIndex; + } else { + this.env.bus.trigger("focus-view"); + } + break; + case "ArrowLeft": + if (currentItem.expanded) { + // Priority 1: fold expanded item. + ev.preventDefault(); + this._expandSource(currentItem, false); + } else if (currentItem.parent) { + // Priority 2: focus parent item. + ev.preventDefault(); + this.state.focusedItem = this.state.sources.indexOf( + currentItem.parent + ); + // Priority 3: Do nothing (navigation inside text). + } else if (ev.target.selectionStart === 0) { + // Priority 4: navigate to rightmost facet. + this._focusFacet(this.model.get("facets").length - 1); + } + break; + case "ArrowRight": + if (ev.target.selectionStart === this.state.inputValue.length) { + // Priority 1: Do nothing (navigation inside text). + if (currentItem.expand) { + // Priority 2: go to first child or expand item. + ev.preventDefault(); + if (currentItem.expanded) { + this.state.focusedItem++; + } else { + this._expandSource(currentItem, true); + } + } else if ( + ev.target.selectionStart === this.state.inputValue.length + ) { + // Priority 3: navigate to leftmost facet. + this._focusFacet(0); + } + } + break; + case "ArrowUp": + ev.preventDefault(); + let previousIndex = this.state.focusedItem - 1; + if (previousIndex < 0) { + previousIndex = this.state.sources.length - 1; + } + this.state.focusedItem = previousIndex; + break; + case "Backspace": + if (!this.state.inputValue.length) { + const facets = this.model.get("facets"); + if (facets.length) { + this._onFacetRemove(facets[facets.length - 1]); + } + } + break; + case "Enter": + if (!this.state.inputValue.length) { + this.model.dispatch("search"); + break; + } + /* Falls through */ + case "Tab": + if (this.state.inputValue.length) { + ev.preventDefault(); // Keep the focus inside the search bar + this._selectSource(currentItem); + } + break; + case "Escape": + if (this.state.sources.length) { + this._closeAutoComplete(); + } + break; + } + }, + }); +}); diff --git a/web_search_with_and/views/assets.xml b/web_search_with_and/views/assets.xml new file mode 100644 index 000000000..c34251868 --- /dev/null +++ b/web_search_with_and/views/assets.xml @@ -0,0 +1,21 @@ + + + +