diff --git a/README.md b/README.md index 793fdd8e84..c548606ba1 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ Available addons ---------------- addon | version | maintainers | summary --- | --- | --- | --- +[base_cancel_confirm](base_cancel_confirm/) | 19.0.1.0.1 | kittiu | Base Cancel Confirm +[base_revision](base_revision/) | 19.0.1.0.0 | | Keep track of revised document +[base_search_custom_field_filter](base_search_custom_field_filter/) | 19.0.1.0.0 | pedrobaeza | Add custom filters for fields via UI +[base_substate](base_substate/) | 19.0.1.0.1 | | Base Sub State +[base_technical_features](base_technical_features/) | 19.0.1.0.0 | | Access to technical features without activating debug mode [date_range](date_range/) | 19.0.1.0.0 | lmignon | Manage all kind of date range [//]: # (end addons) diff --git a/base_cancel_confirm/README.rst b/base_cancel_confirm/README.rst new file mode 100644 index 0000000000..bd989a81c6 --- /dev/null +++ b/base_cancel_confirm/README.rst @@ -0,0 +1,119 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +=================== +Base Cancel Confirm +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:7fe4d781b8346a73ecf494e27b27fad01b51c94376e0d1825f3a777d5b37fe88 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/19.0/base_cancel_confirm + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-19-0/server-ux-19-0-base_cancel_confirm + :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/server-ux&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Many document model that already has cancel action may also want a +confirm dialog with option to provide reason. + +This module does not provide a functionality by itself but an abstract +model to easily implement a confirm with reason wizard when cancel +button is clicked. If reason is provided, it will be visible in form +view. + +**Note:** To be able to use this module in a new model you will need +some development. + +You can see implementation example as followings, + +- `sale_cancel_confirm `__ +- `purchase_cancel_confirm `__ +- `purchase_request_cancel_confirm `__ +- `account_move_cancel_confirm `__ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +By default, the cancel confirm will be disabled (to ensure no side +effect on other module unit test) + +To enable cancel confirm wizard, please add System Parameter +(ir.config_parameter) for each extended module. + +For example, + +- sale_cancel_confirm, add sale.order.cancel_confirm_disable = False +- purchase_cancel_confirm, add purchase.order.cancel_confirm_disable = + False + +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 +------- + +* Ecosoft + +Contributors +------------ + +- Kitti U. + +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-kittiu| image:: https://github.com/kittiu.png?size=40px + :target: https://github.com/kittiu + :alt: kittiu + +Current `maintainer `__: + +|maintainer-kittiu| + +This module is part of the `OCA/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_cancel_confirm/__init__.py b/base_cancel_confirm/__init__.py new file mode 100644 index 0000000000..8a240a4d67 --- /dev/null +++ b/base_cancel_confirm/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import wizard +from . import model diff --git a/base_cancel_confirm/__manifest__.py b/base_cancel_confirm/__manifest__.py new file mode 100644 index 0000000000..063ccc9516 --- /dev/null +++ b/base_cancel_confirm/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Base Cancel Confirm", + "version": "19.0.1.0.1", + "author": "Ecosoft,Odoo Community Association (OCA)", + "category": "Usability", + "license": "AGPL-3", + "website": "https://github.com/OCA/server-ux", + "depends": ["base"], + "data": [ + "wizard/cancel_confirm.xml", + "security/ir.model.access.csv", + "views/cancel_confirm_template.xml", + ], + "auto_install": False, + "installable": True, + "maintainers": ["kittiu"], +} diff --git a/base_cancel_confirm/i18n/base_cancel_confirm.pot b/base_cancel_confirm/i18n/base_cancel_confirm.pot new file mode 100644 index 0000000000..de502d61f3 --- /dev/null +++ b/base_cancel_confirm/i18n/base_cancel_confirm.pot @@ -0,0 +1,125 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_cancel_confirm +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "A flag signify that this document is confirmed for cancellation" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +msgid "An optional cancel reason" +msgstr "" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Are you sure to cancel this document?" +msgstr "" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model,name:base_cancel_confirm.model_cancel_confirm +msgid "Cancel Confirm" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.actions.act_window,name:base_cancel_confirm.action_cancel_confirm_wizard +#: model:ir.model,name:base_cancel_confirm.model_base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel Confirmation" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "Cancel Confirmed" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__cancel_reason +msgid "Cancel Reason" +msgstr "" + +#. module: base_cancel_confirm +#. odoo-python +#: code:addons/base_cancel_confirm/model/base_cancel_confirm.py:0 +msgid "Configuration Error (%s), should be 'True' or 'False'" +msgstr "" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Confirm" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_uid +msgid "Created by" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_date +msgid "Created on" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__display_name +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__display_name +msgid "Display Name" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__has_cancel_reason +msgid "Has Cancel Reason" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__id +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__id +msgid "ID" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_date +msgid "Last Updated on" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__no +msgid "None" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__optional +msgid "Optional" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__required +msgid "Required" +msgstr "" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "or" +msgstr "" diff --git a/base_cancel_confirm/i18n/es.po b/base_cancel_confirm/i18n/es.po new file mode 100644 index 0000000000..b58484e3f0 --- /dev/null +++ b/base_cancel_confirm/i18n/es.po @@ -0,0 +1,130 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_cancel_confirm +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-11 15:33+0000\n" +"Last-Translator: Ivorra78 \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" +"X-Generator: Weblate 4.17\n" + +#. module: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "A flag signify that this document is confirmed for cancellation" +msgstr "" +"Una bandera significa que este documento está confirmado para su anulación" + +#. module: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +msgid "An optional cancel reason" +msgstr "Un motivo de cancelación opcional" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Are you sure to cancel this document?" +msgstr "¿Está seguro de cancelar este documento?" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel" +msgstr "Cancelar" + +#. module: base_cancel_confirm +#: model:ir.model,name:base_cancel_confirm.model_cancel_confirm +msgid "Cancel Confirm" +msgstr "Cancelar Confirmar" + +#. module: base_cancel_confirm +#: model:ir.actions.act_window,name:base_cancel_confirm.action_cancel_confirm_wizard +#: model:ir.model,name:base_cancel_confirm.model_base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel Confirmation" +msgstr "Cancelar Confirmación" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "Cancel Confirmed" +msgstr "Cancelar Confirmado" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__cancel_reason +msgid "Cancel Reason" +msgstr "Cancelar Razón" + +#. module: base_cancel_confirm +#. odoo-python +#: code:addons/base_cancel_confirm/model/base_cancel_confirm.py:0 +msgid "Configuration Error (%s), should be 'True' or 'False'" +msgstr "Error de configuración (%s), debe ser 'True' o 'False'" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Confirm" +msgstr "Confirmar" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__has_cancel_reason +msgid "Has Cancel Reason" +msgstr "Tiene Motivo de Cancelación" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__id +msgid "ID" +msgstr "ID (identificación)" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_date +msgid "Last Updated on" +msgstr "Última Actualización el" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__no +msgid "None" +msgstr "Ninguno/a" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__optional +msgid "Optional" +msgstr "Opcional" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__required +msgid "Required" +msgstr "Requerido" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "or" +msgstr "o" + +#~ msgid "Last Modified on" +#~ msgstr "Última Modifiación el" diff --git a/base_cancel_confirm/i18n/it.po b/base_cancel_confirm/i18n/it.po new file mode 100644 index 0000000000..598a2ca773 --- /dev/null +++ b/base_cancel_confirm/i18n/it.po @@ -0,0 +1,129 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_cancel_confirm +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-12-31 19:35+0000\n" +"Last-Translator: mymage \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: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "A flag signify that this document is confirmed for cancellation" +msgstr "Una spunta indica che questo documento è confermato per l'annullamento" + +#. module: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +msgid "An optional cancel reason" +msgstr "Un motivo di annullamento opzionale" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Are you sure to cancel this document?" +msgstr "Sicuri di annullare questo documento?" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel" +msgstr "Annulla" + +#. module: base_cancel_confirm +#: model:ir.model,name:base_cancel_confirm.model_cancel_confirm +msgid "Cancel Confirm" +msgstr "Annulla conferma" + +#. module: base_cancel_confirm +#: model:ir.actions.act_window,name:base_cancel_confirm.action_cancel_confirm_wizard +#: model:ir.model,name:base_cancel_confirm.model_base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel Confirmation" +msgstr "Annulla conferma" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "Cancel Confirmed" +msgstr "Annullamento confermato" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__cancel_reason +msgid "Cancel Reason" +msgstr "Causale annullamento" + +#. module: base_cancel_confirm +#. odoo-python +#: code:addons/base_cancel_confirm/model/base_cancel_confirm.py:0 +msgid "Configuration Error (%s), should be 'True' or 'False'" +msgstr "Errore configurazione (%s), deve essere 'True' o 'False'" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Confirm" +msgstr "Conferma" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__has_cancel_reason +msgid "Has Cancel Reason" +msgstr "Ha causale annullamento" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__id +msgid "ID" +msgstr "ID" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__no +msgid "None" +msgstr "Nessuna" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__optional +msgid "Optional" +msgstr "Opzionale" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__required +msgid "Required" +msgstr "Richiesta" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "or" +msgstr "o" + +#~ msgid "Last Modified on" +#~ msgstr "Ultima modifica il" diff --git a/base_cancel_confirm/i18n/nl.po b/base_cancel_confirm/i18n/nl.po new file mode 100644 index 0000000000..f8e6897d03 --- /dev/null +++ b/base_cancel_confirm/i18n/nl.po @@ -0,0 +1,129 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_cancel_confirm +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-04-18 10:46+0000\n" +"Last-Translator: Bosd \n" +"Language-Team: none\n" +"Language: nl\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.3.2\n" + +#. module: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "A flag signify that this document is confirmed for cancellation" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,help:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +msgid "An optional cancel reason" +msgstr "" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Are you sure to cancel this document?" +msgstr "" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel" +msgstr "Annuleer" + +#. module: base_cancel_confirm +#: model:ir.model,name:base_cancel_confirm.model_cancel_confirm +msgid "Cancel Confirm" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.actions.act_window,name:base_cancel_confirm.action_cancel_confirm_wizard +#: model:ir.model,name:base_cancel_confirm.model_base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Cancel Confirmation" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_confirm +msgid "Cancel Confirmed" +msgstr "" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_base_cancel_confirm__cancel_reason +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__cancel_reason +msgid "Cancel Reason" +msgstr "Annuleringsreden" + +#. module: base_cancel_confirm +#. odoo-python +#: code:addons/base_cancel_confirm/model/base_cancel_confirm.py:0 +msgid "Configuration Error (%s), should be 'True' or 'False'" +msgstr "" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "Confirm" +msgstr "Bevestig" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_uid +msgid "Created by" +msgstr "Aangemaakt door" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__create_date +msgid "Created on" +msgstr "Aangemaakt op" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__display_name +msgid "Display Name" +msgstr "Weergavenaam" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__has_cancel_reason +msgid "Has Cancel Reason" +msgstr "Heeft annuleringsreden" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__id +msgid "ID" +msgstr "ID" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_uid +msgid "Last Updated by" +msgstr "Laatst bijgewerkt door" + +#. module: base_cancel_confirm +#: model:ir.model.fields,field_description:base_cancel_confirm.field_cancel_confirm__write_date +msgid "Last Updated on" +msgstr "Laatst bijgewerkt op" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__no +msgid "None" +msgstr "Geen" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__optional +msgid "Optional" +msgstr "Optioneel" + +#. module: base_cancel_confirm +#: model:ir.model.fields.selection,name:base_cancel_confirm.selection__cancel_confirm__has_cancel_reason__required +msgid "Required" +msgstr "Vereist" + +#. module: base_cancel_confirm +#: model_terms:ir.ui.view,arch_db:base_cancel_confirm.view_cancel_confirm_wizard +msgid "or" +msgstr "of" + +#~ msgid "Last Modified on" +#~ msgstr "Laatst bijgewerk op" diff --git a/base_cancel_confirm/model/__init__.py b/base_cancel_confirm/model/__init__.py new file mode 100644 index 0000000000..6a575eedfe --- /dev/null +++ b/base_cancel_confirm/model/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import base_cancel_confirm diff --git a/base_cancel_confirm/model/base_cancel_confirm.py b/base_cancel_confirm/model/base_cancel_confirm.py new file mode 100644 index 0000000000..3b4e5eeea7 --- /dev/null +++ b/base_cancel_confirm/model/base_cancel_confirm.py @@ -0,0 +1,82 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import inspect + +from lxml import etree + +from odoo import fields, models, tools +from odoo.exceptions import ValidationError +from odoo.tools.misc import frozendict + + +class BaseCancelConfirm(models.AbstractModel): + _name = "base.cancel.confirm" + _description = "Cancel Confirmation" + + _has_cancel_reason = "no" # ["no", "optional", "required"] + _cancel_reason_xpath = "/form/sheet/group[last()]" + + cancel_confirm = fields.Boolean( + string="Cancel Confirmed", + default=lambda self: self._cancel_confirm_disabled(), + copy=False, + help="A flag signify that this document is confirmed for cancellation", + ) + cancel_reason = fields.Text( + copy=False, + help="An optional cancel reason", + ) + + def _cancel_confirm_disabled(self): + key = f"{self._name}.cancel_confirm_disable" + res = self.env["ir.config_parameter"].sudo().get_param(key) + if not res: + return True + if res not in ("True", "False"): + raise ValidationError( + self.env._("Configuration Error (%s), should be 'True' or 'False'", key) + ) + return tools.str2bool(res) + + def open_cancel_confirm_wizard(self): + xmlid = "base_cancel_confirm.action_cancel_confirm_wizard" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["context"] = { + "cancel_res_model": self._name, + "cancel_res_ids": self.ids, + "cancel_method": inspect.stack()[1][3], + "default_has_cancel_reason": self._has_cancel_reason, + } + return action + + def clear_cancel_confirm_data(self): + self.write({"cancel_confirm": False, "cancel_reason": False}) + + def get_view(self, view_id=None, view_type="form", **options): + res = super().get_view(view_id=view_id, view_type=view_type, **options) + if view_type == "form": + View = self.env["ir.ui.view"] + if view_id and res.get("base_model", self._name) != self._name: + View = View.with_context(base_model_name=res["base_model"]) + doc = etree.XML(res["arch"]) + all_models = res["models"].copy() + for node in doc.xpath(self._cancel_reason_xpath): + str_element = self.env["ir.qweb"]._render( + "base_cancel_confirm.cancel_reason_template" + ) + new_node = etree.fromstring(str_element) + new_arch, new_models = View.postprocess_and_fields(new_node, self._name) + new_node = etree.fromstring(new_arch) + for new_element in new_node: + node.addnext(new_element) + for model in new_models: + if model in all_models: + # Merge field names: existing fields (tuple) + new fields (set) + all_models[model] = tuple( + set(all_models[model]) | new_models[model] + ) + else: + all_models[model] = tuple(new_models[model]) + res["arch"] = etree.tostring(doc) + res["models"] = frozendict(all_models) + return res diff --git a/base_cancel_confirm/pyproject.toml b/base_cancel_confirm/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/base_cancel_confirm/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_cancel_confirm/readme/CONFIGURE.md b/base_cancel_confirm/readme/CONFIGURE.md new file mode 100644 index 0000000000..97654ecf42 --- /dev/null +++ b/base_cancel_confirm/readme/CONFIGURE.md @@ -0,0 +1,11 @@ +By default, the cancel confirm will be disabled (to ensure no side +effect on other module unit test) + +To enable cancel confirm wizard, please add System Parameter +(ir.config_parameter) for each extended module. + +For example, + +- sale_cancel_confirm, add sale.order.cancel_confirm_disable = False +- purchase_cancel_confirm, add purchase.order.cancel_confirm_disable = + False diff --git a/base_cancel_confirm/readme/CONTRIBUTORS.md b/base_cancel_confirm/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..db1787028b --- /dev/null +++ b/base_cancel_confirm/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Kitti U. \<\> diff --git a/base_cancel_confirm/readme/DESCRIPTION.md b/base_cancel_confirm/readme/DESCRIPTION.md new file mode 100644 index 0000000000..ddb1105444 --- /dev/null +++ b/base_cancel_confirm/readme/DESCRIPTION.md @@ -0,0 +1,17 @@ +Many document model that already has cancel action may also want a +confirm dialog with option to provide reason. + +This module does not provide a functionality by itself but an abstract +model to easily implement a confirm with reason wizard when cancel +button is clicked. If reason is provided, it will be visible in form +view. + +**Note:** To be able to use this module in a new model you will need +some development. + +You can see implementation example as followings, + +- [sale_cancel_confirm](https://github.com/OCA/sale-workflow) +- [purchase_cancel_confirm](https://github.com/OCA/purchase-workflow) +- [purchase_request_cancel_confirm](https://github.com/OCA/purchase-workflow) +- [account_move_cancel_confirm](https://github.com/OCA/account-invoicing) diff --git a/base_cancel_confirm/security/ir.model.access.csv b/base_cancel_confirm/security/ir.model.access.csv new file mode 100644 index 0000000000..b798d0c1a4 --- /dev/null +++ b/base_cancel_confirm/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_cancel_confirm,access_cancel_confirm,model_cancel_confirm,base.group_user,1,1,1,1 diff --git a/base_cancel_confirm/static/description/icon.png b/base_cancel_confirm/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/base_cancel_confirm/static/description/icon.png differ diff --git a/base_cancel_confirm/static/description/index.html b/base_cancel_confirm/static/description/index.html new file mode 100644 index 0000000000..4e42e88197 --- /dev/null +++ b/base_cancel_confirm/static/description/index.html @@ -0,0 +1,459 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Base Cancel Confirm

+ +

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

+

Many document model that already has cancel action may also want a +confirm dialog with option to provide reason.

+

This module does not provide a functionality by itself but an abstract +model to easily implement a confirm with reason wizard when cancel +button is clicked. If reason is provided, it will be visible in form +view.

+

Note: To be able to use this module in a new model you will need +some development.

+

You can see implementation example as followings,

+ +

Table of contents

+ +
+

Configuration

+

By default, the cancel confirm will be disabled (to ensure no side +effect on other module unit test)

+

To enable cancel confirm wizard, please add System Parameter +(ir.config_parameter) for each extended module.

+

For example,

+
    +
  • sale_cancel_confirm, add sale.order.cancel_confirm_disable = False
  • +
  • purchase_cancel_confirm, add purchase.order.cancel_confirm_disable = +False
  • +
+
+
+

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

+
    +
  • Ecosoft
  • +
+
+ +
+

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:

+

kittiu

+

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

+

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

+
+
+
+
+ + diff --git a/base_cancel_confirm/tests/__init__.py b/base_cancel_confirm/tests/__init__.py new file mode 100644 index 0000000000..de24118a3d --- /dev/null +++ b/base_cancel_confirm/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_cancel_confirm diff --git a/base_cancel_confirm/tests/cancel_confirm_tester.py b/base_cancel_confirm/tests/cancel_confirm_tester.py new file mode 100644 index 0000000000..c18dc31e4b --- /dev/null +++ b/base_cancel_confirm/tests/cancel_confirm_tester.py @@ -0,0 +1,34 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class CancelConfirmTester(models.Model): + _name = "cancel.confirm.tester" + _description = "Cancel Confirm Tester" + _inherit = ["base.cancel.confirm"] + + _has_cancel_reason = "optional" + + name = fields.Char() + state = fields.Selection( + selection=[ + ("draft", "Draft"), + ("confirmed", "Confirmed"), + ("cancel", "Cancel"), + ], + default="draft", + ) + + def action_confirm(self): + self.write({"state": "confirmed"}) + + def action_cancel(self): + if not self.filtered("cancel_confirm"): + return self.open_cancel_confirm_wizard() + self.write({"state": "cancel"}) + + def action_draft(self): + self.clear_cancel_confirm_data() + self.write({"state": "draft"}) diff --git a/base_cancel_confirm/tests/test_cancel_confirm.py b/base_cancel_confirm/tests/test_cancel_confirm.py new file mode 100644 index 0000000000..4937ea7937 --- /dev/null +++ b/base_cancel_confirm/tests/test_cancel_confirm.py @@ -0,0 +1,108 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from lxml import etree + +from odoo.orm.model_classes import add_to_registry +from odoo.tests import Form, common, tagged + + +@tagged("post_install", "-at_install") +class TestCancelConfirm(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + from .cancel_confirm_tester import CancelConfirmTester + + add_to_registry(cls.registry, CancelConfirmTester) + cls.registry._setup_models__(cls.env.cr, [CancelConfirmTester._name]) + cls.registry.init_models( + cls.env.cr, [CancelConfirmTester._name], {"models_to_check": True} + ) + cls.test_model = cls.env[CancelConfirmTester._name] + cls.tester_model = cls.env["ir.model"].search( + [("model", "=", "cancel.confirm.tester")] + ) + cls.env["ir.config_parameter"].create( + {"key": "cancel.confirm.tester.cancel_confirm_disable", "value": "False"} + ) + # Access record: + cls.env["ir.model.access"].create( + { + "name": "access.cancel.confirm.tester", + "model_id": cls.tester_model.id, + "perm_read": 1, + "perm_write": 1, + "perm_create": 1, + "perm_unlink": 1, + } + ) + + cls.test_record = cls.test_model.create({"name": "DOC-001"}) + + @classmethod + def tearDownClass(cls): + cls.addClassCleanup(cls.registry.__delitem__, "cancel.confirm.tester") + return super().tearDownClass() + + def test_01_cancel_confirm_tester(self): + """Cancel a document, I expect cancel_reason. + Then, set to draft, I expect cancel_reason is deleted. + """ + self.test_record.action_confirm() + # Click cance, cancel confirm wizard will open. Type in cancel_reason + res = self.test_record.action_cancel() + ctx = res.get("context") + self.assertEqual(ctx["cancel_method"], "action_cancel") + self.assertEqual(ctx["default_has_cancel_reason"], "optional") + wizard = Form(self.env["cancel.confirm"].with_context(**ctx)) + wizard.cancel_reason = "Wrong information" + wiz = wizard.save() + # Confirm cancel on wizard + wiz.confirm_cancel() + self.assertEqual(self.test_record.cancel_reason, wizard.cancel_reason) + self.assertEqual(self.test_record.state, "cancel") + # Set to draft + self.test_record.action_draft() + self.assertEqual(self.test_record.cancel_reason, False) + self.assertEqual(self.test_record.state, "draft") + # Check set no cancel reason, reason should be False + # wizard.has_cancel_reason = "no" Invisible field cant write + wiz = wizard.save() + # Confirm cancel on wizard + wiz.confirm_cancel() + self.assertTrue(self.test_record.cancel_reason) + + def test_02_cancel_confirm_tester(self): + self.test_record.action_confirm() + res = self.test_record.action_cancel() + ctx = res.get("context") + # Check set no cancel reason, reason should be "no" + ctx["default_has_cancel_reason"] = "no" + wizard = Form(self.env["cancel.confirm"].with_context(**ctx)) + self.assertEqual(wizard.has_cancel_reason, "no") + wiz = wizard.save() + # Confirm cancel on wizard + wiz.confirm_cancel() + self.test_record.action_confirm() + self.assertFalse(self.test_record.cancel_reason) + + def test_view_automatic(self): + # We need to add a view in order to test fields_view_get() + self.env["ir.ui.view"].create( + { + "model": self.test_record._name, + "name": "Demo view", + "arch": """
+ + + + + +
""", + } + ) + view = self.env[self.test_record._name].get_view(False, "form") + form = etree.fromstring(view["arch"]) + self.assertTrue(form.xpath("//field[@name='cancel_confirm']")) + self.assertTrue(form.xpath("//field[@name='cancel_reason']")) diff --git a/base_cancel_confirm/views/cancel_confirm_template.xml b/base_cancel_confirm/views/cancel_confirm_template.xml new file mode 100644 index 0000000000..98e6d1005d --- /dev/null +++ b/base_cancel_confirm/views/cancel_confirm_template.xml @@ -0,0 +1,11 @@ + + + + diff --git a/base_cancel_confirm/wizard/__init__.py b/base_cancel_confirm/wizard/__init__.py new file mode 100644 index 0000000000..38920ac7c0 --- /dev/null +++ b/base_cancel_confirm/wizard/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import cancel_confirm diff --git a/base_cancel_confirm/wizard/cancel_confirm.py b/base_cancel_confirm/wizard/cancel_confirm.py new file mode 100644 index 0000000000..4cc877128e --- /dev/null +++ b/base_cancel_confirm/wizard/cancel_confirm.py @@ -0,0 +1,32 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class CancelConfirm(models.TransientModel): + _name = "cancel.confirm" + _description = "Cancel Confirm" + + cancel_reason = fields.Text() + has_cancel_reason = fields.Selection( + selection=[ + ("no", "None"), + ("optional", "Optional"), + ("required", "Required"), + ], + default="no", + required=True, + ) + + def confirm_cancel(self): + self.ensure_one() + res_model = self.env.context.get("cancel_res_model") + res_ids = self.env.context.get("cancel_res_ids") + cancel_method = self.env.context.get("cancel_method") + docs = self.env[res_model].browse(res_ids) + docs.write({"cancel_confirm": True}) + # Cancel Reason + if self.has_cancel_reason in ["optional", "required"]: + docs.write({"cancel_reason": self.cancel_reason}) + res = getattr(docs, cancel_method)() + return res diff --git a/base_cancel_confirm/wizard/cancel_confirm.xml b/base_cancel_confirm/wizard/cancel_confirm.xml new file mode 100644 index 0000000000..27ed35e3b4 --- /dev/null +++ b/base_cancel_confirm/wizard/cancel_confirm.xml @@ -0,0 +1,38 @@ + + + + Cancel Confirmation + cancel.confirm + +
+

Are you sure to cancel this document?

+ + + + +
+
+
+
+
+ + Cancel Confirmation + ir.actions.act_window + cancel.confirm + form + + new + +
diff --git a/base_revision/README.rst b/base_revision/README.rst new file mode 100644 index 0000000000..c8e8fb72b0 --- /dev/null +++ b/base_revision/README.rst @@ -0,0 +1,123 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +======================== +Base Revision (abstract) +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d0e2197d0bb40144298439edb54c8636a285e61001bef3f78798e30200526314 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/19.0/base_revision + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-19-0/server-ux-19-0-base_revision + :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/server-ux&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Making revision(s) of a document is a common need across many area. + +This module does not provide a functionality by itself but an abstract +model to implement revision capality in other models (e.g. purchase +orders, sales orders, budgets, expenses...). + +**Note:** To be able to use this module in a new model you will need +some development. + +See `sale_order_revision `__ as an +example of implementation. + +Example with sale_order_revision installed, + +On a cancelled orders, you can click on the "New copy of Quotation" +button. This will create a new revision of the quotation, with the same +base number and a '-revno' suffix appended. A message is added in the +chatter saying that a new revision was created. + +In the form view, a new tab is added that lists the previous revisions, +with the date they were made obsolete and the user who performed the +action. + +The old revisions of a sale order are flagged as inactive, so they don't +clutter up searches. + +**Special Remarks:** Starting on version 14, this module was splitted +from sale_order_revision to, + +- base_revision +- sale_order_revision + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* Agile Business Group +* Dreambits +* Camptocamp +* Akretion +* Serpent Consulting Services Pvt. Ltd. +* Ecosoft + +Contributors +------------ + +- Devang Pipaliya +- Lorenzo Battistini +- Raphael Valyi +- Alexandre Fayolle +- Serpent Consulting Services Pvt. Ltd. +- Akim Juillerat +- Raf Ven +- Jeroen Evens +- Kitti U. + +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/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_revision/__init__.py b/base_revision/__init__.py new file mode 100644 index 0000000000..cb45f2710a --- /dev/null +++ b/base_revision/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/base_revision/__manifest__.py b/base_revision/__manifest__.py new file mode 100644 index 0000000000..2d6d4c60fa --- /dev/null +++ b/base_revision/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2013 Agile Business Group sagl () +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# Copyright 2018 Dreambits Technologies Pvt. Ltd. () +# Copyright 2020 Ecosoft Co., Ltd. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Base Revision (abstract)", + "summary": "Keep track of revised document", + "version": "19.0.1.0.0", + "category": "Tools", + "author": "Agile Business Group," + "Dreambits," + "Camptocamp," + "Akretion," + "Serpent Consulting Services Pvt. Ltd.," + "Ecosoft," + "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-ux", + "license": "AGPL-3", + "depends": ["base"], + "installable": True, +} diff --git a/base_revision/i18n/base_revision.pot b/base_revision/i18n/base_revision.pot new file mode 100644 index 0000000000..47e5396e43 --- /dev/null +++ b/base_revision/i18n/base_revision.pot @@ -0,0 +1,87 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_revision +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__active +msgid "Active" +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__current_revision_id +msgid "Current revision" +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__display_name +msgid "Display Name" +msgstr "" + +#. module: base_revision +#: model:ir.model,name:base_revision.model_base_revision +msgid "Document Revision (abstract)" +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__has_old_revisions +msgid "Has Old Revisions" +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__id +msgid "ID" +msgstr "" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New Revisions" +msgstr "" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New revision created from: %s" +msgstr "" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New revision created: %s" +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__old_revision_ids +msgid "Old revisions" +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__unrevisioned_name +msgid "Original Reference" +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_count +msgid "Previous versions count" +msgstr "" + +#. module: base_revision +#: model:ir.model.constraint,message:base_revision.constraint_base_revision_revision_unique +msgid "Reference and revision must be unique." +msgstr "" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_number +msgid "Revision" +msgstr "" diff --git a/base_revision/i18n/es.po b/base_revision/i18n/es.po new file mode 100644 index 0000000000..2bf9ec656d --- /dev/null +++ b/base_revision/i18n/es.po @@ -0,0 +1,84 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_revision +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-23 09:21+0000\n" +"PO-Revision-Date: 2022-02-23 10:22+0100\n" +"Last-Translator: Ana Suárez \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Poedit 2.3\n" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__active +msgid "Active" +msgstr "Activo" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__current_revision_id +msgid "Current revision" +msgstr "Revisión actual" + +#. module: base_revision +#: model:ir.model,name:base_revision.model_base_revision +msgid "Document Revision (abstract)" +msgstr "Revisión Documento (resumen)" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__has_old_revisions +msgid "Has Old Revisions" +msgstr "Tiene revisiones antiguas" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New Revisions" +msgstr "Nuevas revisiones" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New revision created: %s" +msgstr "Nueva revisión creada: %s" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__old_revision_ids +msgid "Old revisions" +msgstr "Revisiones antiguas" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__unrevisioned_name +msgid "Original Reference" +msgstr "Referencia original" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_count +msgid "Previous versions count" +msgstr "Nº de versiones anteriores" + +#. module: base_revision +#: model:ir.model.constraint,message:base_revision.constraint_base_revision_revision_unique +msgid "Reference and revision must be unique." +msgstr "Referencia y revisión deben ser únicas." + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_number +msgid "Revision" +msgstr "Revisión" + +#~ msgid "Display Name" +#~ msgstr "Nombre mostrado" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Última modificación el" diff --git a/base_revision/i18n/it.po b/base_revision/i18n/it.po new file mode 100644 index 0000000000..533b673303 --- /dev/null +++ b/base_revision/i18n/it.po @@ -0,0 +1,74 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_revision +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-03-25 12:06+0000\n" +"Last-Translator: mymage \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 5.10.2\n" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__active +msgid "Active" +msgstr "Attiva" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__current_revision_id +msgid "Current revision" +msgstr "Revisione corrente" + +#. module: base_revision +#: model:ir.model,name:base_revision.model_base_revision +msgid "Document Revision (abstract)" +msgstr "Revisione documento (sintesi)" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__has_old_revisions +msgid "Has Old Revisions" +msgstr "Ha revisioni precedenti" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New Revisions" +msgstr "Nuove revisioni" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New revision created: %s" +msgstr "Nuova revisione creata: %s" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__old_revision_ids +msgid "Old revisions" +msgstr "Revisioni precedenti" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__unrevisioned_name +msgid "Original Reference" +msgstr "Riferimento originale" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_count +msgid "Previous versions count" +msgstr "Conteggio versioni precedenti" + +#. module: base_revision +#: model:ir.model.constraint,message:base_revision.constraint_base_revision_revision_unique +msgid "Reference and revision must be unique." +msgstr "Il riferimento e la revisione devono essere univoci." + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_number +msgid "Revision" +msgstr "Revisione" diff --git a/base_revision/i18n/pt.po b/base_revision/i18n/pt.po new file mode 100644 index 0000000000..5ba9e45505 --- /dev/null +++ b/base_revision/i18n/pt.po @@ -0,0 +1,74 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_revision +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-03-07 19:06+0000\n" +"Last-Translator: Pedro Castro Silva \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" +"X-Generator: Weblate 5.10.2\n" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__active +msgid "Active" +msgstr "Ativa" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__current_revision_id +msgid "Current revision" +msgstr "Revisão atual" + +#. module: base_revision +#: model:ir.model,name:base_revision.model_base_revision +msgid "Document Revision (abstract)" +msgstr "Revisão do Documento (abstrato)" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__has_old_revisions +msgid "Has Old Revisions" +msgstr "Tem Antigas Revisões" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New Revisions" +msgstr "Novas Revisões" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New revision created: %s" +msgstr "Nova revisão criada: %s" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__old_revision_ids +msgid "Old revisions" +msgstr "Revisões antigas" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__unrevisioned_name +msgid "Original Reference" +msgstr "Referência Original" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_count +msgid "Previous versions count" +msgstr "Nº de versões anteriores" + +#. module: base_revision +#: model:ir.model.constraint,message:base_revision.constraint_base_revision_revision_unique +msgid "Reference and revision must be unique." +msgstr "A referência e a revisão devem devem ser únicas." + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_number +msgid "Revision" +msgstr "Revisão" diff --git a/base_revision/i18n/tr.po b/base_revision/i18n/tr.po new file mode 100644 index 0000000000..a36f2ecee2 --- /dev/null +++ b/base_revision/i18n/tr.po @@ -0,0 +1,74 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_revision +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-08-07 13:26+0000\n" +"Last-Translator: Ömer KÜLAK \n" +"Language-Team: none\n" +"Language: tr\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 5.10.4\n" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__active +msgid "Active" +msgstr "Etkin" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__current_revision_id +msgid "Current revision" +msgstr "Güncel versiyon" + +#. module: base_revision +#: model:ir.model,name:base_revision.model_base_revision +msgid "Document Revision (abstract)" +msgstr "Doküman Revizyonu" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__has_old_revisions +msgid "Has Old Revisions" +msgstr "Eski Revizyonları Var" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New Revisions" +msgstr "Yeni Revizyonlar" + +#. module: base_revision +#. odoo-python +#: code:addons/base_revision/models/base_revision.py:0 +msgid "New revision created: %s" +msgstr "Yeni revizyon oluşturuldu: %s" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__old_revision_ids +msgid "Old revisions" +msgstr "Eski Revizeler" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__unrevisioned_name +msgid "Original Reference" +msgstr "Orjinal Referans" + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_count +msgid "Previous versions count" +msgstr "Önceki versiyon sayısı" + +#. module: base_revision +#: model:ir.model.constraint,message:base_revision.constraint_base_revision_revision_unique +msgid "Reference and revision must be unique." +msgstr "Referans ve revizyon benzersiz olmalıdır." + +#. module: base_revision +#: model:ir.model.fields,field_description:base_revision.field_base_revision__revision_number +msgid "Revision" +msgstr "Revize" diff --git a/base_revision/models/__init__.py b/base_revision/models/__init__.py new file mode 100644 index 0000000000..87cc68f98e --- /dev/null +++ b/base_revision/models/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import base_revision diff --git a/base_revision/models/base_revision.py b/base_revision/models/base_revision.py new file mode 100644 index 0000000000..9e33402d84 --- /dev/null +++ b/base_revision/models/base_revision.py @@ -0,0 +1,133 @@ +# Copyright 2013 Agile Business Group sagl () +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# Copyright 2018 Dreambits Technologies Pvt. Ltd. () +# Copyright 2020 Ecosoft Co., Ltd. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models +from odoo.fields import Command + + +class BaseRevision(models.AbstractModel): + _name = "base.revision" + _description = "Document Revision (abstract)" + + @api.depends("old_revision_ids") + def _compute_has_old_revisions(self): + for rec in self: + rec.has_old_revisions = ( + True if rec.with_context(active_test=False).old_revision_ids else False + ) + + current_revision_id = fields.Many2one( + comodel_name="base.revision", + string="Current revision", + readonly=True, + copy=True, + ) + old_revision_ids = fields.One2many( + comodel_name="base.revision", + inverse_name="current_revision_id", + string="Old revisions", + readonly=True, + domain=["|", ("active", "=", False), ("active", "=", True)], + context={"active_test": False}, + ) + revision_number = fields.Integer(string="Revision", copy=False, default=0) + unrevisioned_name = fields.Char( + string="Original Reference", copy=True, readonly=True + ) + active = fields.Boolean(default=True) + has_old_revisions = fields.Boolean(compute="_compute_has_old_revisions") + revision_count = fields.Integer( + compute="_compute_revision_count", string="Previous versions count" + ) + + @api.depends("old_revision_ids") + def _compute_revision_count(self): + res = ( + self.with_context(active_test=False) + .sudo() + .formatted_read_group( + domain=[("current_revision_id", "in", self.ids)], + groupby=["current_revision_id"], + aggregates=["__count"], + ) + ) + revision_dict = {x["current_revision_id"][0]: x["__count"] for x in res} + for rec in self: + rec.revision_count = revision_dict.get(rec.id, 0) + + _revision_unique = models.Constraint( + "unique(unrevisioned_name, revision_number)", + "Reference and revision must be unique.", + ) + + def copy(self, default=None): + default = default or {} + if "unrevisioned_name" not in default: + default["unrevisioned_name"] = False + revision_records = super().copy(default=default) + for rec in revision_records: + if rec.unrevisioned_name: + continue + name_field = self.env.context.get("revision_name_field", "name") + rec.write({"unrevisioned_name": rec[name_field]}) + return revision_records + + def _get_new_rev_data(self, new_rev_number): + self.ensure_one() + return { + "revision_number": new_rev_number, + "unrevisioned_name": self.unrevisioned_name, + "name": f"{self.unrevisioned_name}-{new_rev_number:02d}", + "old_revision_ids": [Command.link(self.id)], + } + + def _prepare_revision_data(self, new_revision): + return {"active": False, "current_revision_id": new_revision.id} + + def copy_revision_with_context(self): + default_data = self.default_get([]) + new_rev_number = self.revision_number + 1 + vals = self._get_new_rev_data(new_rev_number) + default_data.update(vals) + new_revision = self.copy(default_data) + self.old_revision_ids.write({"current_revision_id": new_revision.id}) + self.write(self._prepare_revision_data(new_revision)) + return new_revision + + @api.model_create_multi + def create(self, vals_list): + name_field = self.env.context.get("revision_name_field", "name") + for vals in vals_list: + if "unrevisioned_name" not in vals: + vals["unrevisioned_name"] = vals[name_field] + + return super().create(vals_list) + + def create_revision(self): + revision_ids = [] + # Looping over records + for rec in self: + # Calling Copy method + copied_rec = rec.copy_revision_with_context() + if hasattr(self, "message_post"): + target_msg = self.env._( + "New revision created from: %s", rec._get_html_link() + ) + copied_rec.message_post(body=target_msg) + source_msg = self.env._( + "New revision created: %s", copied_rec._get_html_link() + ) + rec.message_post(body=source_msg) + revision_ids.append(copied_rec.id) + action = { + "type": "ir.actions.act_window", + "view_mode": "list,form", + "name": self.env._("New Revisions"), + "res_model": self._name, + "domain": f"[('id', 'in', {revision_ids})]", + "target": "current", + } + return action diff --git a/base_revision/pyproject.toml b/base_revision/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/base_revision/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_revision/readme/CONTRIBUTORS.md b/base_revision/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..ccf504d657 --- /dev/null +++ b/base_revision/readme/CONTRIBUTORS.md @@ -0,0 +1,9 @@ +- Devang Pipaliya \<\> +- Lorenzo Battistini \<\> +- Raphael Valyi \<\> +- Alexandre Fayolle \<\> +- Serpent Consulting Services Pvt. Ltd. \<\> +- Akim Juillerat \<\> +- Raf Ven \<\> +- Jeroen Evens \<\> +- Kitti U. \<\> diff --git a/base_revision/readme/DESCRIPTION.md b/base_revision/readme/DESCRIPTION.md new file mode 100644 index 0000000000..4c46f97448 --- /dev/null +++ b/base_revision/readme/DESCRIPTION.md @@ -0,0 +1,31 @@ +Making revision(s) of a document is a common need across many area. + +This module does not provide a functionality by itself but an abstract +model to implement revision capality in other models (e.g. purchase +orders, sales orders, budgets, expenses...). + +**Note:** To be able to use this module in a new model you will need +some development. + +See [sale_order_revision](https://github.com/OCA/sale-workflow) as an +example of implementation. + +Example with sale_order_revision installed, + +On a cancelled orders, you can click on the "New copy of Quotation" +button. This will create a new revision of the quotation, with the same +base number and a '-revno' suffix appended. A message is added in the +chatter saying that a new revision was created. + +In the form view, a new tab is added that lists the previous revisions, +with the date they were made obsolete and the user who performed the +action. + +The old revisions of a sale order are flagged as inactive, so they don't +clutter up searches. + +**Special Remarks:** Starting on version 14, this module was splitted +from sale_order_revision to, + +- base_revision +- sale_order_revision diff --git a/base_revision/static/description/icon.png b/base_revision/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/base_revision/static/description/icon.png differ diff --git a/base_revision/static/description/index.html b/base_revision/static/description/index.html new file mode 100644 index 0000000000..14529c8370 --- /dev/null +++ b/base_revision/static/description/index.html @@ -0,0 +1,465 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Base Revision (abstract)

+ +

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

+

Making revision(s) of a document is a common need across many area.

+

This module does not provide a functionality by itself but an abstract +model to implement revision capality in other models (e.g. purchase +orders, sales orders, budgets, expenses…).

+

Note: To be able to use this module in a new model you will need +some development.

+

See sale_order_revision as an +example of implementation.

+

Example with sale_order_revision installed,

+

On a cancelled orders, you can click on the “New copy of Quotation” +button. This will create a new revision of the quotation, with the same +base number and a ‘-revno’ suffix appended. A message is added in the +chatter saying that a new revision was created.

+

In the form view, a new tab is added that lists the previous revisions, +with the date they were made obsolete and the user who performed the +action.

+

The old revisions of a sale order are flagged as inactive, so they don’t +clutter up searches.

+

Special Remarks: Starting on version 14, this module was splitted +from sale_order_revision to,

+
    +
  • base_revision
  • +
  • sale_order_revision
  • +
+

Table of contents

+ +
+

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

+
    +
  • Agile Business Group
  • +
  • Dreambits
  • +
  • Camptocamp
  • +
  • Akretion
  • +
  • Serpent Consulting Services Pvt. Ltd.
  • +
  • Ecosoft
  • +
+
+
+

Contributors

+ +
+
+

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.

+

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

+

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

+
+
+
+
+ + diff --git a/base_revision/tests/__init__.py b/base_revision/tests/__init__.py new file mode 100644 index 0000000000..301c78bad3 --- /dev/null +++ b/base_revision/tests/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import test_base_revision diff --git a/base_revision/tests/base_revision_tester.py b/base_revision/tests/base_revision_tester.py new file mode 100644 index 0000000000..7a46f6cd06 --- /dev/null +++ b/base_revision/tests/base_revision_tester.py @@ -0,0 +1,32 @@ +# Copyright 2020 Ecosoft (http://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class BaseRevisionTester(models.Model): + _name = "base.revision.tester" + _description = "Base Revision Tester" + _inherit = ["base.revision"] + + name = fields.Char(required=True) + state = fields.Selection( + selection=[ + ("draft", "Draft"), + ("confirmed", "Confirmed"), + ("cancel", "Cancel"), + ], + default="draft", + ) + current_revision_id = fields.Many2one( + comodel_name="base.revision.tester", + ) + old_revision_ids = fields.One2many( + comodel_name="base.revision.tester", + ) + + def action_confirm(self): + self.write({"state": "confirmed"}) + + def action_cancel(self): + self.write({"state": "cancel"}) diff --git a/base_revision/tests/test_base_revision.py b/base_revision/tests/test_base_revision.py new file mode 100644 index 0000000000..5dfb003155 --- /dev/null +++ b/base_revision/tests/test_base_revision.py @@ -0,0 +1,98 @@ +# Copyright 2013 Agile Business Group sagl () +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# Copyright 2018 Dreambits Technologies Pvt. Ltd. () +# Copyright 2020 Ecosoft () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo.orm.model_classes import add_to_registry +from odoo.tests import common + + +class TestBaseRevision(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + from .base_revision_tester import BaseRevisionTester + + add_to_registry(cls.registry, BaseRevisionTester) + cls.registry._setup_models__(cls.env.cr, ["base.revision.tester"]) + cls.registry.init_models( + cls.env.cr, ["base.revision.tester"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "base.revision.tester") + cls.revision_model = cls.env[BaseRevisionTester._name] + + def _create_tester(self, vals_list=None): + if not vals_list: + vals_list = [{}] + for vals in vals_list: + if "name" not in vals: + vals["name"] = "TEST0001" + return self.revision_model.create(vals_list) + + @staticmethod + def _revision_tester(tester): + # Cancel the tester + tester.action_cancel() + # Create a new revision + return tester.create_revision() + + def test_revision(self): + """Check revision process""" + # Create a Tester document + tester_1 = self._create_tester() + + # Create a revision of the Tester + self._revision_tester(tester_1) + + # Check the previous revision of the tester + revision_1 = tester_1.current_revision_id + self.assertEqual(tester_1.state, "cancel") + + # Check the current revision of the tester + self.assertEqual(revision_1.unrevisioned_name, tester_1.name) + self.assertEqual(revision_1.state, "draft") + self.assertTrue(revision_1.active) + self.assertEqual(revision_1.old_revision_ids, tester_1) + self.assertEqual(revision_1.revision_number, 1) + self.assertEqual(revision_1.name.endswith("-01"), True) + self.assertEqual(revision_1.has_old_revisions, True) + self.assertEqual(revision_1.revision_count, 1) + + # Create a new revision of the tester + self._revision_tester(revision_1) + revision_2 = revision_1.current_revision_id + + # Check the previous revision of the tester + self.assertEqual(revision_1.state, "cancel") + self.assertFalse(revision_1.active) + + # Check the current revision of the tester + self.assertEqual(revision_2.unrevisioned_name, tester_1.name) + self.assertEqual(revision_2, tester_1.current_revision_id) + self.assertEqual(revision_2.state, "draft") + self.assertTrue(revision_2.active) + self.assertEqual(revision_2.old_revision_ids, tester_1 + revision_1) + self.assertEqual(revision_2.revision_number, 2) + self.assertEqual(revision_2.name.endswith("-02"), True) + self.assertEqual(revision_2.has_old_revisions, True) + self.assertEqual(revision_2.revision_count, 2) + + def test_simple_copy(self): + """Check copy process""" + # Create a tester + tester_2 = self._create_tester() + # Check the 'Order Reference' of the tester + self.assertEqual(tester_2.name, tester_2.unrevisioned_name) + + # Copy the tester + tester_3 = tester_2.copy({"name": "TEST0002"}) + # Check the 'Reference' of the copied tester + self.assertEqual(tester_3.name, tester_3.unrevisioned_name) + + def test_create_multiple(self): + """Check copy process""" + # Create a tester + tester_2 = self._create_tester([{"name": "TEST0001"}, {"name": "TEST0002"}]) + # Check the 'Order Reference' of the tester + for tester in tester_2: + self.assertEqual(tester.name, tester.unrevisioned_name) diff --git a/base_search_custom_field_filter/README.rst b/base_search_custom_field_filter/README.rst new file mode 100644 index 0000000000..41e4eee82d --- /dev/null +++ b/base_search_custom_field_filter/README.rst @@ -0,0 +1,146 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +==================================== +Add custom filters for fields via UI +==================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:feb72fea19956dd2994c348c417251dc03543356eb9829273454617316b0547d + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/19.0/base_search_custom_field_filter + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-19-0/server-ux-19-0-base_search_custom_field_filter + :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/server-ux&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to define custom filters in the search views for an +specific field belonging to the document or any other related document. + +This nature makes the definition quite technical, but once done, it adds +the element in the UI for regular user use. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +1. Go to *Settings > Technical > User Interface > Custom Field Filters*. +2. Create a new record, and define following information: + + - The **Model** for which you are defining the filter. It will appear + in all the search views of this model. + - The label you want to see on the search line on the **Name** field. + This field allows translations for proper UI in different + languages. + - The **Expression**, which is the field chain string with dot + notation. Examples: product_id, product_id.seller_ids.name, + partner_id.lang. + - Optionally, you can fill **Position After** for indicating after + which existing field (technical name) the filter will appear. If + empty or not found, the filter will be added at the end. + +3. You can reorder records for determining sorting for multiple filters + for the same model with the arrow handle in the left part. + +Usage +===== + +1. Go to the menu entry for which you have defined the custom field + filter. +2. On the search bar, type anything. +3. In the filter list, you will see the line for the element you have + defined. + +As demo data, a custom field filter is included for sample purposes: + +|image| + +Steps for trying this sample: + +1. Install contacts module. +2. Go to *Contacts*. +3. Type "english" and you'll find the filter "Language" at the end: + +|image1| + +.. |image| image:: https://raw.githubusercontent.com/OCA/server-ux/19.0/base_search_custom_field_filter/static/src/img/ir_ui_custom_field_filter.png +.. |image1| image:: https://raw.githubusercontent.com/OCA/server-ux/19.0/base_search_custom_field_filter/static/src/img/contact_search.png + +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 +------- + +* Tecnativa +* Amitaujas + +Contributors +------------ + +- `Tecnativa `__: + + - Carlos Dauden + - Pedro M. Baeza + +- `Amitaujas `__: +- `Studio73 `__ + + - Sergio Martínez + +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-pedrobaeza| image:: https://github.com/pedrobaeza.png?size=40px + :target: https://github.com/pedrobaeza + :alt: pedrobaeza + +Current `maintainer `__: + +|maintainer-pedrobaeza| + +This module is part of the `OCA/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_search_custom_field_filter/__init__.py b/base_search_custom_field_filter/__init__.py new file mode 100644 index 0000000000..c32fd62b78 --- /dev/null +++ b/base_search_custom_field_filter/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/base_search_custom_field_filter/__manifest__.py b/base_search_custom_field_filter/__manifest__.py new file mode 100644 index 0000000000..8cdbdf81f0 --- /dev/null +++ b/base_search_custom_field_filter/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2020 Tecnativa - Carlos Dauden +# Copyright 2020 Tecnativa - Pedro M. Baeza +# Copyright 2023 Amitaujas +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Add custom filters for fields via UI", + "version": "19.0.1.0.0", + "category": "Usability", + "website": "https://github.com/OCA/server-ux", + "author": "Tecnativa, Amitaujas, Odoo Community Association (OCA)", + "demo": ["demo/demo_ir_ui_custom_field_filter.xml"], + "data": [ + "security/ir.model.access.csv", + "views/ir_ui_custom_field_filter_views.xml", + ], + "depends": ["web"], + "license": "AGPL-3", + "installable": True, + "maintainers": ["pedrobaeza"], +} diff --git a/base_search_custom_field_filter/demo/demo_ir_ui_custom_field_filter.xml b/base_search_custom_field_filter/demo/demo_ir_ui_custom_field_filter.xml new file mode 100644 index 0000000000..9db4efda7d --- /dev/null +++ b/base_search_custom_field_filter/demo/demo_ir_ui_custom_field_filter.xml @@ -0,0 +1,7 @@ + + + + Language + lang + + diff --git a/base_search_custom_field_filter/i18n/base_search_custom_field_filter.pot b/base_search_custom_field_filter/i18n/base_search_custom_field_filter.pot new file mode 100644 index 0000000000..0124a18b3e --- /dev/null +++ b/base_search_custom_field_filter/i18n/base_search_custom_field_filter.pot @@ -0,0 +1,114 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_search_custom_field_filter +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: base_search_custom_field_filter +#: model:ir.model.constraint,message:base_search_custom_field_filter.constraint_ir_ui_custom_field_filter_unique_model_expression +msgid "A filter with the same expression already exists for this model." +msgstr "" + +#. module: base_search_custom_field_filter +#. odoo-python +#: code:addons/base_search_custom_field_filter/models/ir_ui_custom_field_filter.py:0 +msgid "A filter with the same name already exists for this model." +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model,name:base_search_custom_field_filter.model_base +msgid "Base" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__create_uid +msgid "Created by" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__create_date +msgid "Created on" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.actions.act_window,name:base_search_custom_field_filter.action_ir_ui_custom_field_filter +#: model:ir.ui.menu,name:base_search_custom_field_filter.menu_ir_ui_custom_field_filter +msgid "Custom Field Filters" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model,name:base_search_custom_field_filter.model_ir_ui_custom_field_filter +msgid "Custom UI field filter" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__display_name +msgid "Display Name" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__expression +msgid "Expression" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__id +msgid "ID" +msgstr "" + +#. module: base_search_custom_field_filter +#. odoo-python +#: code:addons/base_search_custom_field_filter/models/ir_ui_custom_field_filter.py:0 +msgid "Incorrect expression: %s" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__write_date +msgid "Last Updated on" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__model_id +msgid "Model" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__model_name +msgid "Model name" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__name +msgid "Name" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,help:base_search_custom_field_filter.field_ir_ui_custom_field_filter__position_after +msgid "" +"Optional field name for putting the filter after that one. If empty or not " +"found, it will be put at the end." +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__position_after +msgid "Position After" +msgstr "" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__sequence +msgid "Sequence" +msgstr "" diff --git a/base_search_custom_field_filter/i18n/es.po b/base_search_custom_field_filter/i18n/es.po new file mode 100644 index 0000000000..4bb54e9051 --- /dev/null +++ b/base_search_custom_field_filter/i18n/es.po @@ -0,0 +1,119 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_search_custom_field_filter +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-08-27 16:07+0000\n" +"Last-Translator: Ivorra78 \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" +"X-Generator: Weblate 4.17\n" + +#. module: base_search_custom_field_filter +#: model:ir.model,name:base_search_custom_field_filter.model_base +msgid "Base" +msgstr "Base" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: base_search_custom_field_filter +#: model:ir.actions.act_window,name:base_search_custom_field_filter.action_ir_ui_custom_field_filter +#: model:ir.ui.menu,name:base_search_custom_field_filter.menu_ir_ui_custom_field_filter +msgid "Custom Field Filters" +msgstr "Filtros campos customizados" + +#. module: base_search_custom_field_filter +#: model:ir.model,name:base_search_custom_field_filter.model_ir_ui_custom_field_filter +msgid "Custom UI field filter" +msgstr "Filtro de campo de IU personalizado" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__expression +msgid "Expression" +msgstr "Expresión" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__id +msgid "ID" +msgstr "ID" + +#. module: base_search_custom_field_filter +#. odoo-python +#: code:addons/base_search_custom_field_filter/models/ir_ui_custom_field_filter.py:0 +#, python-format +msgid "Incorrect expression: %s." +msgstr "Expresión incorrecta: %s." + +#. module: base_search_custom_field_filter +#: model:ir.ui.custom.field.filter,name:base_search_custom_field_filter.custom_field_filter_demo +msgid "Language" +msgstr "Idioma" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__write_uid +msgid "Last Updated by" +msgstr "Última modificación por" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__write_date +msgid "Last Updated on" +msgstr "Última modificación el" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__model_id +msgid "Model" +msgstr "Modelo" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__model_name +msgid "Model name" +msgstr "Nombre modelo" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__name +msgid "Name" +msgstr "Nombre" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,help:base_search_custom_field_filter.field_ir_ui_custom_field_filter__position_after +msgid "" +"Optional field name for putting the filter after that one. If empty or not " +"found, it will be put at the end." +msgstr "" +"Nombre de campo opcional para poner el filtro después de ese. Si está vacío " +"o no encontrado, se pondrá al final." + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__position_after +msgid "Position After" +msgstr "Posición Después" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__sequence +msgid "Sequence" +msgstr "Secuencia" diff --git a/base_search_custom_field_filter/i18n/it.po b/base_search_custom_field_filter/i18n/it.po new file mode 100644 index 0000000000..9d325e3d2d --- /dev/null +++ b/base_search_custom_field_filter/i18n/it.po @@ -0,0 +1,119 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_search_custom_field_filter +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-06-09 14:26+0000\n" +"Last-Translator: mymage \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 5.10.4\n" + +#. module: base_search_custom_field_filter +#: model:ir.model,name:base_search_custom_field_filter.model_base +msgid "Base" +msgstr "Base" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: base_search_custom_field_filter +#: model:ir.actions.act_window,name:base_search_custom_field_filter.action_ir_ui_custom_field_filter +#: model:ir.ui.menu,name:base_search_custom_field_filter.menu_ir_ui_custom_field_filter +msgid "Custom Field Filters" +msgstr "Filtri Campi Personalizzati" + +#. module: base_search_custom_field_filter +#: model:ir.model,name:base_search_custom_field_filter.model_ir_ui_custom_field_filter +msgid "Custom UI field filter" +msgstr "Filtro campo UI personalizzato" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__expression +msgid "Expression" +msgstr "Espressione" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__id +msgid "ID" +msgstr "ID" + +#. module: base_search_custom_field_filter +#. odoo-python +#: code:addons/base_search_custom_field_filter/models/ir_ui_custom_field_filter.py:0 +#, python-format +msgid "Incorrect expression: %s." +msgstr "Espressione non corretta: %s." + +#. module: base_search_custom_field_filter +#: model:ir.ui.custom.field.filter,name:base_search_custom_field_filter.custom_field_filter_demo +msgid "Language" +msgstr "Lingua" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__model_id +msgid "Model" +msgstr "Modello" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__model_name +msgid "Model name" +msgstr "Nome modello" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__name +msgid "Name" +msgstr "Nome" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,help:base_search_custom_field_filter.field_ir_ui_custom_field_filter__position_after +msgid "" +"Optional field name for putting the filter after that one. If empty or not " +"found, it will be put at the end." +msgstr "" +"Nome del campo opzionale per inserire il filtro dopo di esso. Se vuoto o non " +"trovato, verrà posizionato alla fine." + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__position_after +msgid "Position After" +msgstr "Posiziona dopo" + +#. module: base_search_custom_field_filter +#: model:ir.model.fields,field_description:base_search_custom_field_filter.field_ir_ui_custom_field_filter__sequence +msgid "Sequence" +msgstr "Sequenza" diff --git a/base_search_custom_field_filter/models/__init__.py b/base_search_custom_field_filter/models/__init__.py new file mode 100644 index 0000000000..99769e90d8 --- /dev/null +++ b/base_search_custom_field_filter/models/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import base +from . import ir_ui_custom_field_filter diff --git a/base_search_custom_field_filter/models/base.py b/base_search_custom_field_filter/models/base.py new file mode 100644 index 0000000000..98dba5ddd4 --- /dev/null +++ b/base_search_custom_field_filter/models/base.py @@ -0,0 +1,85 @@ +# Copyright 2020 Tecnativa - Carlos Dauden +# Copyright 2020 Tecnativa - Pedro M. Baeza +# Copyright 2022 Tecnativa - Víctor Martínez +# Copyright 2023 Amitaujas +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from lxml import etree + +from odoo import api, models + + +class Base(models.AbstractModel): + _inherit = "base" + + @api.model + def _add_custom_filters(self, res, custom_filters): + """ + Add custom filter fields to the view architecture. + + This method modifies the XML architecture of a view by injecting custom filter + fields at specific positions. For each custom filter, it attempts to place the + field after a specified field (if position_after is defined) or after the last + field in the view. + + Args: + res (dict): The view data dictionary containing the architecture + custom_filters (recordset): Custom filter records to be added to the view + + Returns: + dict: The modified view data with custom filters injected + """ + arch = etree.fromstring(res["arch"]) + for custom_filter in custom_filters: + node = False + if custom_filter.position_after: + node = arch.xpath(f"//field[@name='{custom_filter.position_after}']") + if not node: + node = arch.xpath("//field[last()]") + if node: + elem = etree.Element( + "field", + {"name": custom_filter.expression, "string": custom_filter.name}, + ) + node[0].addnext(elem) + res["arch"] = etree.tostring(arch) + return res + + @api.model + def get_view(self, view_id=None, view_type="form", **options): + """Inject fields field in search views.""" + res = super().get_view(view_id, view_type, **options) + if view_type == "search": + custom_filters = self.env["ir.ui.custom.field.filter"].search( + [("model_name", "=", res.get("model"))] + ) + if custom_filters: + res = self._add_custom_filters(res, custom_filters) + return res + + @api.model + def get_views(self, views, options=None): + """Inject fake field definition for having custom filters available.""" + res = super().get_views(views, options) + if self._name not in res["models"]: + res["models"][self._name] = {"fields": {}} + custom_filters = self.env["ir.ui.custom.field.filter"].search( + [("model_name", "=", self._name)] + ) + for custom_filter in custom_filters: + field = custom_filter._get_related_field() + # Safeguard: Ensure the related field exists before processing. + # This check is necessary because a custom filter might reference + # a field that no longer exists or is misconfigured. In such cases, + # we skip the filter to avoid runtime errors. + if not field: + continue + field_name = custom_filter.expression + res["models"][self._name]["fields"][field_name] = field.get_description( + self.env + ) + # Force these properties to prevent the field from appearing in the UI + res["models"][self._name]["fields"][field_name]["selectable"] = False + res["models"][self._name]["fields"][field_name]["sortable"] = False + res["models"][self._name]["fields"][field_name]["store"] = False + return res diff --git a/base_search_custom_field_filter/models/ir_ui_custom_field_filter.py b/base_search_custom_field_filter/models/ir_ui_custom_field_filter.py new file mode 100644 index 0000000000..f651d90196 --- /dev/null +++ b/base_search_custom_field_filter/models/ir_ui_custom_field_filter.py @@ -0,0 +1,87 @@ +# Copyright 2020 Tecnativa - Carlos Dauden +# Copyright 2020 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, exceptions, fields, models +from odoo.fields import Domain + + +class IrUiCustomFilter(models.Model): + _name = "ir.ui.custom.field.filter" + _description = "Custom UI field filter" + _order = "model_id, sequence, id" + + _unique_model_expression = models.Constraint( + "UNIQUE(model_id, expression)", + "A filter with the same expression already exists for this model.", + ) + + sequence = fields.Integer() + model_id = fields.Many2one( + comodel_name="ir.model", required=True, ondelete="cascade" + ) + model_name = fields.Char( + related="model_id.model", + store=True, + readonly=True, + index=True, + string="Model name", + ) + name = fields.Char(required=True, translate=True) + expression = fields.Char(required=True) + position_after = fields.Char( + help="Optional field name for putting the filter after that one. " + "If empty or not found, it will be put at the end.", + ) + + def _get_related_field(self): + """Determine the chain of fields.""" + self.ensure_one() + related = self.expression.split(".") + target = self.env[self.model_name] + for name in related: + field = target._fields.get(name) + target = target[name] + return field + + @api.constrains("model_id", "expression") + def _check_expression(self): + """ + Validate that the expression refers to valid fields. + + This constraint ensures that the field expression can be resolved + through the model's field chain. It attempts to traverse the field + path and raises a validation error if any part of the path is invalid. + """ + for record in self: + try: + record._get_related_field() + except KeyError as e: + raise exceptions.ValidationError( + self.env._("Incorrect expression: %s", record.expression) + ) from e + + @api.constrains("model_id", "name") + def _check_name_unique(self): + """ + Ensure filter names are unique per model. + + This constraint prevents creating multiple filters with the same name + for the same model, which would cause confusion in the UI. It checks + for existing filters with the same name and model, excluding the + current record. + """ + for record in self: + domain = Domain.AND( + [ + Domain("model_id", "=", record.model_id.id), + Domain("name", "=", record.name), + Domain("id", "!=", record.id), + ] + ) + if self.search_count(domain): + raise exceptions.ValidationError( + self.env._( + "A filter with the same name already exists for this model." + ) + ) diff --git a/base_search_custom_field_filter/pyproject.toml b/base_search_custom_field_filter/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/base_search_custom_field_filter/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_search_custom_field_filter/readme/CONFIGURE.md b/base_search_custom_field_filter/readme/CONFIGURE.md new file mode 100644 index 0000000000..0ce74edb76 --- /dev/null +++ b/base_search_custom_field_filter/readme/CONFIGURE.md @@ -0,0 +1,16 @@ +1. Go to *Settings \> Technical \> User Interface \> Custom Field + Filters*. +2. Create a new record, and define following information: + - The **Model** for which you are defining the filter. It will + appear in all the search views of this model. + - The label you want to see on the search line on the **Name** + field. This field allows translations for proper UI in different + languages. + - The **Expression**, which is the field chain string with dot + notation. Examples: product_id, product_id.seller_ids.name, + partner_id.lang. + - Optionally, you can fill **Position After** for indicating after + which existing field (technical name) the filter will appear. If + empty or not found, the filter will be added at the end. +3. You can reorder records for determining sorting for multiple filters + for the same model with the arrow handle in the left part. diff --git a/base_search_custom_field_filter/readme/CONTRIBUTORS.md b/base_search_custom_field_filter/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..9ed9fdab65 --- /dev/null +++ b/base_search_custom_field_filter/readme/CONTRIBUTORS.md @@ -0,0 +1,6 @@ +- [Tecnativa](https://www.tecnativa.com): + - Carlos Dauden + - Pedro M. Baeza +- [Amitaujas](https://www.amitaujas.com): +- [Studio73](https://www.studio73.es/) + - Sergio Martínez diff --git a/base_search_custom_field_filter/readme/DESCRIPTION.md b/base_search_custom_field_filter/readme/DESCRIPTION.md new file mode 100644 index 0000000000..273446ecf3 --- /dev/null +++ b/base_search_custom_field_filter/readme/DESCRIPTION.md @@ -0,0 +1,5 @@ +This module allows to define custom filters in the search views for an +specific field belonging to the document or any other related document. + +This nature makes the definition quite technical, but once done, it adds +the element in the UI for regular user use. diff --git a/base_search_custom_field_filter/readme/USAGE.md b/base_search_custom_field_filter/readme/USAGE.md new file mode 100644 index 0000000000..7f87a525a7 --- /dev/null +++ b/base_search_custom_field_filter/readme/USAGE.md @@ -0,0 +1,17 @@ +1. Go to the menu entry for which you have defined the custom field + filter. +2. On the search bar, type anything. +3. In the filter list, you will see the line for the element you have + defined. + +As demo data, a custom field filter is included for sample purposes: + +![image](../static/src/img/ir_ui_custom_field_filter.png) + +Steps for trying this sample: + +1. Install contacts module. +2. Go to *Contacts*. +3. Type "english" and you'll find the filter "Language" at the end: + +![image](../static/src/img/contact_search.png) diff --git a/base_search_custom_field_filter/security/ir.model.access.csv b/base_search_custom_field_filter/security/ir.model.access.csv new file mode 100644 index 0000000000..ba1e3b55aa --- /dev/null +++ b/base_search_custom_field_filter/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 +access_ir_ui_custom_field_filter_user,ir.ui.custom.field.filter,model_ir_ui_custom_field_filter,base.group_user,1,0,0,0 +access_ir_ui_custom_field_filter_system,ir.ui.custom.field.filter,model_ir_ui_custom_field_filter,base.group_system,1,1,1,1 diff --git a/base_search_custom_field_filter/static/description/icon.png b/base_search_custom_field_filter/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/base_search_custom_field_filter/static/description/icon.png differ diff --git a/base_search_custom_field_filter/static/description/index.html b/base_search_custom_field_filter/static/description/index.html new file mode 100644 index 0000000000..7fe1a5c9eb --- /dev/null +++ b/base_search_custom_field_filter/static/description/index.html @@ -0,0 +1,487 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Add custom filters for fields via UI

+ +

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

+

This module allows to define custom filters in the search views for an +specific field belonging to the document or any other related document.

+

This nature makes the definition quite technical, but once done, it adds +the element in the UI for regular user use.

+

Table of contents

+ +
+

Configuration

+
    +
  1. Go to Settings > Technical > User Interface > Custom Field Filters.
  2. +
  3. Create a new record, and define following information:
      +
    • The Model for which you are defining the filter. It will appear +in all the search views of this model.
    • +
    • The label you want to see on the search line on the Name field. +This field allows translations for proper UI in different +languages.
    • +
    • The Expression, which is the field chain string with dot +notation. Examples: product_id, product_id.seller_ids.name, +partner_id.lang.
    • +
    • Optionally, you can fill Position After for indicating after +which existing field (technical name) the filter will appear. If +empty or not found, the filter will be added at the end.
    • +
    +
  4. +
  5. You can reorder records for determining sorting for multiple filters +for the same model with the arrow handle in the left part.
  6. +
+
+
+

Usage

+
    +
  1. Go to the menu entry for which you have defined the custom field +filter.
  2. +
  3. On the search bar, type anything.
  4. +
  5. In the filter list, you will see the line for the element you have +defined.
  6. +
+

As demo data, a custom field filter is included for sample purposes:

+

image

+

Steps for trying this sample:

+
    +
  1. Install contacts module.
  2. +
  3. Go to Contacts.
  4. +
  5. Type “english” and you’ll find the filter “Language” at the end:
  6. +
+

image1

+
+
+

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

+
    +
  • Tecnativa
  • +
  • Amitaujas
  • +
+
+
+

Contributors

+ +
+
+

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:

+

pedrobaeza

+

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

+

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

+
+
+
+
+ + diff --git a/base_search_custom_field_filter/static/src/img/contact_search.png b/base_search_custom_field_filter/static/src/img/contact_search.png new file mode 100644 index 0000000000..ba421d4873 Binary files /dev/null and b/base_search_custom_field_filter/static/src/img/contact_search.png differ diff --git a/base_search_custom_field_filter/static/src/img/ir_ui_custom_field_filter.png b/base_search_custom_field_filter/static/src/img/ir_ui_custom_field_filter.png new file mode 100644 index 0000000000..c4dee48486 Binary files /dev/null and b/base_search_custom_field_filter/static/src/img/ir_ui_custom_field_filter.png differ diff --git a/base_search_custom_field_filter/tests/__init__.py b/base_search_custom_field_filter/tests/__init__.py new file mode 100644 index 0000000000..9d5fb781bb --- /dev/null +++ b/base_search_custom_field_filter/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_filter diff --git a/base_search_custom_field_filter/tests/test_filter.py b/base_search_custom_field_filter/tests/test_filter.py new file mode 100644 index 0000000000..513d3a6c0c --- /dev/null +++ b/base_search_custom_field_filter/tests/test_filter.py @@ -0,0 +1,118 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from lxml import etree + +from odoo import exceptions +from odoo.exceptions import ValidationError +from odoo.tests import Form + +from odoo.addons.base.tests.common import BaseCommon + + +class TestFilter(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.model = cls.env["res.partner"] + cls.custom_filter_model = cls.env["ir.ui.custom.field.filter"] + + def test_00(self): + filter_form = Form(self.custom_filter_model) + filter_form.model_id = self.env.ref("base.model_res_partner") + filter_form.name = "Title" + + with self.assertRaises(exceptions.ValidationError): + filter_form.expression = "title_1" + filter_form.save() + filter_form.expression = "vat_label" + filter_form.save() + arch = self.model.get_view(False, "search")["arch"] + search = etree.fromstring(arch) + self.assertTrue(search.xpath("//search/field[@name='vat_label']")) + + def test_01_invalid_expression(self): + filter_form = Form(self.custom_filter_model) + filter_form.model_id = self.env.ref("base.model_res_partner") + filter_form.name = "Invalid Expression" + with self.assertRaises(exceptions.ValidationError): + filter_form.expression = "invalid_field" + filter_form.save() + + def test_02_valid_expression(self): + filter_form = Form(self.custom_filter_model) + filter_form.model_id = self.env.ref("base.model_res_partner") + filter_form.name = "Valid Expression" + filter_form.expression = "name" + filter_form.save() + arch = self.model.get_view(False, "search")["arch"] + search = etree.fromstring(arch) + self.assertTrue(search.xpath("//search/field[@name='name']")) + + def test_03_duplicate_filter(self): + self.env["ir.ui.custom.field.filter"].create( + { + "model_id": self.env.ref("base.model_res_partner").id, + "name": "Duplicate Filter", + "expression": "name", + } + ) + with self.assertRaises(ValidationError): + self.env["ir.ui.custom.field.filter"].create( + { + "model_id": self.env.ref("base.model_res_partner").id, + "name": "Duplicate Filter", + "expression": "email", + } + ) + + def test_04_add_custom_filters(self): + res = {"arch": ""} + custom_filters = [ + self.custom_filter_model.create( + { + "model_id": self.env.ref("base.model_res_partner").id, + "name": "Custom Name", + "expression": "name", + "position_after": "email", + } + ), + self.custom_filter_model.create( + { + "model_id": self.env.ref("base.model_res_partner").id, + "name": "Custom Phone", + "expression": "phone", + "position_after": "", + } + ), + ] + res = self.model._add_custom_filters(res, custom_filters) + arch = etree.fromstring(res["arch"]) + email_field = arch.xpath("//field[@name='email']") + self.assertTrue(email_field) + next_field = email_field[0].getnext() + self.assertEqual(next_field.get("name"), "name") + last_field = arch.xpath("//field[last()]") + self.assertTrue(last_field) + self.assertEqual(last_field[0].get("name"), "phone") + + def test_05_get_views_with_custom_filter(self): + self.custom_filter_model.create( + { + "model_id": self.env.ref("base.model_res_partner").id, + "name": "Custom Name", + "expression": "name", + } + ) + res = self.env["res.partner"].get_views([], {}) + self.assertIn("name", res["models"]["res.partner"]["fields"]) + self.assertFalse(res["models"]["res.partner"]["fields"]["name"]["selectable"]) + + def test_06_invalid_related_field(self): + with self.assertRaises(ValidationError): + self.custom_filter_model.create( + { + "model_id": self.env.ref("base.model_res_partner").id, + "name": "Invalid Field", + "expression": "non_existent_field", + } + ) diff --git a/base_search_custom_field_filter/views/ir_ui_custom_field_filter_views.xml b/base_search_custom_field_filter/views/ir_ui_custom_field_filter_views.xml new file mode 100644 index 0000000000..99966248eb --- /dev/null +++ b/base_search_custom_field_filter/views/ir_ui_custom_field_filter_views.xml @@ -0,0 +1,29 @@ + + + + + ir.ui.custom.field.filter + + + + + + + + + + + + + Custom Field Filters + ir.ui.custom.field.filter + + + + diff --git a/base_substate/README.rst b/base_substate/README.rst new file mode 100644 index 0000000000..c59e370dce --- /dev/null +++ b/base_substate/README.rst @@ -0,0 +1,100 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +============== +Base Sub State +============== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:19e767ff86cf0d9938666b02a0c54fbbe50ebe579e65e5a0d3acf7ca89ba1e3f + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/19.0/base_substate + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-19-0/server-ux-19-0-base_substate + :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/server-ux&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module provide abstract models to manage customizable substates to +be applied on different models (sale order, purchase, ...). + +example: +-------- + +- for the quotation state of a sale order we can define 3 substates "In + negotiation", "Won" and "Lost". +- We can also send mail when the substate is reached. + +It is not useful by itself. You can see an example of implementation in +the 'purchase_substate' module. (purchase-workflow repository). + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +1. You must install an application module depending this one (for + example purchase_substate) + +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 +------- + +* Akretion + +Contributors +------------ + +- Mourad EL HADJ MIMOUNE +- Kitti U. +- Alexei Rivera (migration to 15.0) +- Saran Lim. saranl@ecosoft.co.th + +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/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_substate/__init__.py b/base_substate/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/base_substate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_substate/__manifest__.py b/base_substate/__manifest__.py new file mode 100644 index 0000000000..9c3561b1a3 --- /dev/null +++ b/base_substate/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2020 Akretion () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Base Sub State", + "version": "19.0.1.0.1", + "category": "Tools", + "author": "Akretion, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-ux", + "license": "AGPL-3", + "depends": ["base", "mail"], + "data": [ + "security/base_substate_security.xml", + "security/ir.model.access.csv", + "views/base_substate_type_views.xml", + "views/base_substate_value_views.xml", + "views/base_substate_views.xml", + ], + "installable": True, +} diff --git a/base_substate/i18n/base_substate.pot b/base_substate/i18n/base_substate.pot new file mode 100644 index 0000000000..d38675c53a --- /dev/null +++ b/base_substate/i18n/base_substate.pot @@ -0,0 +1,221 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_substate +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__active +msgid "Active" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__model +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__model +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__model +msgid "Apply on" +msgstr "" + +#. module: base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +msgid "Archived" +msgstr "" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_view +#: model:ir.model,name:base_substate.model_base_substate +#: model:ir.ui.menu,name:base_substate.menu_base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_search +msgid "Base Substate" +msgstr "" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_type +msgid "Base Substate Type" +msgstr "" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_mixin +msgid "BaseSubstate Mixin" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_uid +msgid "Created by" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_date +msgid "Created on" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__description +msgid "Description" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__display_name +#: model:ir.model.fields,field_description:base_substate.field_base_substate_mixin__display_name +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__display_name +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__display_name +msgid "Display Name" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__mail_template_id +msgid "Email Template" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__sequence +msgid "Gives the sequence order when applying the default substate" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__id +#: model:ir.model.fields,field_description:base_substate.field_base_substate_mixin__id +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__id +msgid "ID" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__mail_template_id +msgid "" +"If set, an email will be sent to the partner when the object reaches this " +"substate." +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_date +msgid "Last Updated on" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__model +#: model:ir.model.fields,help:base_substate.field_target_state_value__model +msgid "Model for technical use" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__name +msgid "Name" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__sequence +msgid "Sequence" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_mixin__substate_id +msgid "Sub State" +msgstr "" + +#. module: base_substate +#: model:ir.ui.menu,name:base_substate.menu_substate_config +msgid "Sub State Configuration" +msgstr "" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_type_view +#: model:ir.ui.menu,name:base_substate.menu_base_substate_type +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_search +msgid "Sub State Type" +msgstr "" + +#. module: base_substate +#. odoo-python +#: code:addons/base_substate/models/base_substate_mixin.py:0 +msgid "" +"Substate %(substate_name)s not defined for state %(state_name)s but for " +"%(target_state_name)s" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__name +msgid "Substate Name" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__base_substate_type_id +msgid "Substate Type" +msgstr "" + +#. module: base_substate +#: model:res.groups,name:base_substate.group_substate_manager +msgid "Substate manager" +msgstr "" + +#. module: base_substate +#. odoo-python +#: code:addons/base_substate/models/base_substate_mixin.py:0 +msgid "Substate not for this object but for %(model_name)s" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__target_state_field +msgid "Target State Field" +msgstr "" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_target_state_value_view +#: model:ir.model,name:base_substate.model_target_state_value +#: model:ir.model.fields,field_description:base_substate.field_base_substate__target_state_value_id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__target_state_value +#: model:ir.ui.menu,name:base_substate.menu_target_state_value +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_search +msgid "Target State Value" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__name +msgid "Target state Name" +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__name +msgid "" +"Target state translateble name.\n" +"Ex: for sale order \"Quotation\", \"Sale order\", \"Locked\"..." +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate_type__target_state_field +msgid "" +"Technical target state field name. Ex for sale order \"state\" for other " +"\"status\" ... " +msgstr "" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__target_state_value +msgid "" +"Technical target state value.\n" +"Ex: for sale order \"draft\", \"sale\", \"done\", ..." +msgstr "" diff --git a/base_substate/i18n/es.po b/base_substate/i18n/es.po new file mode 100644 index 0000000000..8ed151561a --- /dev/null +++ b/base_substate/i18n/es.po @@ -0,0 +1,237 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_substate +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-11 15:17+0000\n" +"Last-Translator: Ivorra78 \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" +"X-Generator: Weblate 4.17\n" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__active +msgid "Active" +msgstr "Activo" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__model +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__model +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__model +msgid "Apply on" +msgstr "Aplicar sobre" + +#. module: base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +msgid "Archived" +msgstr "Archivado" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_view +#: model:ir.model,name:base_substate.model_base_substate +#: model:ir.ui.menu,name:base_substate.menu_base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_search +msgid "Base Substate" +msgstr "Subestado Base" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_type +msgid "Base Substate Type" +msgstr "Tipo de Subestado Base" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_mixin +msgid "BaseSubstate Mixin" +msgstr "Mezcla de subestado base" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__description +msgid "Description" +msgstr "Descripción" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__display_name +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__display_name +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__mail_template_id +msgid "Email Template" +msgstr "Plantilla de Correo Electrónico" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__sequence +msgid "Gives the sequence order when applying the default substate" +msgstr "Indica el orden de secuencia al aplicar el subestado por defecto" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__id +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__id +msgid "ID" +msgstr "ID (identificación)" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__mail_template_id +msgid "" +"If set, an email will be sent to the partner when the object reaches this " +"substate." +msgstr "" +"Si se establece, se enviará un correo electrónico al socio cuando el objeto " +"alcance este subestado." + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_uid +msgid "Last Updated by" +msgstr "Actualizado por Última vez por" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_date +msgid "Last Updated on" +msgstr "Última Actualización el" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__model +#: model:ir.model.fields,help:base_substate.field_target_state_value__model +msgid "Model for technical use" +msgstr "Modelo para uso técnico" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__name +msgid "Name" +msgstr "Nombre" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_mixin__substate_id +msgid "Sub State" +msgstr "Sub Estado" + +#. module: base_substate +#: model:ir.ui.menu,name:base_substate.menu_substate_config +msgid "Sub State Configuration" +msgstr "Configuración del Sub Estado" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_type_view +#: model:ir.ui.menu,name:base_substate.menu_base_substate_type +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_search +msgid "Sub State Type" +msgstr "Tipo de Sub Estado" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__name +msgid "Substate Name" +msgstr "Nombre del Subestado" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__base_substate_type_id +msgid "Substate Type" +msgstr "Tipo de Subestado" + +#. module: base_substate +#: model:res.groups,name:base_substate.group_substate_manager +msgid "Substate manager" +msgstr "Gerente del Subestado" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__target_state_field +msgid "Target State Field" +msgstr "Objetivo Campo del Estado" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_target_state_value_view +#: model:ir.model,name:base_substate.model_target_state_value +#: model:ir.model.fields,field_description:base_substate.field_base_substate__target_state_value_id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__target_state_value +#: model:ir.ui.menu,name:base_substate.menu_target_state_value +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_search +msgid "Target State Value" +msgstr "Valor Objetivo del Estado" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__name +msgid "Target state Name" +msgstr "Nombre del Estado objetivo" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__name +msgid "" +"Target state translateble name.\n" +"Ex: for sale order \"Quotation\", \"Sale order\", \"Locked\"..." +msgstr "" +"Nombre traducible del estado de destino.\n" +"Ej: para orden de venta \"Quotation\", \"Sale order\", \"Locked\"..." + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate_type__target_state_field +msgid "" +"Technical target state field name. Ex for sale order \"state\" for other " +"\"status\" ... " +msgstr "" +"Nombre de campo de estado de destino técnico. Ej para pedido de venta " +"\"state \" para otro \"status \" ... " + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__target_state_value +msgid "" +"Technical target state value.\n" +"Ex: for sale order \"draft\", \"sale\", \"done\", ..." +msgstr "" +"Valor de estado del objetivo técnico.\n" +"Ej: para pedido de venta \"draft \", \"sale \", \"done \", ..." + +#. module: base_substate +#. odoo-python +#: code:addons/base_substate/models/base_substate_mixin.py:0 +#, python-format +msgid "" +"The substate %(name)s is not defined for the state %(state)s but for " +"%(target_state)s " +msgstr "" +"El subestado %(name)s no está definido para el estado %(state)s sino para " +"%(target_state)s " + +#. module: base_substate +#. odoo-python +#: code:addons/base_substate/models/base_substate_mixin.py:0 +#, python-format +msgid "This substate is not define for this object but for %s" +msgstr "Este subestado no está definido para este objeto sino para %s" + +#~ msgid "Last Modified on" +#~ msgstr "Última Modificación el" diff --git a/base_substate/i18n/it.po b/base_substate/i18n/it.po new file mode 100644 index 0000000000..92b05253b7 --- /dev/null +++ b/base_substate/i18n/it.po @@ -0,0 +1,238 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_substate +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-02-21 18:06+0000\n" +"Last-Translator: mymage \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 5.6.2\n" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__active +msgid "Active" +msgstr "Attivo" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__model +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__model +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__model +msgid "Apply on" +msgstr "Applica a" + +#. module: base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +msgid "Archived" +msgstr "In archivio" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_view +#: model:ir.model,name:base_substate.model_base_substate +#: model:ir.ui.menu,name:base_substate.menu_base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_search +msgid "Base Substate" +msgstr "Substato base" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_type +msgid "Base Substate Type" +msgstr "Tipo substato base" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_mixin +msgid "BaseSubstate Mixin" +msgstr "Mixin substato base" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__description +msgid "Description" +msgstr "Descrizione" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__display_name +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__display_name +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__mail_template_id +msgid "Email Template" +msgstr "Modello e-mail" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__sequence +msgid "Gives the sequence order when applying the default substate" +msgstr "Fornisce la sequenza quando si applica il substato predefinito" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__id +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__id +msgid "ID" +msgstr "ID" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__mail_template_id +msgid "" +"If set, an email will be sent to the partner when the object reaches this " +"substate." +msgstr "" +"Se impostato, verrà mandata una e-mail al partner quando l'oggetto raggiunge " +"questo substato." + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__model +#: model:ir.model.fields,help:base_substate.field_target_state_value__model +msgid "Model for technical use" +msgstr "Modello per uso tecnico" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__name +msgid "Name" +msgstr "Nome" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__sequence +msgid "Sequence" +msgstr "Sequenza" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_mixin__substate_id +msgid "Sub State" +msgstr "Substato" + +#. module: base_substate +#: model:ir.ui.menu,name:base_substate.menu_substate_config +msgid "Sub State Configuration" +msgstr "Configurazione substato" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_type_view +#: model:ir.ui.menu,name:base_substate.menu_base_substate_type +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_search +msgid "Sub State Type" +msgstr "Tipo substato" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__name +msgid "Substate Name" +msgstr "Nome substato" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__base_substate_type_id +msgid "Substate Type" +msgstr "Tipo substato" + +#. module: base_substate +#: model:res.groups,name:base_substate.group_substate_manager +msgid "Substate manager" +msgstr "Responsabile substato" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__target_state_field +msgid "Target State Field" +msgstr "Campo stato obiettivo" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_target_state_value_view +#: model:ir.model,name:base_substate.model_target_state_value +#: model:ir.model.fields,field_description:base_substate.field_base_substate__target_state_value_id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__target_state_value +#: model:ir.ui.menu,name:base_substate.menu_target_state_value +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_search +msgid "Target State Value" +msgstr "Valore stato obiettivo" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__name +msgid "Target state Name" +msgstr "Nome stato obiettivo" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__name +msgid "" +"Target state translateble name.\n" +"Ex: for sale order \"Quotation\", \"Sale order\", \"Locked\"..." +msgstr "" +"Nome traducibile dello stato obiettivo.\n" +"Es: per ordine di vendita \"Preventivo\", \"Ordine di vendita\", " +"\"Bloccato\"..." + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate_type__target_state_field +msgid "" +"Technical target state field name. Ex for sale order \"state\" for other " +"\"status\" ... " +msgstr "" +"Nome tecnico del campo obiettivo dello stato. Es: per ordine di vendita " +"\"state\", per altri \"status\" ... " + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__target_state_value +msgid "" +"Technical target state value.\n" +"Ex: for sale order \"draft\", \"sale\", \"done\", ..." +msgstr "" +"Valore tecnico dello stato obiettivo.\n" +"Es: per ordine di vendita \"draft\", \"sale\", \"done\", ..." + +#. module: base_substate +#. odoo-python +#: code:addons/base_substate/models/base_substate_mixin.py:0 +#, python-format +msgid "" +"The substate %(name)s is not defined for the state %(state)s but for " +"%(target_state)s " +msgstr "" +"Il substato %(name)s non è definito per lo stato %(state)s ma per " +"%(target_state)s " + +#. module: base_substate +#. odoo-python +#: code:addons/base_substate/models/base_substate_mixin.py:0 +#, python-format +msgid "This substate is not define for this object but for %s" +msgstr "Questo sottostato non è definito per questo oggetto ma per %s" + +#~ msgid "Last Modified on" +#~ msgstr "Ultima modifica il" diff --git a/base_substate/i18n/nl.po b/base_substate/i18n/nl.po new file mode 100644 index 0000000000..04338a2d03 --- /dev/null +++ b/base_substate/i18n/nl.po @@ -0,0 +1,216 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_substate +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-06-19 20:25+0000\n" +"Last-Translator: Bosd \n" +"Language-Team: none\n" +"Language: nl\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 5.10.4\n" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__active +msgid "Active" +msgstr "Actief" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__model +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__model +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__model +msgid "Apply on" +msgstr "Toepassen op" + +#. module: base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +msgid "Archived" +msgstr "Gearchiveerd" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_view +#: model:ir.model,name:base_substate.model_base_substate +#: model:ir.ui.menu,name:base_substate.menu_base_substate +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_view_search +msgid "Base Substate" +msgstr "Basis Substatus" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_type +msgid "Base Substate Type" +msgstr "Basis Substatus Type" + +#. module: base_substate +#: model:ir.model,name:base_substate.model_base_substate_mixin +msgid "BaseSubstate Mixin" +msgstr "BaseSubstate Mixin" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_uid +msgid "Created by" +msgstr "Aangemaakt door" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__create_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__create_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__create_date +msgid "Created on" +msgstr "Aangemaakt op" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__description +msgid "Description" +msgstr "Omschrijving" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__display_name +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__display_name +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__display_name +msgid "Display Name" +msgstr "Weergavenaam" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__mail_template_id +msgid "Email Template" +msgstr "E-mailsjabloon" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__sequence +msgid "Gives the sequence order when applying the default substate" +msgstr "Geeft de volgorde aan bij het toepassen van de standaard substatus" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__id +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__id +msgid "ID" +msgstr "ID" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__mail_template_id +msgid "" +"If set, an email will be sent to the partner when the object reaches this " +"substate." +msgstr "" +"Indien ingesteld, wordt er een e-mail naar de partner verzonden wanneer het " +"object deze substatus bereikt." + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_uid +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_uid +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_uid +msgid "Last Updated by" +msgstr "Laatst bijgewerkt door" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__write_date +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__write_date +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__write_date +msgid "Last Updated on" +msgstr "Laatst bijgewerkt op" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate__model +#: model:ir.model.fields,help:base_substate.field_target_state_value__model +msgid "Model for technical use" +msgstr "Model voor technisch gebruik" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__name +msgid "Name" +msgstr "Naam" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__sequence +msgid "Sequence" +msgstr "Volgorde" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_mixin__substate_id +msgid "Sub State" +msgstr "Substatus" + +#. module: base_substate +#: model:ir.ui.menu,name:base_substate.menu_substate_config +msgid "Sub State Configuration" +msgstr "Substatus Configuratie" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_base_substate_type_view +#: model:ir.ui.menu,name:base_substate.menu_base_substate_type +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.base_substate_type_view_search +msgid "Sub State Type" +msgstr "Substatus Type" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate__name +msgid "Substate Name" +msgstr "Naam Substatus" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__base_substate_type_id +msgid "Substate Type" +msgstr "Substatus Type" + +#. module: base_substate +#: model:res.groups,name:base_substate.group_substate_manager +msgid "Substate manager" +msgstr "Substatus beheerder" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_base_substate_type__target_state_field +msgid "Target State Field" +msgstr "Doelstatusveld" + +#. module: base_substate +#: model:ir.actions.act_window,name:base_substate.act_open_target_state_value_view +#: model:ir.model,name:base_substate.model_target_state_value +#: model:ir.model.fields,field_description:base_substate.field_base_substate__target_state_value_id +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__target_state_value +#: model:ir.ui.menu,name:base_substate.menu_target_state_value +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_form +#: model_terms:ir.ui.view,arch_db:base_substate.target_state_value_view_search +msgid "Target State Value" +msgstr "Doelstatuswaarde" + +#. module: base_substate +#: model:ir.model.fields,field_description:base_substate.field_target_state_value__name +msgid "Target state Name" +msgstr "Naam doelstatus" + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__name +msgid "" +"Target state translateble name.\n" +"Ex: for sale order \"Quotation\", \"Sale order\", \"Locked\"..." +msgstr "" +"Vertaalbare naam van de doelstatus.\n" +"Bijv: voor verkooporder \"Offerte\", \"Verkooporder\", \"Vergrendeld\"..." + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_base_substate_type__target_state_field +msgid "" +"Technical target state field name. Ex for sale order \"state\" for other " +"\"status\" ... " +msgstr "" +"Technische naam van het doelstatusveld. Bijv. voor verkooporder \"state\" " +"voor andere \"status\"... " + +#. module: base_substate +#: model:ir.model.fields,help:base_substate.field_target_state_value__target_state_value +msgid "" +"Technical target state value.\n" +"Ex: for sale order \"draft\", \"sale\", \"done\", ..." +msgstr "" +"Technische doelstatuswaarde.\n" +"Bijv: voor verkooporder \"draft\", \"sale\", \"done\", ..." diff --git a/base_substate/models/__init__.py b/base_substate/models/__init__.py new file mode 100644 index 0000000000..6ac2cf86a3 --- /dev/null +++ b/base_substate/models/__init__.py @@ -0,0 +1,2 @@ +from . import base_substate +from . import base_substate_mixin diff --git a/base_substate/models/base_substate.py b/base_substate/models/base_substate.py new file mode 100644 index 0000000000..79706d1b6e --- /dev/null +++ b/base_substate/models/base_substate.py @@ -0,0 +1,103 @@ +# Copyright 2020 Akretion +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class BaseSubstateType(models.Model): + """This model defines technical data which precises + for each target model concerned by substate, + the technical "state" field name. + Data in this model should be created by import as technical data + in the specific module. For example in sale_substate we can define: + base.substate.type: + - name: Sale order Substate + - model: sale.order + - target_state_field: state + """ + + _name = "base.substate.type" + _description = "Base Substate Type" + _order = "name asc, model asc" + + name = fields.Char(required=True, translate=True) + model = fields.Selection(selection=[], string="Apply on", required=True) + target_state_field = fields.Char( + required=True, + help="Technical target state field name." + ' Ex for sale order "state" for other "status" ... ', + ) + + +class TargetStateValue(models.Model): + """This model define technical data that precise the translatable name + of the target model state (ex:Quotation for 'draft' State) + Data in this model should be created by import as technical data + in specific module ex : sale_subsatate + """ + + _name = "target.state.value" + _description = "Target State Value" + _order = "name asc" + + name = fields.Char( + "Target state Name", + required=True, + translate=True, + help="Target state translateble name.\n" + 'Ex: for sale order "Quotation", "Sale order", "Locked"...', + ) + base_substate_type_id = fields.Many2one( + "base.substate.type", + string="Substate Type", + ondelete="restrict", + ) + target_state_value = fields.Char( + required=True, + help="Technical target state value.\n" + 'Ex: for sale order "draft", "sale", "done", ...', + ) + model = fields.Selection( + related="base_substate_type_id.model", + store=True, + readonly=True, + help="Model for technical use", + ) + + +class BaseSubstate(models.Model): + """This model define substates that will be applied on the target model. + for each state we can define one or more substate. + ex: + for the quotation state of a sale order we can define + 3 substates "In negotiation", + "Won" and "Lost". + We can also send mail when the susbstate is reached. + """ + + _name = "base.substate" + _description = "Base Substate" + _order = "active desc, sequence asc" + + name = fields.Char("Substate Name", required=True, translate=True) + description = fields.Text(translate=True) + sequence = fields.Integer( + index=True, + help="Gives the sequence order when applying the default substate", + ) + target_state_value_id = fields.Many2one( + "target.state.value", string="Target State Value", ondelete="restrict" + ) + active = fields.Boolean(default=True) + mail_template_id = fields.Many2one( + "mail.template", + string="Email Template", + help="If set, an email will be sent to the partner " + "when the object reaches this substate.", + ) + model = fields.Selection( + related="target_state_value_id.model", + store=True, + readonly=True, + help="Model for technical use", + ) diff --git a/base_substate/models/base_substate_mixin.py b/base_substate/models/base_substate_mixin.py new file mode 100644 index 0000000000..95ed114f0a --- /dev/null +++ b/base_substate/models/base_substate_mixin.py @@ -0,0 +1,140 @@ +# Copyright 2020 Akretion +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.exceptions import ValidationError +from odoo.fields import Domain + + +class BaseSubstateMixin(models.AbstractModel): + _name = "base.substate.mixin" + _description = "BaseSubstate Mixin" + _state_field = "state" + + @api.constrains("substate_id", _state_field) + def check_substate_id_value(self): + if self.env.context.get("skip_substate_while_state_field_computation"): + return + rec_states = dict(self._fields[self._state_field].selection) + for rec in self: + target_state = rec.substate_id.target_state_value_id.target_state_value + if rec.substate_id and rec.state != target_state: + raise ValidationError( + self.env._( + "Substate %(substate_name)s not defined for " + "state %(state_name)s but for %(target_state_name)s", + substate_name=rec.substate_id.name, + state_name=rec_states[rec.state], + target_state_name=rec_states[target_state], + ) + ) + + def _get_default_substate_id(self, state_val=False): + """Gives default substate_id""" + search_domain = self._get_default_substate_domain(state_val) + # perform search, return the first found + return ( + self.env["base.substate"] + .search(search_domain, order="sequence", limit=1) + .id + ) + + def _get_default_substate_domain(self, state_val=False): + """Override this method + to change domain values + """ + if not state_val: + state_val = self._get_default_state_value() + substate_type = self._get_substate_type() + state_field = substate_type.target_state_field + if self and not state_val and state_field in self._fields: + state_val = self[state_field] + + domain = Domain( + "target_state_value_id.target_state_value", "=", state_val + ) & Domain("target_state_value_id.base_substate_type_id", "=", substate_type.id) + return domain + + def _get_default_state_value(self): + """Override this method to change state_value""" + return "draft" + + def _get_substate_type(self): + """Override this method to change substate_type (get by xml id for example)""" + return self.env["base.substate.type"].search( + Domain("model", "=", self._name), limit=1 + ) + + substate_id = fields.Many2one( + "base.substate", + string="Sub State", + ondelete="restrict", + default=lambda self: self._get_default_substate_id(), + index=True, + domain=lambda self: Domain("model", "=", self._name), + copy=False, + tracking=True, + ) + + @api.constrains("substate_id") + def check_substate_id_consistency(self): + for mixin_obj in self: + if mixin_obj.substate_id and mixin_obj.substate_id.model != self._name: + raise ValidationError( + self.env._( + "Substate not for this object but for %(model_name)s", + model_name=mixin_obj.substate_id.model, + ) + ) + + def _get_state_field(self): + substate_type = self._get_substate_type() + return substate_type.target_state_field + + def _update_before_write_create(self, values): + state_field = self._get_state_field() + if values.get(state_field) and not values.get("substate_id"): + state_val = values.get(state_field) + values["substate_id"] = self._get_default_substate_id(state_val) + # Send mail if substate has mail template + if values.get("substate_id"): + substate = self.env["base.substate"].browse(values["substate_id"]) + if hasattr(self, "message_post_with_source") and substate.mail_template_id: + self.message_post_with_source( + substate.mail_template_id, + message_type="comment", + subtype_id=self.env["ir.model.data"]._xmlid_to_res_id( + "mail.mt_note" + ), + ) + return values + + def write(self, values): + values = self._update_before_write_create(values) + res = super().write(values) + return res + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + vals = self._update_before_write_create(vals) + res = super().create(vals_list) + return res + + def _compute_field_value(self, field): + # OVERRIDE: if ``_state_field`` is computed, it does not go through + # write method, so we need to set substate in _compute_field_value + state_field = self._get_state_field() + # Store former values for all records in the recordset + former_values = {rec.id: rec[state_field] for rec in self} + res = super( + BaseSubstateMixin, + self.with_context(skip_substate_while_state_field_computation=True), + )._compute_field_value(field) + # Update substate for records where state changed + if field.name == state_field: + for rec in self: + new_value = rec[state_field] + if former_values.get(rec.id) != new_value: + rec.substate_id = rec._get_default_substate_id(new_value) + return res diff --git a/base_substate/pyproject.toml b/base_substate/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/base_substate/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_substate/readme/CONTRIBUTORS.md b/base_substate/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..b7d7ee53e9 --- /dev/null +++ b/base_substate/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +- Mourad EL HADJ MIMOUNE \<\> +- Kitti U. \<\> +- Alexei Rivera \<\> (migration to 15.0) +- Saran Lim. \ No newline at end of file diff --git a/base_substate/readme/DESCRIPTION.md b/base_substate/readme/DESCRIPTION.md new file mode 100644 index 0000000000..d00de1535b --- /dev/null +++ b/base_substate/readme/DESCRIPTION.md @@ -0,0 +1,11 @@ +This module provide abstract models to manage customizable substates to +be applied on different models (sale order, purchase, ...). + +## example: + +- for the quotation state of a sale order we can define 3 substates "In + negotiation", "Won" and "Lost". +- We can also send mail when the substate is reached. + +It is not useful by itself. You can see an example of implementation in +the 'purchase_substate' module. (purchase-workflow repository). diff --git a/base_substate/readme/USAGE.md b/base_substate/readme/USAGE.md new file mode 100644 index 0000000000..bc9abb0944 --- /dev/null +++ b/base_substate/readme/USAGE.md @@ -0,0 +1,2 @@ +1. You must install an application module depending this one (for + example purchase_substate) diff --git a/base_substate/security/base_substate_security.xml b/base_substate/security/base_substate_security.xml new file mode 100644 index 0000000000..4f98ee90ea --- /dev/null +++ b/base_substate/security/base_substate_security.xml @@ -0,0 +1,7 @@ + + + + Substate manager + + + diff --git a/base_substate/security/ir.model.access.csv b/base_substate/security/ir.model.access.csv new file mode 100644 index 0000000000..4cdb1a9ebd --- /dev/null +++ b/base_substate/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_base_substate_type,base.substate.type user,model_base_substate_type,base.group_user,1,0,0,0 +access_base_substate_type_manager,base.substate.type manager,model_base_substate_type,base_substate.group_substate_manager,1,1,1,1 +access_target_state_value,base.substate.value user,model_target_state_value,base.group_user,1,0,0,0 +access_target_state_value_manager,base.substate.value manager,model_target_state_value,base_substate.group_substate_manager,1,1,1,1 +access_base_substate,base.substate user,model_base_substate,base.group_user,1,0,0,0 +access_base_substate_manager,base.substate manager,model_base_substate,base_substate.group_substate_manager,1,1,1,1 diff --git a/base_substate/static/description/icon.png b/base_substate/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/base_substate/static/description/icon.png differ diff --git a/base_substate/static/description/index.html b/base_substate/static/description/index.html new file mode 100644 index 0000000000..bf7a17dfeb --- /dev/null +++ b/base_substate/static/description/index.html @@ -0,0 +1,446 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Base Sub State

+ +

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

+

This module provide abstract models to manage customizable substates to +be applied on different models (sale order, purchase, …).

+
+

example:

+
    +
  • for the quotation state of a sale order we can define 3 substates “In +negotiation”, “Won” and “Lost”.
  • +
  • We can also send mail when the substate is reached.
  • +
+

It is not useful by itself. You can see an example of implementation in +the ‘purchase_substate’ module. (purchase-workflow repository).

+

Table of contents

+ +
+

Usage

+
    +
  1. You must install an application module depending this one (for +example purchase_substate)
  2. +
+
+
+

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.

+
+ +
+
+

Authors

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

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.

+

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

+

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

+
+
+
+ + diff --git a/base_substate/tests/__init__.py b/base_substate/tests/__init__.py new file mode 100644 index 0000000000..6486f3f539 --- /dev/null +++ b/base_substate/tests/__init__.py @@ -0,0 +1,2 @@ +from . import common +from . import test_base_substate diff --git a/base_substate/tests/common.py b/base_substate/tests/common.py new file mode 100644 index 0000000000..39856fe244 --- /dev/null +++ b/base_substate/tests/common.py @@ -0,0 +1,61 @@ +# Copyright 2025 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo.orm.model_classes import add_to_registry + +from odoo.addons.base.tests.common import BaseCommon + + +class CommonBaseSubstate(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + from .sale_test import ( + BaseSubstateType, + LineTest, + SaleTest, + ) + + add_to_registry(cls.registry, SaleTest) + add_to_registry(cls.registry, LineTest) + add_to_registry(cls.registry, BaseSubstateType) + + cls.registry._setup_models__( + cls.env.cr, + ["base.substate.test.sale", "base.substate.test.sale.line"], + ) + cls.registry.init_models( + cls.env.cr, + ["base.substate.test.sale", "base.substate.test.sale.line"], + {"models_to_check": True}, + ) + + cls.sale_test_model = cls.env[SaleTest._name] + cls.sale_line_test_model = cls.env[LineTest._name] + + models = cls.env["ir.model"].search( + [ + ( + "model", + "in", + ["base.substate.test.sale", "base.substate.test.sale.line"], + ) + ] + ) + for model in models: + cls.env["ir.model.access"].create( + { + "name": f"access {model.name}", + "model_id": model.id, + "perm_read": 1, + "perm_write": 1, + "perm_create": 1, + "perm_unlink": 1, + } + ) + + @classmethod + def tearDownClass(cls): + cls.addClassCleanup(cls.registry.__delitem__, "base.substate.test.sale") + cls.addClassCleanup(cls.registry.__delitem__, "base.substate.test.sale.line") + return super().tearDownClass() diff --git a/base_substate/tests/sale_test.py b/base_substate/tests/sale_test.py new file mode 100644 index 0000000000..d108801545 --- /dev/null +++ b/base_substate/tests/sale_test.py @@ -0,0 +1,75 @@ +# Copyright 2020 Akretion Mourad EL HADJ MIMOUNE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class BaseSubstateType(models.Model): + _inherit = "base.substate.type" + + model = fields.Selection( + selection_add=[("base.substate.test.sale", "Sale Test")], + ondelete={"base.substate.test.sale": "cascade"}, + ) + + +class SaleTest(models.Model): + _name = "base.substate.test.sale" + _inherit = ["base.substate.mixin", "mail.thread"] + _description = "Base substate Test Model" + + name = fields.Char(required=True) + user_id = fields.Many2one("res.users", string="Responsible") + state = fields.Selection( + [("draft", "New"), ("cancel", "Cancelled"), ("sale", "Sale"), ("done", "Done")], + string="Status", + readonly=True, + default="draft", + compute="_compute_state", + store=True, + ) + active = fields.Boolean(default=True) + partner_id = fields.Many2one("res.partner", string="Partner") + line_ids = fields.One2many( + comodel_name="base.substate.test.sale.line", + inverse_name="sale_id", + context={"active_test": False}, + ) + amount_total = fields.Float(compute="_compute_amount_total", store=True) + + @api.depends("line_ids") + def _compute_amount_total(self): + self.amount_total = 0 + for record in self: + for line in record.line_ids: + record.amount_total += line.amount * line.qty + + def button_confirm(self): + self.write({"state": "sale"}) + return True + + def button_cancel(self): + self.write({"state": "cancel"}) + + @api.depends("line_ids.done") + def _compute_state(self): + for record in self: + if record.line_ids and all(record.mapped("line_ids.done")): + record.state = "done" + + +class LineTest(models.Model): + _name = "base.substate.test.sale.line" + _description = "Base substate Test Model Line" + + name = fields.Char() + sale_id = fields.Many2one( + comodel_name="base.substate.test.sale", + ondelete="cascade", + context={"active_test": False}, + ) + qty = fields.Float() + amount = fields.Float() + done = fields.Boolean(default=False) + + def button_done(self): + self.done = True diff --git a/base_substate/tests/test_base_substate.py b/base_substate/tests/test_base_substate.py new file mode 100644 index 0000000000..f0feb3c5ff --- /dev/null +++ b/base_substate/tests/test_base_substate.py @@ -0,0 +1,159 @@ +# Copyright 2020 Akretion Mourad EL HADJ MIMOUNE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import Command +from odoo.tests import tagged + +from .common import CommonBaseSubstate + + +@tagged("post_install", "-at_install", "mi_tag") +class TestBaseSubstate(CommonBaseSubstate): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.substate_type = cls.env["base.substate.type"] + cls.base_substate = cls.env["base.substate"] + + cls.mail_template = cls.env["mail.template"].create( + { + "name": "Waiting for legal documents", + "model_id": cls.env["ir.model"]._get(cls.sale_test_model._name).id, + "subject": "Test Email Substate", + } + ) + cls.sale_test_substate_type = cls.substate_type.create( + { + "name": "Sale", + "model": "base.substate.test.sale", + "target_state_field": "state", + } + ) + + cls.substate_val_quotation = cls.env["target.state.value"].create( + { + "name": "Quotation", + "base_substate_type_id": cls.sale_test_substate_type.id, + "target_state_value": "draft", + } + ) + + cls.substate_val_sale = cls.env["target.state.value"].create( + { + "name": "Sale order", + "base_substate_type_id": cls.sale_test_substate_type.id, + "target_state_value": "sale", + } + ) + + cls.substate_val_done = cls.env["target.state.value"].create( + { + "name": "Done", + "base_substate_type_id": cls.sale_test_substate_type.id, + "target_state_value": "done", + } + ) + + cls.substate_under_negotiation = cls.base_substate.create( + { + "name": "Under negotiation", + "sequence": 1, + "target_state_value_id": cls.substate_val_quotation.id, + } + ) + + cls.substate_won = cls.base_substate.create( + { + "name": "Won", + "sequence": 1, + "target_state_value_id": cls.substate_val_quotation.id, + } + ) + + cls.substate_wait_docs = cls.base_substate.create( + { + "name": "Waiting for legal documents", + "sequence": 2, + "target_state_value_id": cls.substate_val_sale.id, + "mail_template_id": cls.mail_template.id, + } + ) + + cls.substate_valid_docs = cls.base_substate.create( + { + "name": "To validate legal documents", + "sequence": 3, + "target_state_value_id": cls.substate_val_sale.id, + } + ) + + cls.substate_in_delivering = cls.base_substate.create( + { + "name": "In delivering", + "sequence": 4, + "target_state_value_id": cls.substate_val_sale.id, + } + ) + + cls.substate_to_publish = cls.base_substate.create( + { + "name": "To publish", + "sequence": 5, + "target_state_value_id": cls.substate_val_done.id, + } + ) + + cls.substate_published = cls.base_substate.create( + { + "name": "Published", + "sequence": 6, + "target_state_value_id": cls.substate_val_done.id, + } + ) + + def test_sale_order_substate(self): + # Create a partner instead of using a potentially non-existent XML ID + partner = self.env["res.partner"].create( + { + "name": "Test Partner", + "email": "test@example.com", + } + ) + so_test1 = self.sale_test_model.create( + { + "name": "Test base substate to basic sale", + "partner_id": partner.id, + "line_ids": [ + Command.create({"name": "line test", "amount": 120.0, "qty": 1.5}) + ], + } + ) + self.assertTrue(so_test1.state == "draft") + self.assertTrue(so_test1.substate_id == self.substate_under_negotiation) + self.assertNotIn( + self.mail_template.subject, so_test1.message_ids.mapped("subject") + ) + # Test that validation of sale order change substate_id + so_test1.button_confirm() + self.assertTrue(so_test1.state == "sale") + self.assertTrue(so_test1.substate_id == self.substate_wait_docs) + # Check some message_ids are created and sent email + self.assertIn( + self.mail_template.subject, so_test1.message_ids.mapped("subject") + ) + # Test that computation of sale order state change substate_id + so_test1.line_ids.button_done() + self.assertEqual(so_test1.state, "done") + self.assertEqual(so_test1.substate_id, self.substate_to_publish) + # If the state computation does not change its value, + # it should not change the substate + so_test1.substate_id = self.substate_published + so_test1.line_ids.button_done() # Triggers the recomputation despite no change + self.assertEqual(so_test1.state, "done") + self.assertEqual(so_test1.substate_id, self.substate_published) + # Test that substate_id is set to false if + # there is not substate corresponding to state + so_test1.button_cancel() + self.assertTrue(so_test1.state == "cancel") + self.assertTrue(not so_test1.substate_id) diff --git a/base_substate/views/base_substate_type_views.xml b/base_substate/views/base_substate_type_views.xml new file mode 100644 index 0000000000..29e5eadc15 --- /dev/null +++ b/base_substate/views/base_substate_type_views.xml @@ -0,0 +1,85 @@ + + + + + base.substate.type + + + + + + + + + + base.substate.type + +
+ +
+
+ + + + + + +
+
+
+
+ + base.substate.type + + + + + + + + + + Sub State Type + ir.actions.act_window + base.substate.type + list,form + + [] + {} + + + + + form + + + + + + list + + + + +
diff --git a/base_substate/views/base_substate_value_views.xml b/base_substate/views/base_substate_value_views.xml new file mode 100644 index 0000000000..f77950e22b --- /dev/null +++ b/base_substate/views/base_substate_value_views.xml @@ -0,0 +1,83 @@ + + + + + target.state.value + + + + + + + + + + target.state.value + +
+ +
+
+ + + + + + +
+
+
+
+ + target.state.value + + + + + + + + + + Target State Value + ir.actions.act_window + target.state.value + list,form + + [] + {} + + + + + form + + + + + + list + + + +
diff --git a/base_substate/views/base_substate_views.xml b/base_substate/views/base_substate_views.xml new file mode 100644 index 0000000000..0a4a15b87b --- /dev/null +++ b/base_substate/views/base_substate_views.xml @@ -0,0 +1,86 @@ + + + + + base.substate + + + + + + + + + + + + base.substate + +
+ + +
+
+ + + + + + + + + + +
+
+
+
+ + base.substate + + + + + + + + Base Substate + ir.actions.act_window + base.substate + list,form + + [] + {} + + + + + form + + + + + + list + + + +
diff --git a/base_technical_features/README.rst b/base_technical_features/README.rst new file mode 100644 index 0000000000..5ac4447036 --- /dev/null +++ b/base_technical_features/README.rst @@ -0,0 +1,117 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +======================== +Technical features group +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:439d0d83c06b14233f634809ba6039f8d06beb638ceab71c029f08df0d43166e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/19.0/base_technical_features + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-19-0/server-ux-19-0-base_technical_features + :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/server-ux&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module provides persistent access to technical features based on +user preferences, eliminating the need to activate debug mode. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +After installation of this module, every employee can still access +technical features for the applications that they have access to by +enabling debug mode. + +Additionally, users can check the *Technical feature* field in their +preferences to gain permanent access to the menus and views that fall +under this category. + +|image1| + +Upon installation of this module, this preference is already set for the +administrator user of the database. + +In the background, this preference is mapped to the *Technical feature +(w/o debug mode)* group that this module adds. As an administrator, you +can therefore manage this preference from the regular Users and Groups +menu items. + +.. |image1| image:: https://raw.githubusercontent.com/OCA/server-ux/19.0/base_technical_features/static/description/user_preferences.png + +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 +------- + +* Opener B.V. + +Contributors +------------ + +- Stefan Rijnhart +- Jeroen Evens +- Jim Hoefnagels +- Khoi (Kien Kim) +- Tris Doan +- Iván Todorovich + +Other credits +------------- + +The migration of this module from 18.0 to 19.0 was financially supported +by: + +- Camptocamp. + +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/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_technical_features/__init__.py b/base_technical_features/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/base_technical_features/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_technical_features/__manifest__.py b/base_technical_features/__manifest__.py new file mode 100644 index 0000000000..3dfbb6a730 --- /dev/null +++ b/base_technical_features/__manifest__.py @@ -0,0 +1,13 @@ +# © 2016 Opener B.V. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Technical features group", + "summary": "Access to technical features without activating debug mode", + "version": "19.0.1.0.0", + "category": "Usability", + "website": "https://github.com/OCA/server-ux", + "author": "Opener B.V., Odoo Community Association (OCA)", + "data": ["security/res_groups.xml", "views/res_users.xml", "data/res_users.xml"], + "license": "AGPL-3", + "depends": ["base"], +} diff --git a/base_technical_features/data/res_users.xml b/base_technical_features/data/res_users.xml new file mode 100644 index 0000000000..c0e6544f3b --- /dev/null +++ b/base_technical_features/data/res_users.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/base_technical_features/i18n/am.po b/base_technical_features/i18n/am.po new file mode 100644 index 0000000000..35af40acd0 --- /dev/null +++ b/base_technical_features/i18n/am.po @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: server-tools (9.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-09-10 02:52+0000\n" +"PO-Revision-Date: 2016-01-05 19:43+0000\n" +"Last-Translator: <>\n" +"Language-Team: Amharic (http://www.transifex.com/oca/OCA-server-tools-9-0/" +"language/am/)\n" +"Language: am\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "ID" +#~ msgstr "ID" diff --git a/base_technical_features/i18n/ar.po b/base_technical_features/i18n/ar.po new file mode 100644 index 0000000000..bc9fbcc798 --- /dev/null +++ b/base_technical_features/i18n/ar.po @@ -0,0 +1,69 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-18 02:29+0000\n" +"PO-Revision-Date: 2018-07-01 04:01+0000\n" +"Last-Translator: Osoul \n" +"Language-Team: Arabic (https://www.transifex.com/oca/teams/23907/ar/)\n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" +"X-Generator: Weblate 3.0.1\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "إظهار خانة المزايا التقنية" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "المزايا التقنية" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "المزايا التقنية (بدون وضع التطوير)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "هذا المستخدم ليس لديه صلاحية للمزايا التقنية." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "المستخدمون" + +#~ msgid "base" +#~ msgstr "الأساس" diff --git a/base_technical_features/i18n/base_technical_features.pot b/base_technical_features/i18n/base_technical_features.pot new file mode 100644 index 0000000000..602fdaf883 --- /dev/null +++ b/base_technical_features/i18n/base_technical_features.pot @@ -0,0 +1,58 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_ir_ui_menu__display_name +#: model:ir.model.fields,field_description:base_technical_features.field_ir_ui_view__display_name +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__display_name +msgid "Display Name" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_ir_ui_menu__id +#: model:ir.model.fields,field_description:base_technical_features.field_ir_ui_view__id +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__id +msgid "ID" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_view +msgid "View" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__technical_features +msgid "Whether the user has technical features always enabled." +msgstr "" diff --git a/base_technical_features/i18n/ca.po b/base_technical_features/i18n/ca.po new file mode 100644 index 0000000000..d1d368bf82 --- /dev/null +++ b/base_technical_features/i18n/ca.po @@ -0,0 +1,73 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-08-01 02:43+0000\n" +"PO-Revision-Date: 2018-10-10 21:20+0000\n" +"Last-Translator: Harald Panten \n" +"Language-Team: Catalan (https://www.transifex.com/oca/teams/23907/ca/)\n" +"Language: ca\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 3.1.1\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Mostrar el camp Característiques Tècniques" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Característiques tècniques" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Característiques tècniques (sense mode de depuració)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "L'usuari no té accés a les carcaterístiques tècniques." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" +"Mostrar o no el camp de característiques tècniques a les preferències " +"d'usuari." + +#~ msgid "Users" +#~ msgstr "Usuaris" + +#~ msgid "base" +#~ msgstr "base de dades" + +#~ msgid "ir.ui.menu" +#~ msgstr "ir.ui.menu" diff --git a/base_technical_features/i18n/da.po b/base_technical_features/i18n/da.po new file mode 100644 index 0000000000..e528125d6f --- /dev/null +++ b/base_technical_features/i18n/da.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-18 02:29+0000\n" +"PO-Revision-Date: 2018-09-06 08:12+0000\n" +"Last-Translator: Hans Henrik Gabelgaard \n" +"Language-Team: Danish (https://www.transifex.com/oca/teams/23907/da/)\n" +"Language: da\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 3.1.1\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Vis feltet Tekniske features" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Tekniske features" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Tekniske feature (uden debug mode)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "Denne bruger har ikke adgang til Tekniske features." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "Om feltet Tekniske features skal vises i bruger egenskaber." + +#~ msgid "Users" +#~ msgstr "Brugere" diff --git a/base_technical_features/i18n/de.po b/base_technical_features/i18n/de.po new file mode 100644 index 0000000000..8c06e0dbb4 --- /dev/null +++ b/base_technical_features/i18n/de.po @@ -0,0 +1,73 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-21 04:22+0000\n" +"PO-Revision-Date: 2017-01-21 04:22+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n" +"Language: de\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Feld Technische Eigenschaften anzeigen" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +#, fuzzy +msgid "Technical Features" +msgstr "Technische Eigenschaften" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Technische Eigenschaften (ohne Debugmodus)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "Der Benutzer hat keinen Zugriff auf Technische Eigenschaften." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" +"Ob das Feld Technische Eigenschaften in den Benutzereinstellungen angezeigt " +"wird." + +#~ msgid "Users" +#~ msgstr "Benutzer" + +#~ msgid "base" +#~ msgstr "base" + +#~ msgid "ir.ui.menu" +#~ msgstr "ir.ui.menu" diff --git a/base_technical_features/i18n/el_GR.po b/base_technical_features/i18n/el_GR.po new file mode 100644 index 0000000000..a3eb7e072a --- /dev/null +++ b/base_technical_features/i18n/el_GR.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-18 02:29+0000\n" +"PO-Revision-Date: 2017-02-18 02:29+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Greek (Greece) (https://www.transifex.com/oca/teams/23907/" +"el_GR/)\n" +"Language: el_GR\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Χρήστες" diff --git a/base_technical_features/i18n/es.po b/base_technical_features/i18n/es.po new file mode 100644 index 0000000000..57540e81cc --- /dev/null +++ b/base_technical_features/i18n/es.po @@ -0,0 +1,73 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-21 04:22+0000\n" +"PO-Revision-Date: 2023-08-27 16:07+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\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" +"X-Generator: Weblate 4.17\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "Base" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "Menú" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Mostrar el campo Características Técnicas" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Características técnicas" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Características técnicas (sin modo de depuración)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "El usuario no tiene acceso a las características técnicas." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "Usuario" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" +"Mostrar o no el campo de características técnicas en las preferencias de " +"usuario." + +#~ msgid "Users" +#~ msgstr "Usuarios" + +#~ msgid "base" +#~ msgstr "base de datos" + +#~ msgid "ir.ui.menu" +#~ msgstr "ir.ui.menu" diff --git a/base_technical_features/i18n/es_ES.po b/base_technical_features/i18n/es_ES.po new file mode 100644 index 0000000000..89586826e4 --- /dev/null +++ b/base_technical_features/i18n/es_ES.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-22 00:54+0000\n" +"PO-Revision-Date: 2017-02-22 00:54+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Spanish (Spain) (https://www.transifex.com/oca/teams/23907/" +"es_ES/)\n" +"Language: es_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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Usuarios" diff --git a/base_technical_features/i18n/es_MX.po b/base_technical_features/i18n/es_MX.po new file mode 100644 index 0000000000..7708c36443 --- /dev/null +++ b/base_technical_features/i18n/es_MX.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-12-23 02:01+0000\n" +"PO-Revision-Date: 2016-12-23 02:01+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: Spanish (Mexico) (https://www.transifex.com/oca/teams/23907/" +"es_MX/)\n" +"Language: es_MX\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Module" +#~ msgstr "Módulo" diff --git a/base_technical_features/i18n/fi.po b/base_technical_features/i18n/fi.po new file mode 100644 index 0000000000..e883079d50 --- /dev/null +++ b/base_technical_features/i18n/fi.po @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-18 02:29+0000\n" +"PO-Revision-Date: 2017-02-18 02:29+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Finnish (https://www.transifex.com/oca/teams/23907/fi/)\n" +"Language: fi\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Käyttäjät" diff --git a/base_technical_features/i18n/fr.po b/base_technical_features/i18n/fr.po new file mode 100644 index 0000000000..b5dc5d27c5 --- /dev/null +++ b/base_technical_features/i18n/fr.po @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-21 04:22+0000\n" +"PO-Revision-Date: 2017-01-21 04:22+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" +"Language: fr\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Utilisateurs" diff --git a/base_technical_features/i18n/fr_CA.po b/base_technical_features/i18n/fr_CA.po new file mode 100644 index 0000000000..67d70019d0 --- /dev/null +++ b/base_technical_features/i18n/fr_CA.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-12-23 02:01+0000\n" +"PO-Revision-Date: 2016-12-23 02:01+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: French (Canada) (https://www.transifex.com/oca/teams/23907/" +"fr_CA/)\n" +"Language: fr_CA\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Fields" +#~ msgstr "Champs" diff --git a/base_technical_features/i18n/fr_CH.po b/base_technical_features/i18n/fr_CH.po new file mode 100644 index 0000000000..e402b08e03 --- /dev/null +++ b/base_technical_features/i18n/fr_CH.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-18 02:29+0000\n" +"PO-Revision-Date: 2017-02-18 02:29+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: French (Switzerland) (https://www.transifex.com/oca/" +"teams/23907/fr_CH/)\n" +"Language: fr_CH\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Utilisateurs" diff --git a/base_technical_features/i18n/fr_FR.po b/base_technical_features/i18n/fr_FR.po new file mode 100644 index 0000000000..44069eec3f --- /dev/null +++ b/base_technical_features/i18n/fr_FR.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# Aurel , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-08 03:37+0000\n" +"PO-Revision-Date: 2017-02-08 03:37+0000\n" +"Last-Translator: Aurel , 2017\n" +"Language-Team: French (France) (https://www.transifex.com/oca/teams/23907/" +"fr_FR/)\n" +"Language: fr_FR\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Utilsateurs" diff --git a/base_technical_features/i18n/gl.po b/base_technical_features/i18n/gl.po new file mode 100644 index 0000000000..03b3b976b5 --- /dev/null +++ b/base_technical_features/i18n/gl.po @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: server-tools (9.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-09-10 02:52+0000\n" +"PO-Revision-Date: 2016-01-05 19:43+0000\n" +"Last-Translator: <>\n" +"Language-Team: Galician (http://www.transifex.com/oca/OCA-server-tools-9-0/" +"language/gl/)\n" +"Language: gl\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "ID" +#~ msgstr "ID" diff --git a/base_technical_features/i18n/hr.po b/base_technical_features/i18n/hr.po new file mode 100644 index 0000000000..d97a842bae --- /dev/null +++ b/base_technical_features/i18n/hr.po @@ -0,0 +1,73 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +# Bole , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-05-01 10:38+0000\n" +"PO-Revision-Date: 2019-12-04 12:04+0000\n" +"Last-Translator: Bole \n" +"Language-Team: Croatian (https://www.transifex.com/oca/teams/23907/hr/)\n" +"Language: hr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 3.9.1\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "Osnova" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "Izbornik" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Prikaži polje Tehničke značajke" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Tehničke značajke" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Tehničke značajke ( bez debug opcije)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "Ovaj korisnik nema prava pristupa za Tehničke značajke" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "Prikaz polja tehničkih značajki u korisničkim postavkama." + +#~ msgid "Users" +#~ msgstr "Korisnici" + +#~ msgid "base" +#~ msgstr "base" + +#~ msgid "ir.ui.menu" +#~ msgstr "ir.ui.menu" diff --git a/base_technical_features/i18n/hr_HR.po b/base_technical_features/i18n/hr_HR.po new file mode 100644 index 0000000000..09ec3e1d91 --- /dev/null +++ b/base_technical_features/i18n/hr_HR.po @@ -0,0 +1,70 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-26 03:36+0000\n" +"PO-Revision-Date: 2016-11-26 03:36+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: Croatian (Croatia) (https://www.transifex.com/oca/teams/23907/" +"hr_HR/)\n" +"Language: hr_HR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Prikaži polje Tehničke značajke" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +#, fuzzy +msgid "Technical Features" +msgstr "Tehničke značajke" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Tehničke značajke (bez debug načina)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "Korisnik nema prava pristupa za tehničke značajke." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "Pokazati polja tehničke značajke u postavkama korisnika." + +#~ msgid "Users" +#~ msgstr "Korisnici" + +#~ msgid "ir.ui.menu" +#~ msgstr "ir.ui.menu" diff --git a/base_technical_features/i18n/it.po b/base_technical_features/i18n/it.po new file mode 100644 index 0000000000..b8b184fa93 --- /dev/null +++ b/base_technical_features/i18n/it.po @@ -0,0 +1,72 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-21 04:22+0000\n" +"PO-Revision-Date: 2024-02-04 22:33+0000\n" +"Last-Translator: mymage \n" +"Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\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: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "Base" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "Menù" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Mostrare campo funzioni tecniche" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Funzioni tecniche" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Funzioni tecniche (senza modalità di debug)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "L'utente non ha accesso alle funzioni tecniche." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "Utente" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" +"Indica se visualizzare il campo funzioni tecniche nelle preferenze utente." + +#~ msgid "Users" +#~ msgstr "Utenti" + +#~ msgid "base" +#~ msgstr "base" + +#~ msgid "ir.ui.menu" +#~ msgstr "ir.ui.menu" diff --git a/base_technical_features/i18n/ja.po b/base_technical_features/i18n/ja.po new file mode 100644 index 0000000000..7d36085cad --- /dev/null +++ b/base_technical_features/i18n/ja.po @@ -0,0 +1,62 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2020-09-27 11:00+0000\n" +"Last-Translator: Yoshi Tashiro \n" +"Language-Team: none\n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 3.10\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "メニュー" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "技術機能項目を表示" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "技術機能" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "技術機能(開発者モードの有効化なし)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "ユーザは技術機能へのアクセスがありません。" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "ユーザ個人設定に技術機能項目を表示するか否か。" + +#~ msgid "Users" +#~ msgstr "ユーザ" diff --git a/base_technical_features/i18n/nl.po b/base_technical_features/i18n/nl.po new file mode 100644 index 0000000000..6b6bd3d35a --- /dev/null +++ b/base_technical_features/i18n/nl.po @@ -0,0 +1,67 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-18 02:29+0000\n" +"PO-Revision-Date: 2025-02-04 21:02+0000\n" +"Last-Translator: Bosd \n" +"Language-Team: Dutch (https://www.transifex.com/oca/teams/23907/nl/)\n" +"Language: nl\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 5.6.2\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "Basis" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "Menu" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Toon veld Technische mogelijkheden" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Technische mogelijkheden" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Technische mogelijkheden (zonder debugmodus)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "De gebruiker heeft geen toegang tot technische mogelijkheden." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "Gebruiker" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" +"Geeft aan of het veld Technische mogelijkheden wordt getoond in de " +"voorkeuren van de gebruiker." + +#~ msgid "Users" +#~ msgstr "Gebruikers" diff --git a/base_technical_features/i18n/nl_NL.po b/base_technical_features/i18n/nl_NL.po new file mode 100644 index 0000000000..dc0b74d871 --- /dev/null +++ b/base_technical_features/i18n/nl_NL.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# Peter Hageman , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-06-22 01:12+0000\n" +"PO-Revision-Date: 2017-06-22 01:12+0000\n" +"Last-Translator: Peter Hageman , 2017\n" +"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/" +"teams/23907/nl_NL/)\n" +"Language: nl_NL\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Gebruikers" diff --git a/base_technical_features/i18n/pt.po b/base_technical_features/i18n/pt.po new file mode 100644 index 0000000000..8a3808b067 --- /dev/null +++ b/base_technical_features/i18n/pt.po @@ -0,0 +1,67 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# Pedro Castro Silva , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-08-01 02:43+0000\n" +"PO-Revision-Date: 2019-07-05 16:42+0000\n" +"Last-Translator: Pedro Castro Silva \n" +"Language-Team: Portuguese (https://www.transifex.com/oca/teams/23907/pt/)\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" +"X-Generator: Weblate 3.7.1\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "Menu" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "Menu" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Mostrar campo Funcionalidades Técnicas" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Funcionalidades Técnicas" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Funcionalidades Técnicas (s/ modo de depuração)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "O utilizador não tem acesso às funcionalidades técnicas." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" +"Indica se o campo funcionalidades técnicas será exibido nas preferências de " +"utilizador." + +#~ msgid "Users" +#~ msgstr "Utilizadores" diff --git a/base_technical_features/i18n/pt_BR.po b/base_technical_features/i18n/pt_BR.po new file mode 100644 index 0000000000..d1b8d863cc --- /dev/null +++ b/base_technical_features/i18n/pt_BR.po @@ -0,0 +1,67 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-21 04:22+0000\n" +"PO-Revision-Date: 2019-09-03 03:24+0000\n" +"Last-Translator: Rodrigo Macedo \n" +"Language-Team: Portuguese (Brazil) (https://www.transifex.com/oca/" +"teams/23907/pt_BR/)\n" +"Language: pt_BR\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 3.8\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "Suporte" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "Menú" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Mostrar campo de Recursos Técnicos" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "Recursos Técnicos" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Recursos Técnicos (w/o modo debug)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "O usuário não tem acesso aos recursos técnicos." + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" +"Se o campo de recursos técnicos deve ser exibido nas preferências do usuário." + +#~ msgid "Users" +#~ msgstr "Usuários" diff --git a/base_technical_features/i18n/pt_PT.po b/base_technical_features/i18n/pt_PT.po new file mode 100644 index 0000000000..cec47f7279 --- /dev/null +++ b/base_technical_features/i18n/pt_PT.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-12-23 02:01+0000\n" +"PO-Revision-Date: 2016-12-23 02:01+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: Portuguese (Portugal) (https://www.transifex.com/oca/" +"teams/23907/pt_PT/)\n" +"Language: pt_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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Fields" +#~ msgstr "Campos" diff --git a/base_technical_features/i18n/ro.po b/base_technical_features/i18n/ro.po new file mode 100644 index 0000000000..e4aa4f6f51 --- /dev/null +++ b/base_technical_features/i18n/ro.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# Daniel Schweiger , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-06-22 01:12+0000\n" +"PO-Revision-Date: 2017-06-22 01:12+0000\n" +"Last-Translator: Daniel Schweiger , 2017\n" +"Language-Team: Romanian (https://www.transifex.com/oca/teams/23907/ro/)\n" +"Language: ro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?" +"2:1));\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Utilizatori" diff --git a/base_technical_features/i18n/sl.po b/base_technical_features/i18n/sl.po new file mode 100644 index 0000000000..2cf1dfaf5c --- /dev/null +++ b/base_technical_features/i18n/sl.po @@ -0,0 +1,69 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-21 04:22+0000\n" +"PO-Revision-Date: 2017-01-21 04:22+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3);\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "Prikaži polje \"Tehnične funkcije\"" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +#, fuzzy +msgid "Technical Features" +msgstr "Tehnične funkcije" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "Tehnične funkcije (brez razhroščevalnika)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "Uporabnik nima dostopa do tehničnih finkcij" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "Ali naj se prikaže polje tehnične funkcije v uporabniških nastavitvah." + +#~ msgid "Users" +#~ msgstr "Uporabniki" + +#~ msgid "ir.ui.menu" +#~ msgstr "ir.ui.menu" diff --git a/base_technical_features/i18n/tr.po b/base_technical_features/i18n/tr.po new file mode 100644 index 0000000000..50b1c685f2 --- /dev/null +++ b/base_technical_features/i18n/tr.po @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-21 04:22+0000\n" +"PO-Revision-Date: 2017-01-21 04:22+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: Turkish (https://www.transifex.com/oca/teams/23907/tr/)\n" +"Language: tr\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" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Kullanıcılar" diff --git a/base_technical_features/i18n/tr_TR.po b/base_technical_features/i18n/tr_TR.po new file mode 100644 index 0000000000..e7bfc6bcf8 --- /dev/null +++ b/base_technical_features/i18n/tr_TR.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-22 00:54+0000\n" +"PO-Revision-Date: 2017-02-22 00:54+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Turkish (Turkey) (https://www.transifex.com/oca/teams/23907/" +"tr_TR/)\n" +"Language: tr_TR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "" + +#~ msgid "Users" +#~ msgstr "Kullanıcılar" diff --git a/base_technical_features/i18n/zh_CN.po b/base_technical_features/i18n/zh_CN.po new file mode 100644 index 0000000000..451c1b87e6 --- /dev/null +++ b/base_technical_features/i18n/zh_CN.po @@ -0,0 +1,66 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_technical_features +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-26 03:36+0000\n" +"PO-Revision-Date: 2019-09-02 16:56+0000\n" +"Last-Translator: 黎伟杰 <674416404@qq.com>\n" +"Language-Team: Chinese (China) (https://www.transifex.com/oca/teams/23907/" +"zh_CN/)\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 3.8\n" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_base +msgid "Base" +msgstr "基础" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_ir_ui_menu +msgid "Menu" +msgstr "菜单" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__show_technical_features +msgid "Show field Technical Features" +msgstr "显示字段技术特性" + +#. module: base_technical_features +#: model:ir.model.fields,field_description:base_technical_features.field_res_users__technical_features +msgid "Technical Features" +msgstr "技术特性" + +#. module: base_technical_features +#: model:res.groups,name:base_technical_features.group_technical_features +msgid "Technical Features (w/o debug mode)" +msgstr "技术特性(无调试模式)" + +#. module: base_technical_features +#. odoo-python +#: code:addons/base_technical_features/models/res_users.py:0 +msgid "The user does not have access to technical features." +msgstr "用户无权访问技术功能。" + +#. module: base_technical_features +#: model:ir.model,name:base_technical_features.model_res_users +msgid "User" +msgstr "" + +#. module: base_technical_features +#: model:ir.model.fields,help:base_technical_features.field_res_users__show_technical_features +msgid "" +"Whether to display the technical features field in the user preferences." +msgstr "是否在用户首选项中显示技术功能字段。" + +#~ msgid "Users" +#~ msgstr "用户" diff --git a/base_technical_features/models/__init__.py b/base_technical_features/models/__init__.py new file mode 100644 index 0000000000..0a11085c9a --- /dev/null +++ b/base_technical_features/models/__init__.py @@ -0,0 +1,3 @@ +from . import ir_ui_menu +from . import ir_ui_view +from . import res_users diff --git a/base_technical_features/models/ir_ui_menu.py b/base_technical_features/models/ir_ui_menu.py new file mode 100644 index 0000000000..b73dec56d0 --- /dev/null +++ b/base_technical_features/models/ir_ui_menu.py @@ -0,0 +1,15 @@ +# © 2016 Opener B.V. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import api, models + + +class IrUiMenu(models.Model): + _inherit = "ir.ui.menu" + + @api.model + def _visible_menu_ids(self, debug=False): + # OVERRIDE to view menus typically shown in debug mode, when technical features + # are enabled in the user preferences. + if not debug and self.env.user.technical_features: + debug = True + return super()._visible_menu_ids(debug=debug) diff --git a/base_technical_features/models/ir_ui_view.py b/base_technical_features/models/ir_ui_view.py new file mode 100644 index 0000000000..55e31252f2 --- /dev/null +++ b/base_technical_features/models/ir_ui_view.py @@ -0,0 +1,15 @@ +# Copyright 2026 Camptocamp SA (https://www.camptocamp.com). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class IrUiView(models.Model): + _inherit = "ir.ui.view" + + def _postprocess_debug(self, tree): + # OVERRIDE to treat debug nodes as regular nodes, when technical features + # are enabled in the user preferences. + if self.env.user.technical_features: + self = self.with_context(force_debug_mode=True) + return super()._postprocess_debug(tree) diff --git a/base_technical_features/models/res_users.py b/base_technical_features/models/res_users.py new file mode 100644 index 0000000000..433b05141d --- /dev/null +++ b/base_technical_features/models/res_users.py @@ -0,0 +1,48 @@ +# © 2016 Opener B.V. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import Command, api, fields, models + + +class ResUsers(models.Model): + _inherit = "res.users" + + technical_features = fields.Boolean( + compute="_compute_technical_features", + inverse="_inverse_technical_features", + help="Whether the user has technical features always enabled.", + ) + + def has_group(self, group_ext_id: str) -> bool: + # OVERRIDE to forcefully enable debug mode through context key + self.ensure_one() + if ( + group_ext_id == "base.group_no_one" + and self.env.context.get("force_debug_mode") + and self.technical_features + ): + return True + return super().has_group(group_ext_id) + + @api.depends("group_ids") + def _compute_technical_features(self): + for user in self: + user.technical_features = user.has_group( + "base_technical_features.group_technical_features" + ) + + def _inverse_technical_features(self): + """Map boolean field value to group membership""" + group = self.env.ref("base_technical_features.group_technical_features") + if to_add := self.filtered("technical_features"): + to_add.sudo().write({"group_ids": [Command.link(group.id)]}) + if to_remove := self - to_add: + to_remove.sudo().write({"group_ids": [Command.unlink(group.id)]}) + + @property + def SELF_READABLE_FIELDS(self): + return super().SELF_READABLE_FIELDS + ["technical_features"] + + @property + def SELF_WRITEABLE_FIELDS(self): + return super().SELF_WRITEABLE_FIELDS + ["technical_features"] diff --git a/base_technical_features/pyproject.toml b/base_technical_features/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/base_technical_features/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_technical_features/readme/CONFIGURE.md b/base_technical_features/readme/CONFIGURE.md new file mode 100644 index 0000000000..3f216c60b2 --- /dev/null +++ b/base_technical_features/readme/CONFIGURE.md @@ -0,0 +1,14 @@ +After installation of this module, every employee can still access technical features +for the applications that they have access to by enabling debug mode. + +Additionally, users can check the *Technical feature* field in their preferences to +gain permanent access to the menus and views that fall under this category. + +![](static/description/user_preferences.png) + +Upon installation of this module, this preference is already set for the administrator +user of the database. + +In the background, this preference is mapped to the *Technical feature (w/o debug mode)* +group that this module adds. As an administrator, you can therefore manage this +preference from the regular Users and Groups menu items. diff --git a/base_technical_features/readme/CONTRIBUTORS.md b/base_technical_features/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..09c3c6811f --- /dev/null +++ b/base_technical_features/readme/CONTRIBUTORS.md @@ -0,0 +1,6 @@ +- Stefan Rijnhart \<\> +- Jeroen Evens \<\> +- Jim Hoefnagels \<\> +- Khoi (Kien Kim) \<\> +- Tris Doan \<\> +- Iván Todorovich \<\> diff --git a/base_technical_features/readme/CREDITS.md b/base_technical_features/readme/CREDITS.md new file mode 100644 index 0000000000..393f71f140 --- /dev/null +++ b/base_technical_features/readme/CREDITS.md @@ -0,0 +1,3 @@ +The migration of this module from 18.0 to 19.0 was financially supported by: + +- Camptocamp. diff --git a/base_technical_features/readme/DESCRIPTION.md b/base_technical_features/readme/DESCRIPTION.md new file mode 100644 index 0000000000..b439f85868 --- /dev/null +++ b/base_technical_features/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module provides persistent access to technical features based on user preferences, +eliminating the need to activate debug mode. diff --git a/base_technical_features/security/res_groups.xml b/base_technical_features/security/res_groups.xml new file mode 100644 index 0000000000..efb88d031c --- /dev/null +++ b/base_technical_features/security/res_groups.xml @@ -0,0 +1,7 @@ + + + + Technical Features (w/o debug mode) + + + diff --git a/base_technical_features/static/description/icon.png b/base_technical_features/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/base_technical_features/static/description/icon.png differ diff --git a/base_technical_features/static/description/index.html b/base_technical_features/static/description/index.html new file mode 100644 index 0000000000..0dd1165178 --- /dev/null +++ b/base_technical_features/static/description/index.html @@ -0,0 +1,461 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Technical features group

+ +

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

+

This module provides persistent access to technical features based on +user preferences, eliminating the need to activate debug mode.

+

Table of contents

+ +
+

Configuration

+

After installation of this module, every employee can still access +technical features for the applications that they have access to by +enabling debug mode.

+

Additionally, users can check the Technical feature field in their +preferences to gain permanent access to the menus and views that fall +under this category.

+

image1

+

Upon installation of this module, this preference is already set for the +administrator user of the database.

+

In the background, this preference is mapped to the Technical feature +(w/o debug mode) group that this module adds. As an administrator, you +can therefore manage this preference from the regular Users and Groups +menu items.

+
+
+

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

+
    +
  • Opener B.V.
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The migration of this module from 18.0 to 19.0 was financially supported +by:

+
    +
  • Camptocamp.
  • +
+
+
+

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.

+

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

+

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

+
+
+
+
+ + diff --git a/base_technical_features/static/description/user_preferences.png b/base_technical_features/static/description/user_preferences.png new file mode 100644 index 0000000000..284176c3b6 Binary files /dev/null and b/base_technical_features/static/description/user_preferences.png differ diff --git a/base_technical_features/tests/__init__.py b/base_technical_features/tests/__init__.py new file mode 100644 index 0000000000..f52b4ebc57 --- /dev/null +++ b/base_technical_features/tests/__init__.py @@ -0,0 +1 @@ +from . import test_base_technical_features diff --git a/base_technical_features/tests/test_base_technical_features.py b/base_technical_features/tests/test_base_technical_features.py new file mode 100644 index 0000000000..97061387c9 --- /dev/null +++ b/base_technical_features/tests/test_base_technical_features.py @@ -0,0 +1,116 @@ +# Copyright 2026 Camptocamp SA (https://www.camptocamp.com). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import Command +from odoo.tests import Form, TransactionCase + + +class TestBaseTechnicalFeatures(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Group references + cls.group_no_one = cls.env.ref("base.group_no_one") + cls.group_technical_features_xmlid = ( + "base_technical_features.group_technical_features" + ) + cls.group_technical_features = cls.env.ref(cls.group_technical_features_xmlid) + # Create a test view for the hidden fields + cls.view = cls.env["ir.ui.view"].create( + { + "name": "Test view", + "model": "res.users", + "type": "form", + "arch": """ +
+ + + + """, + } + ) + # Create a test technical menu + action = cls.env["ir.actions.act_window"].create( + { + "name": "action (test)", + "res_model": "res.partner", + "view_ids": [Command.create({"view_mode": "form"})], + } + ) + cls.menu = cls.env["ir.ui.menu"].create( + { + "name": "child menu (test)", + "action": f"{action._name},{action.id}", + "group_ids": [Command.set(cls.group_no_one.ids)], + } + ) + + def test_technical_features_field(self): + self.assertFalse(self.env.user.technical_features) + self.assertFalse(self.env.user.has_group(self.group_technical_features_xmlid)) + # Setting the field must add the group to the user + self.env.user.technical_features = True + self.assertTrue(self.env.user.has_group(self.group_technical_features_xmlid)) + # Setting the field to False must remove the group from the user + self.env.user.technical_features = False + self.assertFalse(self.env.user.has_group(self.group_technical_features_xmlid)) + # Adding the group must also be reflected in the field + self.env.user.group_ids += self.group_technical_features + self.assertTrue(self.env.user.technical_features) + # Removing the group must also be reflected in the field + self.env.user.group_ids -= self.group_technical_features + self.assertFalse(self.env.user.technical_features) + + def test_visible_menu_hidden(self): + """The menu is not visible when technical features is not enabled""" + self.assertNotIn(self.menu.id, self.env["ir.ui.menu"]._visible_menu_ids()) + + def test_visible_menu_with_technical_features(self): + """The menu is visible when technical features is enabled""" + self.env.user.technical_features = True + self.assertIn(self.menu.id, self.env["ir.ui.menu"]._visible_menu_ids()) + + def test_visible_menu_with_debug_mode(self): + """The menu is visible when debug mode is enabled + + This just tests we haven't broken the core behavior of the debug mode + """ + self.assertFalse(self.env.user.technical_features) + self.assertIn( + self.menu.id, self.env["ir.ui.menu"]._visible_menu_ids(debug=True) + ) + + def test_visible_fields_hidden(self): + """A technical field is hidden by default""" + form = Form(self.env["res.users"], view=self.view) + self.assertTrue(form._get_modifier("partner_id", "invisible")) + + def test_visible_fields_with_technical_features(self): + """A technical field is visible when technical features is enabled""" + self.env.user.technical_features = True + form = Form(self.env["res.users"], view=self.view) + self.assertFalse(form._get_modifier("partner_id", "invisible")) + + def test_visible_fields_with_debug_mode(self): + """A technical field is visible when debug mode is enabled + + This just tests we haven't broken the core behavior of the debug mode + """ + self.assertFalse(self.env.user.technical_features) + with self.debug_mode(): + form = Form(self.env["res.users"], view=self.view) + self.assertFalse(form._get_modifier("partner_id", "invisible")) + + def test_field_hidden_only_in_debug(self): + """A technical field is hidden only in debug mode""" + # The partner_id field is hidden in debug mode, so it must be hidden + # when technical features are enabled + self.view.arch = """ +
+ + + + """ + self.env.user.technical_features = True + form = Form(self.env["res.users"], view=self.view) + self.assertTrue(form._get_modifier("partner_id", "invisible")) diff --git a/base_technical_features/views/res_users.xml b/base_technical_features/views/res_users.xml new file mode 100644 index 0000000000..c70444e94e --- /dev/null +++ b/base_technical_features/views/res_users.xml @@ -0,0 +1,21 @@ + + + + res.users + + + + + + + + + res.users + + + + + + + + diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml index 44e417442b..19be08ca9c 100644 --- a/setup/_metapackage/pyproject.toml +++ b/setup/_metapackage/pyproject.toml @@ -1,7 +1,12 @@ [project] name = "odoo-addons-oca-server-ux" -version = "19.0.20251114.0" +version = "19.0.20260416.0" dependencies = [ + "odoo-addon-base_cancel_confirm==19.0.*", + "odoo-addon-base_revision==19.0.*", + "odoo-addon-base_search_custom_field_filter==19.0.*", + "odoo-addon-base_substate==19.0.*", + "odoo-addon-base_technical_features==19.0.*", "odoo-addon-date_range==19.0.*", ] classifiers=[