diff --git a/web_field_required_invisible_manager/README.rst b/web_field_required_invisible_manager/README.rst new file mode 100644 index 000000000000..e000f8f41950 --- /dev/null +++ b/web_field_required_invisible_manager/README.rst @@ -0,0 +1,113 @@ +============================================== +Web Field Required Invisible Readonly Managerr +============================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:b8db09bac933e558edafafdefcf07eac51dc2973fa12c9c2084cdbe41a99c413 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/18.0/web_field_required_invisible_manager + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-18-0/web-18-0-web_field_required_invisible_manager + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to set a field required, invisible or readonly for +users belonging to a specific group. + +The field can be required, invisible or readonly in any case, or +according to specific conditions. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Go to Settings > Technical > Models + +Select a model > in tab "Custom required fields", "Custom invisible +fields" or "Custom readonly fields" add a line + +Select a field, add one or more group and enable flag "Required", +"Invisible" or "Readonly" + +If needed, set a condition for which the field should be required, +invisible or readonly for users of those groups. + +Known issues / Roadmap +====================== + +When one model inherits another (e.g. res.users inherits res.partner) +and new custom field is created, then same field is created for +inheriting model as well, which should be avoided or at least duplicated +field should be also manual, but created as base. + +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Ilyas +* ooops404 + +Contributors +------------ + +- `Ooops404 `__: + + - Ilyas + +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. + +.. |maintainer-ilyasProgrammer| image:: https://github.com/ilyasProgrammer.png?size=40px + :target: https://github.com/ilyasProgrammer + :alt: ilyasProgrammer + +Current `maintainer `__: + +|maintainer-ilyasProgrammer| + +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_field_required_invisible_manager/__init__.py b/web_field_required_invisible_manager/__init__.py new file mode 100644 index 000000000000..0650744f6bc6 --- /dev/null +++ b/web_field_required_invisible_manager/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/web_field_required_invisible_manager/__manifest__.py b/web_field_required_invisible_manager/__manifest__.py new file mode 100644 index 000000000000..8e21e1e79c80 --- /dev/null +++ b/web_field_required_invisible_manager/__manifest__.py @@ -0,0 +1,15 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +{ + "name": "Web Field Required Invisible Readonly Managerr", + "category": "Web", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "Ilyas, ooops404, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/web", + "maintainers": ["ilyasProgrammer"], + "depends": ["base"], + "data": [ + "security/ir.model.access.csv", + "views/views.xml", + ], +} diff --git a/web_field_required_invisible_manager/i18n/it.po b/web_field_required_invisible_manager/i18n/it.po new file mode 100644 index 000000000000..9b184c8102ee --- /dev/null +++ b/web_field_required_invisible_manager/i18n/it.po @@ -0,0 +1,239 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_field_required_invisible_manager +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-02-13 11:40+0000\n" +"Last-Translator: Francesco Foresti \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" +"X-Generator: Weblate 4.17\n" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_cfr_all_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_all +msgid "All Fields Restrictions" +msgstr "Tutte le restrizioni sui campi" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_base +msgid "Base" +msgstr "Base" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__condition_domain +msgid "Condition Domain" +msgstr "Dominio condizione" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.view_model_form_inherit +msgid "Custom Invisible Fields" +msgstr "Campi invisibili personalizzati" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__custom_invisible_restriction_ids +msgid "Custom Invisible Restriction" +msgstr "Restrizione invisibile" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.view_model_form_inherit +msgid "Custom Readonly Fields" +msgstr "Campi in sola lettura personalizzati" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__custom_readonly_restriction_ids +msgid "Custom Readonly Restriction" +msgstr "Restrizione sola lettura" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.view_model_form_inherit +msgid "Custom Required Fields" +msgstr "Campi obbligatori personalizzati" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__custom_required_restriction_ids +msgid "Custom Required Restriction" +msgstr "Restrizione obbligatorio" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__display_name +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__display_name +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_ui_view__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_id +msgid "Field" +msgstr "Campo" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_invisible +msgid "Field Invisible" +msgstr "Campo invisibile" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_name +msgid "Field Name" +msgstr "Nome campo" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_readonly +msgid "Field Readonly" +msgstr "Campo in sola lettura" + +#. module: web_field_required_invisible_manager +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_config +msgid "Fields Restrictions" +msgstr "Restrizioni sui campi" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__group_ids +msgid "Group" +msgstr "Gruppo" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__id +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__id +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_ui_view__id +msgid "ID" +msgstr "ID" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.cfr_all_view_form +msgid "Invisible Field Rule" +msgstr "Regola campo invisibile" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_cfr_invisible_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_invisible +msgid "Invisible Fields" +msgstr "Campi invisibili" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction____last_update +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model____last_update +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_ui_view____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_custom_field_restriction +msgid "Make field invisible or required" +msgstr "Rendi invisibile o obbligatorio" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__readonly_model_id +msgid "Model (readonly)" +msgstr "Modello (sola lettura)" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__required_model_id +msgid "Model (required)" +msgstr "Modello (obbligatorio)" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__invisible_model_id +msgid "Model (visibility)" +msgstr "Modello (visibilità)" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__model_name +msgid "Model Name" +msgstr "Nome modello" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_ir_model +msgid "Models" +msgstr "Modelli" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__readonly_field_id +msgid "Readonly Field" +msgstr "Campo in sola lettura" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.cfr_all_view_form +msgid "Readonly Field Rule" +msgstr "Regola campo sola lettura" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_cfr_readonly_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_readonly +msgid "Readonly Fields" +msgstr "Campi in sola lettura" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__required +msgid "Required" +msgstr "Obbligatorio" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__required_field_id +msgid "Required Field" +msgstr "Campo obbligatorio" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.cfr_all_view_form +msgid "Required Field Rule" +msgstr "Regola campo obbligatorio" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_field_manager_required_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_required +msgid "Required Fields" +msgstr "Campi obbligatori" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__default_required +msgid "Required by Default" +msgstr "Obbligatorio per default" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_ir_ui_view +msgid "View" +msgstr "Vista" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__visibility_field_id +msgid "Visibility Field" +msgstr "Visibilità campo" + +#~ msgid "invisible_model_id" +#~ msgstr "invisible_model_id" + +#~ msgid "readonly_model_id" +#~ msgstr "readonly_model_id" + +#~ msgid "required_model_id" +#~ msgstr "required_model_id" + +#~ msgid "Fields" +#~ msgstr "Campi" diff --git a/web_field_required_invisible_manager/i18n/web_field_required_invisible_manager.pot b/web_field_required_invisible_manager/i18n/web_field_required_invisible_manager.pot new file mode 100644 index 000000000000..bd1a5eea561e --- /dev/null +++ b/web_field_required_invisible_manager/i18n/web_field_required_invisible_manager.pot @@ -0,0 +1,224 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_field_required_invisible_manager +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.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" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_cfr_all_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_all +msgid "All Fields Restrictions" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_base +msgid "Base" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__condition_domain +msgid "Condition Domain" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__create_uid +msgid "Created by" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__create_date +msgid "Created on" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.view_model_form_inherit +msgid "Custom Invisible Fields" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__custom_invisible_restriction_ids +msgid "Custom Invisible Restriction" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.view_model_form_inherit +msgid "Custom Readonly Fields" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__custom_readonly_restriction_ids +msgid "Custom Readonly Restriction" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.view_model_form_inherit +msgid "Custom Required Fields" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__custom_required_restriction_ids +msgid "Custom Required Restriction" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__display_name +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__display_name +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_ui_view__display_name +msgid "Display Name" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_id +msgid "Field" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_invisible +msgid "Field Invisible" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_name +msgid "Field Name" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__field_readonly +msgid "Field Readonly" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_config +msgid "Fields Restrictions" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__group_ids +msgid "Group" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__id +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model__id +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_ui_view__id +msgid "ID" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.cfr_all_view_form +msgid "Invisible Field Rule" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_cfr_invisible_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_invisible +msgid "Invisible Fields" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction____last_update +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_model____last_update +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_ir_ui_view____last_update +msgid "Last Modified on" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__write_date +msgid "Last Updated on" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_custom_field_restriction +msgid "Make field invisible or required" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__readonly_model_id +msgid "Model (readonly)" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__required_model_id +msgid "Model (required)" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__invisible_model_id +msgid "Model (visibility)" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__model_name +msgid "Model Name" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_ir_model +msgid "Models" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__readonly_field_id +msgid "Readonly Field" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.cfr_all_view_form +msgid "Readonly Field Rule" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_cfr_readonly_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_readonly +msgid "Readonly Fields" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__required +msgid "Required" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__required_field_id +msgid "Required Field" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model_terms:ir.ui.view,arch_db:web_field_required_invisible_manager.cfr_all_view_form +msgid "Required Field Rule" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.actions.act_window,name:web_field_required_invisible_manager.action_field_manager_required_view_tree +#: model:ir.ui.menu,name:web_field_required_invisible_manager.menu_field_manager_required +msgid "Required Fields" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__default_required +msgid "Required by Default" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model,name:web_field_required_invisible_manager.model_ir_ui_view +msgid "View" +msgstr "" + +#. module: web_field_required_invisible_manager +#: model:ir.model.fields,field_description:web_field_required_invisible_manager.field_custom_field_restriction__visibility_field_id +msgid "Visibility Field" +msgstr "" diff --git a/web_field_required_invisible_manager/models/__init__.py b/web_field_required_invisible_manager/models/__init__.py new file mode 100644 index 000000000000..4f7d30749ddf --- /dev/null +++ b/web_field_required_invisible_manager/models/__init__.py @@ -0,0 +1,4 @@ +from . import custom_field_restriction +from . import base +from . import ir_ui_view +from . import ir_model diff --git a/web_field_required_invisible_manager/models/base.py b/web_field_required_invisible_manager/models/base.py new file mode 100644 index 000000000000..0e294bafb772 --- /dev/null +++ b/web_field_required_invisible_manager/models/base.py @@ -0,0 +1,66 @@ +# Copyright 2023 ooops404 +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +from odoo import models +from odoo.tools.safe_eval import safe_eval + + +class Base(models.AbstractModel): + _inherit = "base" + + def default_get(self, fields_list): + res = super().default_get(fields_list) + if self.env.user.has_group("base.group_user"): + vals = self._default_get_compute_restrictions_fields() + if vals: + res.update(vals) + return res + + def _default_get_compute_restrictions_fields(self): + restrictions = self.env["custom.field.restriction"].search( + [("model_name", "=", self._name)] + ) + values = {} + if not restrictions: + return values + for r in restrictions: + if r.visibility_field_id: + field_name = r.visibility_field_id.name + values[field_name] = False + if r.required_field_id: + field_name = r.required_field_id.name + values[field_name] = False + if r.readonly_field_id: + field_name = r.readonly_field_id.name + values[field_name] = False + if r.group_ids: + if r.group_ids & self.env.user.groups_id: + values[field_name] = True + return values + + def _compute_restrictions_fields(self): + """Common compute method for all restrictions types""" + for record in self: + restrictions = self.env["custom.field.restriction"].search( + [("model_name", "=", self._name)] + ) + if not restrictions: + return + for r in restrictions: + if r.visibility_field_id: + field_name = r.visibility_field_id.name + record[field_name] = False + if r.required_field_id: + field_name = r.required_field_id.name + record[field_name] = False + if r.readonly_field_id: + field_name = r.readonly_field_id.name + record[field_name] = False + if r.condition_domain: + filtered_rec_id = record.filtered_domain( + safe_eval(r.condition_domain) + ) + if filtered_rec_id and r.group_ids & self.env.user.groups_id: + record[field_name] = True + elif r.group_ids: + if r.group_ids & self.env.user.groups_id: + record[field_name] = True diff --git a/web_field_required_invisible_manager/models/custom_field_restriction.py b/web_field_required_invisible_manager/models/custom_field_restriction.py new file mode 100644 index 000000000000..292518840e90 --- /dev/null +++ b/web_field_required_invisible_manager/models/custom_field_restriction.py @@ -0,0 +1,164 @@ +# Copyright 2023 ooops404 +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval + + +class CustomFieldRestriction(models.Model): + _name = "custom.field.restriction" + _description = "Make field invisible or required" + + field_id = fields.Many2one( + "ir.model.fields", + ondelete="cascade", + required=True, + string="Field", + ) + field_name = fields.Char( + related="field_id.name", + store=True, + string="Field Name", + ) + required_model_id = fields.Many2one( + "ir.model", + ondelete="cascade", + string="Model (required)", + index=True, + ) + invisible_model_id = fields.Many2one( + "ir.model", + ondelete="cascade", + string="Model (visibility)", + index=True, + ) + readonly_model_id = fields.Many2one( + "ir.model", + ondelete="cascade", + string="Model (readonly)", + index=True, + ) + model_name = fields.Char( + compute="_compute_model_name", + store=True, + index=True, + ) + condition_domain = fields.Char() + group_ids = fields.Many2many("res.groups", required=True) + default_required = fields.Boolean( + related="field_id.required", string="Required by Default" + ) + required = fields.Boolean() + field_invisible = fields.Boolean() + field_readonly = fields.Boolean() + # generated technical fields used in form attrs: + visibility_field_id = fields.Many2one("ir.model.fields") + readonly_field_id = fields.Many2one("ir.model.fields") + required_field_id = fields.Many2one("ir.model.fields") + + @api.onchange("field_id") + def onchange_field_id(self): + vals = {} + if self.env.context.get("default_readonly_model_id"): + vals["readonly_model_id"] = ( + self.env["ir.model"] + ._get(self.env.context.get("default_readonly_model_id")) + .id + ) + elif self.env.context.get("default_required_model_id"): + vals["required_model_id"] = ( + self.env["ir.model"] + ._get(self.env.context.get("default_required_model_id")) + .id + ) + elif self.env.context.get("default_invisible_model_id"): + vals["invisible_model_id"] = ( + self.env["ir.model"] + ._get(self.env.context.get("default_invisible_model_id")) + .id + ) + self.update(vals) + + @api.depends("required_model_id", "invisible_model_id", "readonly_model_id") + def _compute_model_name(self): + for rec in self: + if rec.required_model_id: + rec.model_name = rec.required_model_id.model + rec.required = True + elif rec.invisible_model_id: + rec.model_name = rec.invisible_model_id.model + rec.field_invisible = True + elif rec.readonly_model_id: + rec.model_name = rec.readonly_model_id.model + rec.field_readonly = True + + @api.model + def create(self, vals): + rec = super().create(vals) + if rec.invisible_model_id and rec.field_invisible: + rec.create_restriction_field("visibility") + elif rec.readonly_model_id and rec.field_readonly: + rec.create_restriction_field("readonly") + elif rec.required_model_id and rec.required: + rec.create_restriction_field("required") + return rec + + def write(self, vals): + res = super().write(vals) + if vals.get("field_id"): + if self.visibility_field_id: + self.visibility_field_id.unlink() + self.create_restriction_field("visibility") + elif self.readonly_field_id: + self.readonly_field_id.unlink() + self.create_restriction_field("readonly") + elif self.required_field_id: + self.required_field_id.unlink() + self.create_restriction_field("required") + return res + + def create_restriction_field(self, f_type): + field_name = self.get_field_name(f_type) + field_id = self.env["ir.model.fields"].search( + [("name", "=", field_name), ("state", "=", "manual")] + ) + if f_type == "required": + rec_model_id = self.required_model_id.id + rec_field_name = "required_field_id" + elif f_type == "readonly": + rec_model_id = self.readonly_model_id.id + rec_field_name = "readonly_field_id" + elif f_type == "visibility": + rec_model_id = self.invisible_model_id.id + rec_field_name = "visibility_field_id" + if not field_id: + deps = "" + if self.condition_domain: + deps = ",".join( + {r[0] for r in safe_eval(self.condition_domain)} - {"id", "&", "|"} + ) + field_id = self.env["ir.model.fields"].create( + { + "name": field_name, + "model_id": rec_model_id, + "state": "manual", + "field_description": f"{self.field_id.name} {f_type} field", + "store": False, + "ttype": "boolean", + "compute": "for r in self: r._compute_restrictions_fields()", + "depends": deps, + } + ) + self[rec_field_name] = field_id + + def get_field_name(self, f_type): + # e.g. x_computed_res_partner_name_readonly + model_name = self.field_id.model.replace(".", "_") + field_name = self.field_id.name + return f"x_computed_{model_name}_{field_name}_{f_type}" + + def unlink(self): + for rec in self: + rec.visibility_field_id.unlink() + rec.readonly_field_id.unlink() + rec.required_field_id.unlink() + return super().unlink() diff --git a/web_field_required_invisible_manager/models/ir_model.py b/web_field_required_invisible_manager/models/ir_model.py new file mode 100644 index 000000000000..527cb761c0d6 --- /dev/null +++ b/web_field_required_invisible_manager/models/ir_model.py @@ -0,0 +1,20 @@ +# Copyright 2023 ooops404 +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +from odoo import fields, models + + +class IrModel(models.Model): + _inherit = "ir.model" + + custom_required_restriction_ids = fields.One2many( + "custom.field.restriction", + "required_model_id", + ) + custom_invisible_restriction_ids = fields.One2many( + "custom.field.restriction", + "invisible_model_id", + ) + custom_readonly_restriction_ids = fields.One2many( + "custom.field.restriction", + "readonly_model_id", + ) diff --git a/web_field_required_invisible_manager/models/ir_ui_view.py b/web_field_required_invisible_manager/models/ir_ui_view.py new file mode 100644 index 000000000000..e6f27be8620b --- /dev/null +++ b/web_field_required_invisible_manager/models/ir_ui_view.py @@ -0,0 +1,170 @@ +# Copyright 2023 ooops404 +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) + +import ast + +from lxml import etree + +from odoo import models +from odoo.osv.expression import distribute_not, normalize_domain + +from odoo.addons.base.models.ir_ui_view import NameManager + + +class IrUiView(models.Model): + _inherit = "ir.ui.view" + + def postprocess_and_fields(self, node, model=None, validate=False, **options): + arch, new_fields = super().postprocess_and_fields( + node, model, validate=validate, **options + ) + if self.type not in ["form", "tree"] and node.tag not in ["form", "tree"]: + return arch, new_fields + restrictions = self.env["custom.field.restriction"].search( + [ + ("model_name", "=", model or self.model), + ("group_ids", "in", self.env.user.groups_id.ids), + ] + ) + view_type = node.tag or self.type + if restrictions: + arch = self.create_restrictions_fields(restrictions, view_type, arch) + arch = self.process_child_fields(arch, new_fields, view_type) + return arch, new_fields + + def process_child_fields(self, arch, new_fields, view_type): + """Collect all relational fields and update their views""" + view_model = self.env["ir.model"]._get(self.model) + related_fields = [ + (k.name, k.relation) + for k in view_model.field_id + if k.ttype in ["many2many", "many2one", "one2many"] + ] + related_models_names = [r[1] for r in related_fields] + restrictions = self.env["custom.field.restriction"].search( + [ + ("model_name", "in", related_models_names), + ("group_ids", "in", self.env.user.groups_id.ids), + ] + ) + if restrictions and view_type == "form": + for restr in restrictions: + todo_fields = list( + filter(lambda x: x[1] == restr.model_name, related_fields) + ) + for todo_field in todo_fields: + if not new_fields.get(todo_field[0]): + continue + for sub_view_type, sub_view in new_fields[todo_field[0]][ + "views" + ].items(): + if sub_view_type not in ["form", "tree"]: + continue + z_arch = sub_view + z_arch["arch"] = self.create_restrictions_fields( + restr, view_type, z_arch["arch"] + ) + if isinstance(z_arch["arch"], bytes): + z_arch["arch"] = z_arch["arch"].decode("utf-8") + name_manager = NameManager(False, self.env[restr.model_name]) + if restr.readonly_field_id and restr.readonly_model_id: + model_field_infos = name_manager.fields_get.get( + restr.readonly_field_id.name + ) + z_arch["fields"][restr.readonly_field_id.name] = ( + model_field_infos + ) + if restr.visibility_field_id and restr.invisible_model_id: + model_field_infos = name_manager.fields_get.get( + restr.visibility_field_id.name + ) + z_arch["fields"][restr.visibility_field_id.name] = ( + model_field_infos + ) + if restr.required_field_id and restr.required_model_id: + model_field_infos = name_manager.fields_get.get( + restr.required_field_id.name + ) + z_arch["fields"][restr.required_field_id.name] = ( + model_field_infos + ) + return arch + + def create_restrictions_fields(self, restrictions, view_type, arch): + doc = etree.XML(arch) + for node in doc.xpath("//field"): + field_name = node.attrib.get("name") + restrictions_filtered = restrictions.filtered( + lambda r, field_name=field_name: r.field_id.name == field_name + ) + if not restrictions_filtered: + continue + + for r in restrictions_filtered: + domain = ( + ( + ast.literal_eval(r.condition_domain) + if isinstance(r.condition_domain, str) + else r.condition_domain + ) + if r.condition_domain + else [] + ) + + if r.field_invisible and r.invisible_model_id: + node.set( + "invisible", + self.domain_to_expression(domain) + or r.get_field_name("visibility"), + ) + if r.required_field_id and r.required_model_id: + node.set( + "required", + self.domain_to_expression(domain) + or r.get_field_name("required"), + ) + if r.readonly_field_id and r.readonly_model_id: + node.set( + "readonly", + self.domain_to_expression(domain) + or r.get_field_name("readonly"), + ) + + return etree.tostring(doc, encoding="unicode") + + @staticmethod + def domain_to_expression(domain): + """Convert the given domain into a python expression""" + domain = normalize_domain(domain) + domain = distribute_not(domain) + expression = [] + for leaf in reversed(domain): + if leaf == "&": + right = expression.pop() + left = expression.pop() + expression.append(f"({left} and {right})") + elif leaf == "|": + right = expression.pop() + left = expression.pop() + expression.append(f"({left} or {right})") + elif leaf == "!": + expr = expression.pop() + expression.append(f"(not {expr})") + elif isinstance(leaf, tuple | list): + left, operator, right = leaf + if operator == "=": + operator = "==" + elif operator == "<>": + operator = "!=" + if operator in ["in", "not in"] and not isinstance(right, list | tuple): + right = [right] + if operator == "in": + expr = f"{right!r} in {left}" + elif operator == "not in": + expr = f"{right!r} not in {left}" + else: + expr = f"{left} {operator} {right!r}" + expression.append(expr) + else: + expression.append(str(leaf)) + return " ".join(expression) diff --git a/web_field_required_invisible_manager/pyproject.toml b/web_field_required_invisible_manager/pyproject.toml new file mode 100644 index 000000000000..4231d0cccb3d --- /dev/null +++ b/web_field_required_invisible_manager/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/web_field_required_invisible_manager/readme/CONTRIBUTORS.md b/web_field_required_invisible_manager/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..3dd90a0a8cca --- /dev/null +++ b/web_field_required_invisible_manager/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [Ooops404](https://www.ooops404.com): + - Ilyas \<\> diff --git a/web_field_required_invisible_manager/readme/DESCRIPTION.md b/web_field_required_invisible_manager/readme/DESCRIPTION.md new file mode 100644 index 000000000000..2540acbb76f4 --- /dev/null +++ b/web_field_required_invisible_manager/readme/DESCRIPTION.md @@ -0,0 +1,5 @@ +This module allows to set a field required, invisible or readonly for +users belonging to a specific group. + +The field can be required, invisible or readonly in any case, or +according to specific conditions. diff --git a/web_field_required_invisible_manager/readme/ROADMAP.md b/web_field_required_invisible_manager/readme/ROADMAP.md new file mode 100644 index 000000000000..ab7d6767ab83 --- /dev/null +++ b/web_field_required_invisible_manager/readme/ROADMAP.md @@ -0,0 +1,4 @@ +When one model inherits another (e.g. res.users inherits res.partner) +and new custom field is created, then same field is created for +inheriting model as well, which should be avoided or at least duplicated +field should be also manual, but created as base. diff --git a/web_field_required_invisible_manager/readme/USAGE.md b/web_field_required_invisible_manager/readme/USAGE.md new file mode 100644 index 000000000000..59db3384cc86 --- /dev/null +++ b/web_field_required_invisible_manager/readme/USAGE.md @@ -0,0 +1,10 @@ +Go to Settings \> Technical \> Models + +Select a model \> in tab "Custom required fields", "Custom invisible +fields" or "Custom readonly fields" add a line + +Select a field, add one or more group and enable flag "Required", +"Invisible" or "Readonly" + +If needed, set a condition for which the field should be required, +invisible or readonly for users of those groups. diff --git a/web_field_required_invisible_manager/security/ir.model.access.csv b/web_field_required_invisible_manager/security/ir.model.access.csv new file mode 100644 index 000000000000..28c2198ec80b --- /dev/null +++ b/web_field_required_invisible_manager/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +cfr1,cfr1,model_custom_field_restriction,base.group_system,1,1,1,1 +cfr2,cfr2,model_custom_field_restriction,base.group_user,1,0,0,0 diff --git a/web_field_required_invisible_manager/static/description/icon.png b/web_field_required_invisible_manager/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/web_field_required_invisible_manager/static/description/icon.png differ diff --git a/web_field_required_invisible_manager/static/description/index.html b/web_field_required_invisible_manager/static/description/index.html new file mode 100644 index 000000000000..f557a5230c4c --- /dev/null +++ b/web_field_required_invisible_manager/static/description/index.html @@ -0,0 +1,451 @@ + + + + + +Web Field Required Invisible Readonly Managerr + + + +
+

Web Field Required Invisible Readonly Managerr

+ + +

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

+

This module allows to set a field required, invisible or readonly for +users belonging to a specific group.

+

The field can be required, invisible or readonly in any case, or +according to specific conditions.

+

Table of contents

+ +
+

Usage

+

Go to Settings > Technical > Models

+

Select a model > in tab “Custom required fields”, “Custom invisible +fields” or “Custom readonly fields” add a line

+

Select a field, add one or more group and enable flag “Required”, +“Invisible” or “Readonly”

+

If needed, set a condition for which the field should be required, +invisible or readonly for users of those groups.

+
+
+

Known issues / Roadmap

+

When one model inherits another (e.g. res.users inherits res.partner) +and new custom field is created, then same field is created for +inheriting model as well, which should be avoided or at least duplicated +field should be also manual, but created as base.

+
+
+

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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Ilyas
  • +
  • ooops404
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +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.

+

Current maintainer:

+

ilyasProgrammer

+

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_field_required_invisible_manager/tests/__init__.py b/web_field_required_invisible_manager/tests/__init__.py new file mode 100644 index 000000000000..487f25a271e5 --- /dev/null +++ b/web_field_required_invisible_manager/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_web_field_required_invisible_manager diff --git a/web_field_required_invisible_manager/tests/test_web_field_required_invisible_manager.py b/web_field_required_invisible_manager/tests/test_web_field_required_invisible_manager.py new file mode 100644 index 000000000000..a3d26eb58c16 --- /dev/null +++ b/web_field_required_invisible_manager/tests/test_web_field_required_invisible_manager.py @@ -0,0 +1,231 @@ +# Copyright 2023 ooops404 +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import Form, common + + +class TestFieldRequiredIvisibleManager(common.SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.view_model = cls.env["ir.ui.view"].sudo() + cls.partner_name_field_id = cls.env["ir.model.fields"].search( + [("model", "=", "res.partner"), ("name", "=", "name")] + ) + cls.partner_id_field_id = cls.env["ir.model.fields"].search( + [("model", "=", "res.partner"), ("name", "=", "name")] + ) + cls.partner_model_id = cls.partner_name_field_id.model_id + cls.partner_title_model_id = cls.env["ir.model"].search( + [("model", "=", "res.partner.title")] + ) + cls.partner_title_name_field_id = cls.env["ir.model.fields"].search( + [("model", "=", "res.partner.title"), ("name", "=", "name")] + ) + cls.partner_name_field_id = cls.env["ir.model.fields"].search( + [("model", "=", "res.partner"), ("name", "=", "name")] + ) + cls.partner_title_shortcut_field_id = cls.env["ir.model.fields"].search( + [("model", "=", "res.partner.title"), ("name", "=", "shortcut")] + ) + cls.invisible_title_rec_id = cls.env["custom.field.restriction"].create( + { + "invisible_model_id": cls.partner_title_model_id.id, + "field_id": cls.partner_title_shortcut_field_id.id, + "group_ids": [(6, 0, cls.env.ref("base.group_user").ids)], + "field_invisible": True, + } + ) + cls.invisible_rec_id = cls.env["custom.field.restriction"].create( + { + "invisible_model_id": cls.partner_model_id.id, + "field_id": cls.partner_id_field_id.id, + "group_ids": [(6, 0, cls.env.ref("base.group_user").ids)], + "field_invisible": True, + "condition_domain": "[('id', '<', 0)]", + } + ) + cls.required_rec_id = cls.env["custom.field.restriction"].create( + { + "required_model_id": cls.partner_model_id.id, + "field_id": cls.partner_id_field_id.id, + "group_ids": [(6, 0, cls.env.ref("base.group_user").ids)], + "required": True, + "condition_domain": "[('id', '>', 0)]", + } + ) + cls.readonly_rec_id = cls.env["custom.field.restriction"].create( + { + "readonly_model_id": cls.partner_model_id.id, + "field_id": cls.partner_id_field_id.id, + "group_ids": [(6, 0, cls.env.ref("base.group_user").ids)], + "field_readonly": True, + "condition_domain": "[('id', '>', 0)]", + } + ) + cls.res_partner_title_madam = cls.env.ref("base.res_partner_title_madam") + cls.deco_addict = cls.env.ref("base.res_partner_2") + cls.partner_form_view = cls.env.ref("base.view_partner_simple_form") + cls.partner_tree_view = cls.env.ref("base.view_partner_tree") + cls.view_partner_title_form = cls.env.ref("base.view_partner_title_form") + cls.view_partner_title_tree = cls.env.ref("base.view_partner_title_tree") + cls.users_model = cls.env["ir.model"]._get("res.users") + cls.view_users_form = cls.env.ref("base.view_users_form") + cls.view_users_tree = cls.env.ref("base.view_users_tree") + + cls.company_model_id = cls.env["ir.model"]._get("res.company") + cls.company_name_field = cls.env["ir.model.fields"].search( + [("model", "=", "res.company"), ("name", "=", "name")] + ) + cls.company_street_field = cls.env["ir.model.fields"].search( + [("model", "=", "res.company"), ("name", "=", "street")] + ) + cls.company_city_field = cls.env["ir.model.fields"].search( + [("model", "=", "res.company"), ("name", "=", "city")] + ) + cls.company_name_readonly = cls.env["custom.field.restriction"].create( + { + "readonly_model_id": cls.company_model_id.id, + "field_id": cls.company_name_field.id, + "group_ids": [(6, 0, cls.env.ref("base.group_user").ids)], + "field_readonly": True, + "condition_domain": "[('id', '>', 0)]", + } + ) + cls.company_street_required = cls.env["custom.field.restriction"].create( + { + "required_model_id": cls.company_model_id.id, + "field_id": cls.company_street_field.id, + "group_ids": [(6, 0, cls.env.ref("base.group_user").ids)], + "required": True, + "condition_domain": "[('id', '>', 0)]", + } + ) + cls.company_city_invisible = cls.env["custom.field.restriction"].create( + { + "invisible_model_id": cls.company_model_id.id, + "field_id": cls.company_city_field.id, + "group_ids": [(6, 0, cls.env.ref("base.group_user").ids)], + "field_invisible": True, + "condition_domain": "[('id', '>', 0)]", + } + ) + cls.company_tree_view = cls.env["ir.ui.view"].create( + { + "name": "Test Tree", + "model": "res.company", + "type": "tree", + "mode": "primary", + "arch": "" + "" + "", + } + ) + cls.view_users_form_arch_orig = cls.view_users_form.arch + cls.view_users_form.arch = cls.view_users_form.arch.replace( + "", + "" + "" + "", + ) + + def test_model_form(self): + """onchange should set correct model""" + + partner_form_view = self.view_model.default_view( + self.partner_model_id._name, "form" + ) + with Form(self.partner_model_id, view=partner_form_view) as partner_form: + with partner_form.custom_required_restriction_ids.new() as new_line: + self.assertTrue(new_line.required_model_id) + new_line.field_id = self.partner_name_field_id + with partner_form.custom_readonly_restriction_ids.new() as new_line: + self.assertTrue(new_line.readonly_model_id) + new_line.field_id = self.partner_name_field_id + with partner_form.custom_invisible_restriction_ids.new() as new_line: + self.assertTrue(new_line.invisible_model_id) + new_line.field_id = self.partner_name_field_id + + def test_relational_fields(self): + """res.users has company_ids field. It has restrictions. + Restrictions should be added into company list view""" + user = self.env["res.users"].search([], limit=1) + arch = user.fields_view_get(view_id=self.view_users_form.id, view_type="form") + sub_arch = arch["fields"]["company_ids"]["views"]["tree"]["arch"] + self.assertIn("x_computed_res_company_name_readonly", sub_arch) + self.assertIn("x_computed_res_company_street_required", sub_arch) + self.assertIn("x_computed_res_company_city_visibility", sub_arch) + + def test_all_web_field_required_invisible_manager(self): + # related fields are created + self.assertTrue(self.invisible_rec_id.visibility_field_id) + self.assertTrue(self.required_rec_id.required_field_id) + self.assertTrue(self.readonly_rec_id.readonly_field_id) + # _compute_model_name() + self.invisible_rec_id._compute_model_name() + self.assertEqual(self.invisible_rec_id.model_name, "res.partner") + self.required_rec_id._compute_model_name() + self.assertEqual(self.required_rec_id.model_name, "res.partner") + # fields_view_get() + self.deco_addict.fields_view_get( + view_id=self.partner_form_view.id, view_type="form" + ) + self.deco_addict.fields_view_get( + view_id=self.partner_tree_view.id, view_type="tree" + ) + self.res_partner_title_madam.fields_view_get( + view_id=self.view_partner_title_form.id, view_type="form" + ) + self.res_partner_title_madam.fields_view_get( + view_id=self.view_partner_title_tree.id, view_type="tree" + ) + self.env.user.fields_view_get(view_id=self.view_users_form.id, view_type="form") + self.env.user.fields_view_get(view_id=self.view_users_tree.id, view_type="tree") + # read + self.deco_addict.read( + [ + "id", + "name", + "x_computed_res_partner_name_readonly", + "x_computed_res_partner_name_required", + "x_computed_res_partner_name_visibility", + ] + ) + self.res_partner_title_madam.read( + ["id", "name", "x_computed_res_partner_title_shortcut_visibility"] + ) + self.env.user.read(["id", "name"]) + self.env.user._compute_restrictions_fields() + # computed value + self.assertTrue(self.deco_addict.x_computed_res_partner_name_readonly) + self.assertTrue(self.deco_addict.x_computed_res_partner_name_required) + self.assertFalse(self.deco_addict.x_computed_res_partner_name_visibility) + self.assertTrue( + self.res_partner_title_madam.x_computed_res_partner_title_shortcut_visibility + ) + # change domain, reset cache. Should be True now + self.invisible_rec_id.condition_domain = "[('id', '>', 0)]" + self.deco_addict.invalidate_cache() + self.deco_addict.read(["x_computed_res_partner_name_visibility"]) + self.assertTrue(self.deco_addict.x_computed_res_partner_name_visibility) + # default get + self.env["res.partner"].default_get(["name"]) + self.env["res.partner"].default_get(["city"]) + self.env["res.partner.title"].default_get(["name"]) + self.env["res.partner.title"].default_get(["shortcut"]) + # onchange_field_id() + self.assertFalse(self.invisible_title_rec_id.required) + self.invisible_title_rec_id.field_id = self.partner_title_name_field_id + self.invisible_title_rec_id.required_model_id = self.partner_model_id + self.invisible_title_rec_id._compute_model_name() + self.assertTrue(self.invisible_title_rec_id.required) + # unlink + self.invisible_rec_id.unlink() + # case when child view has no fields + self.view_users_form.arch = self.view_users_form_arch_orig + self.view_users_form.arch = self.view_users_form.arch.replace( + "", + "

", + ) + self.env.user.fields_view_get(view_id=self.view_users_form.id, view_type="form") + self.env.user.fields_view_get(view_id=self.view_users_tree.id, view_type="tree") diff --git a/web_field_required_invisible_manager/views/views.xml b/web_field_required_invisible_manager/views/views.xml new file mode 100644 index 000000000000..d602776e7447 --- /dev/null +++ b/web_field_required_invisible_manager/views/views.xml @@ -0,0 +1,312 @@ + + + + view.model.form.inherit + ir.model + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + custom.field.restriction.all.view.form + custom.field.restriction + +
+ +
+

+ Required Field Rule

+

+ Invisible Field Rule

+

+ Readonly Field Rule

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + custom.field.restriction.required.view.list + custom.field.restriction + + + + + + + + + + + + + + + + + custom.field.restriction.invisible.view.list + custom.field.restriction + + + + + + + + + + + + + + + + custom.field.restriction.readonly.view.list + custom.field.restriction + + + + + + + + + + + + + + + + custom.field.restriction.all.view.list + custom.field.restriction + + + + + + + + + + + + + + + + + + + + + + + + Required Fields + custom.field.restriction + list,form + [('required', '=', True)] + {'show_required_form': True} + + + + + Invisible Fields + custom.field.restriction + list,form + [('field_invisible', '=', True)] + {'show_invisible_form': True} + + + + + Readonly Fields + custom.field.restriction + list,form + [('field_readonly', '=', True)] + {'show_readonly_form': True} + + + + + All Fields Restrictions + custom.field.restriction + list,form + {'show_readonly_form': True, 'show_invisible_form': True, 'show_required_form': True, 'hide_title': True} + + + + + + + + + + + + + +