From 339312667449d730d841b2cbb42dd2ef7abbecca Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 25 May 2021 12:02:01 +0200 Subject: [PATCH 001/770] [ADD] new module module_change_auto_install to configure auto installable modules by configuration --- module_change_auto_install/README.rst | 8 ++++ module_change_auto_install/__init__.py | 1 + module_change_auto_install/__manifest__.py | 17 ++++++++ module_change_auto_install/patch.py | 40 +++++++++++++++++++ .../readme/CONFIGURE.rst | 33 +++++++++++++++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 15 +++++++ module_change_auto_install/readme/DEVELOP.rst | 4 ++ module_change_auto_install/readme/INSTALL.rst | 4 ++ 9 files changed, 123 insertions(+) create mode 100644 module_change_auto_install/README.rst create mode 100644 module_change_auto_install/__init__.py create mode 100644 module_change_auto_install/__manifest__.py create mode 100644 module_change_auto_install/patch.py create mode 100644 module_change_auto_install/readme/CONFIGURE.rst create mode 100644 module_change_auto_install/readme/CONTRIBUTORS.rst create mode 100644 module_change_auto_install/readme/DESCRIPTION.rst create mode 100644 module_change_auto_install/readme/DEVELOP.rst create mode 100644 module_change_auto_install/readme/INSTALL.rst diff --git a/module_change_auto_install/README.rst b/module_change_auto_install/README.rst new file mode 100644 index 00000000000..e650c809f18 --- /dev/null +++ b/module_change_auto_install/README.rst @@ -0,0 +1,8 @@ +=============================== +Change auto installable modules +=============================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/module_change_auto_install/__init__.py b/module_change_auto_install/__init__.py new file mode 100644 index 00000000000..2e653d695c9 --- /dev/null +++ b/module_change_auto_install/__init__.py @@ -0,0 +1 @@ +from .patch import post_load diff --git a/module_change_auto_install/__manifest__.py b/module_change_auto_install/__manifest__.py new file mode 100644 index 00000000000..6a6220a2a16 --- /dev/null +++ b/module_change_auto_install/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2021 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Change auto installable modules", + "summary": "Customize auto installables modules by configuration", + "version": "14.0.1.0.1", + "category": "Tools", + "maintainers": ["legalsylvain"], + "author": "GRAP, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-tools", + "installable": True, + "depends": ["base"], + "post_load": "post_load", + "license": "AGPL-3", +} diff --git a/module_change_auto_install/patch.py b/module_change_auto_install/patch.py new file mode 100644 index 00000000000..9285ffddba8 --- /dev/null +++ b/module_change_auto_install/patch.py @@ -0,0 +1,40 @@ +# Copyright (C) 2021 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo import modules +from odoo.tools import config + +_logger = logging.getLogger(__name__) +_original_load_information_from_description_file = ( + modules.module.load_information_from_description_file +) + + +def _overload_load_information_from_description_file(module, mod_path=None): + res = _original_load_information_from_description_file(module, mod_path=None) + auto_install = res.get("auto_install", False) + + modules_auto_install_enabled = config.get("modules_auto_install_enabled", []) + modules_auto_install_disabled = config.get("modules_auto_install_disabled", []) + + if module in modules_auto_install_disabled and auto_install: + _logger.info("Module '%s' has been marked as not auto installable." % module) + res["auto_install"] = False + + if module in modules_auto_install_enabled and not auto_install: + _logger.info("Module '%s' has been marked as auto installable." % module) + res["auto_install"] = True + + return res + + +def post_load(): + modules.module.load_information_from_description_file = ( + _overload_load_information_from_description_file + ) + modules.load_information_from_description_file = ( + _overload_load_information_from_description_file + ) diff --git a/module_change_auto_install/readme/CONFIGURE.rst b/module_change_auto_install/readme/CONFIGURE.rst new file mode 100644 index 00000000000..e766d4315d3 --- /dev/null +++ b/module_change_auto_install/readme/CONFIGURE.rst @@ -0,0 +1,33 @@ +* Edit your ``odoo.cfg`` configuration file: + +* Add the module ``module_change_auto_install`` in the ``server_wide_modules`` list. + +* (optional) Add a new entry ``modules_auto_install_disabled`` to mark + a list of modules as NOT auto installable. + +* (optional) Add a new entry ``modules_auto_install_enabled`` to mark + a list of modules as auto installable. This feature can be usefull for companies + that are hosting a lot of Odoo instances for many customers, and want some modules + to be always installed. + +**Typical Settings** + +.. code-block:: shell + + server_wide_modules = web,module_change_auto_install + + modules_auto_install_disabled = partner_autocomplete,iap,mail_bot,account_edi,account_edi_facturx,account_edi_ubl + + modules_auto_install_enabled = web_responsive,web_no_bubble,base_technical_features,disable_odoo_online,account_menu + +Run your instance and check logs. Modules that has been altered should be present in your log, at the load of your instance: + +.. code-block:: shell + + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_facturx' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_ubl' has been marked as not auto installable. + INFO db_name odoo.modules.loading: 42 modules loaded in 0.32s, 0 queries (+0 extra) diff --git a/module_change_auto_install/readme/CONTRIBUTORS.rst b/module_change_auto_install/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..9f76a75bc18 --- /dev/null +++ b/module_change_auto_install/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sylvain LE GAL diff --git a/module_change_auto_install/readme/DESCRIPTION.rst b/module_change_auto_install/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..dbd184a2fed --- /dev/null +++ b/module_change_auto_install/readme/DESCRIPTION.rst @@ -0,0 +1,15 @@ +In odoo, by default some modules are marked as auto installable +by the ``auto_install`` key present in the manifest. + +* This feature is very useful for "glue" modules that allow two modules to work together. + (A typical example is ``sale_stock`` which allows ``sale`` and ``stock`` modules to work together). + +* However, Odoo SA also marks some modules as auto installable, even though + this is not technically required. This can happen + for modules the company wants to promote like ``iap``, + modules with a big wow effect like ``partner_autocomplete``, + or some modules they consider useful by default like ``account_edi``. + See the discussion: https://github.com/odoo/odoo/issues/71190 + +This module allows to change by configuration, the list of auto installable modules, +adding or removing some modules to auto install. diff --git a/module_change_auto_install/readme/DEVELOP.rst b/module_change_auto_install/readme/DEVELOP.rst new file mode 100644 index 00000000000..a2ee648d603 --- /dev/null +++ b/module_change_auto_install/readme/DEVELOP.rst @@ -0,0 +1,4 @@ +If you upgrade your odoo Instance from a major version to another, +using the OCA Free Software project "OpenUpgrade", you can also use +this module during the upgrade process, to avoid the installation of +useless new modules. diff --git a/module_change_auto_install/readme/INSTALL.rst b/module_change_auto_install/readme/INSTALL.rst new file mode 100644 index 00000000000..c7804edadb8 --- /dev/null +++ b/module_change_auto_install/readme/INSTALL.rst @@ -0,0 +1,4 @@ +You don't have to install this module. To make the features working : + +* make the module ``module_change_auto_install`` available in your addons path +* update your ``odoo.cfg`` following the "Configure" section From 7a41943d60acc2a2f1b2d692beb2a2ca20beb0f0 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Tue, 25 May 2021 21:10:32 +0000 Subject: [PATCH 002/770] [UPD] Update module_change_auto_install.pot --- .../i18n/module_change_auto_install.pot | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 module_change_auto_install/i18n/module_change_auto_install.pot diff --git a/module_change_auto_install/i18n/module_change_auto_install.pot b/module_change_auto_install/i18n/module_change_auto_install.pot new file mode 100644 index 00000000000..4d8b20f912f --- /dev/null +++ b/module_change_auto_install/i18n/module_change_auto_install.pot @@ -0,0 +1,13 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" From 1814950483c88527f51bd1e9c22c07ef0f964ca1 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 25 May 2021 21:14:55 +0000 Subject: [PATCH 003/770] [UPD] README.rst --- module_change_auto_install/README.rst | 140 +++++ .../static/description/index.html | 483 ++++++++++++++++++ 2 files changed, 623 insertions(+) create mode 100644 module_change_auto_install/static/description/index.html diff --git a/module_change_auto_install/README.rst b/module_change_auto_install/README.rst index e650c809f18..725a259e429 100644 --- a/module_change_auto_install/README.rst +++ b/module_change_auto_install/README.rst @@ -6,3 +6,143 @@ Change auto installable modules !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/14.0/module_change_auto_install + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-14-0/server-tools-14-0-module_change_auto_install + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +In odoo, by default some modules are marked as auto installable +by the ``auto_install`` key present in the manifest. + +* This feature is very useful for "glue" modules that allow two modules to work together. + (A typical example is ``sale_stock`` which allows ``sale`` and ``stock`` modules to work together). + +* However, Odoo SA also marks some modules as auto installable, even though + this is not technically required. This can happen + for modules the company wants to promote like ``iap``, + modules with a big wow effect like ``partner_autocomplete``, + or some modules they consider useful by default like ``account_edi``. + See the discussion: https://github.com/odoo/odoo/issues/71190 + +This module allows to change by configuration, the list of auto installable modules, +adding or removing some modules to auto install. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +You don't have to install this module. To make the features working : + +* make the module ``module_change_auto_install`` available in your addons path +* update your ``odoo.cfg`` following the "Configure" section + +Configuration +============= + +* Edit your ``odoo.cfg`` configuration file: + +* Add the module ``module_change_auto_install`` in the ``server_wide_modules`` list. + +* (optional) Add a new entry ``modules_auto_install_disabled`` to mark + a list of modules as NOT auto installable. + +* (optional) Add a new entry ``modules_auto_install_enabled`` to mark + a list of modules as auto installable. This feature can be usefull for companies + that are hosting a lot of Odoo instances for many customers, and want some modules + to be always installed. + +**Typical Settings** + +.. code-block:: shell + + server_wide_modules = web,module_change_auto_install + + modules_auto_install_disabled = partner_autocomplete,iap,mail_bot,account_edi,account_edi_facturx,account_edi_ubl + + modules_auto_install_enabled = web_responsive,web_no_bubble,base_technical_features,disable_odoo_online,account_menu + +Run your instance and check logs. Modules that has been altered should be present in your log, at the load of your instance: + +.. code-block:: shell + + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_facturx' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_ubl' has been marked as not auto installable. + INFO db_name odoo.modules.loading: 42 modules loaded in 0.32s, 0 queries (+0 extra) + +Development +=========== + +If you upgrade your odoo Instance from a major version to another, +using the OCA Free Software project "OpenUpgrade", you can also use +this module during the upgrade process, to avoid the installation of +useless new modules. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* GRAP + +Contributors +~~~~~~~~~~~~ + +* Sylvain LE GAL + +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-legalsylvain| image:: https://github.com/legalsylvain.png?size=40px + :target: https://github.com/legalsylvain + :alt: legalsylvain + +Current `maintainer `__: + +|maintainer-legalsylvain| + +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/module_change_auto_install/static/description/index.html b/module_change_auto_install/static/description/index.html new file mode 100644 index 00000000000..462cf550bdb --- /dev/null +++ b/module_change_auto_install/static/description/index.html @@ -0,0 +1,483 @@ + + + + + + +Change auto installable modules + + + +
+

Change auto installable modules

+ + +

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

+

In odoo, by default some modules are marked as auto installable +by the auto_install key present in the manifest.

+
    +
  • This feature is very useful for “glue” modules that allow two modules to work together. +(A typical example is sale_stock which allows sale and stock modules to work together).
  • +
  • However, Odoo SA also marks some modules as auto installable, even though +this is not technically required. This can happen +for modules the company wants to promote like iap, +modules with a big wow effect like partner_autocomplete, +or some modules they consider useful by default like account_edi. +See the discussion: https://github.com/odoo/odoo/issues/71190
  • +
+

This module allows to change by configuration, the list of auto installable modules, +adding or removing some modules to auto install.

+

Table of contents

+ +
+

Installation

+

You don’t have to install this module. To make the features working :

+
    +
  • make the module module_change_auto_install available in your addons path
  • +
  • update your odoo.cfg following the “Configure” section
  • +
+
+
+

Configuration

+
    +
  • Edit your odoo.cfg configuration file:
  • +
  • Add the module module_change_auto_install in the server_wide_modules list.
  • +
  • (optional) Add a new entry modules_auto_install_disabled to mark +a list of modules as NOT auto installable.
  • +
  • (optional) Add a new entry modules_auto_install_enabled to mark +a list of modules as auto installable. This feature can be usefull for companies +that are hosting a lot of Odoo instances for many customers, and want some modules +to be always installed.
  • +
+

Typical Settings

+
+server_wide_modules = web,module_change_auto_install
+
+modules_auto_install_disabled = partner_autocomplete,iap,mail_bot,account_edi,account_edi_facturx,account_edi_ubl
+
+modules_auto_install_enabled = web_responsive,web_no_bubble,base_technical_features,disable_odoo_online,account_menu
+
+

Run your instance and check logs. Modules that has been altered should be present in your log, at the load of your instance:

+
+INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as not auto installable.
+INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as not auto installable.
+INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as not auto installable.
+INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi' has been marked as not auto installable.
+INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_facturx' has been marked as not auto installable.
+INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_ubl' has been marked as not auto installable.
+INFO db_name odoo.modules.loading: 42 modules loaded in 0.32s, 0 queries (+0 extra)
+
+
+
+

Development

+

If you upgrade your odoo Instance from a major version to another, +using the OCA Free Software project “OpenUpgrade”, you can also use +this module during the upgrade process, to avoid the installation of +useless new modules.

+
+
+

Bug Tracker

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • GRAP
  • +
+
+ +
+

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:

+

legalsylvain

+

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

+

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

+
+
+
+ + From 45c9abb8001575d611304882389f19ea3c168159 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 25 May 2021 21:14:56 +0000 Subject: [PATCH 004/770] [ADD] icon.png --- .../static/description/icon.png | Bin 0 -> 9455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 module_change_auto_install/static/description/icon.png diff --git a/module_change_auto_install/static/description/icon.png b/module_change_auto_install/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 From 969999b7e855e265a52928f301cf477b464629a2 Mon Sep 17 00:00:00 2001 From: "moylop260@vauxoo.com" Date: Wed, 26 May 2021 14:22:37 +0000 Subject: [PATCH 005/770] [FIX] module_change_auto_install: Split string sep by comma Optimize validation Add logger to apply patch --- module_change_auto_install/patch.py | 31 +++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/module_change_auto_install/patch.py b/module_change_auto_install/patch.py index 9285ffddba8..14dc00fa675 100644 --- a/module_change_auto_install/patch.py +++ b/module_change_auto_install/patch.py @@ -13,18 +13,40 @@ ) +def split_strip(s): + """Split string and strip each component sep by comma + + >>> split_strip("foo, bar,") + ['foo', 'bar'] + + >>> split_strip("") + [] + + >>> split_strip(None) + [] + """ + s = (s or "").strip(" ,") + if not s: + return [] + return [x.strip() for x in s.split(",")] + + def _overload_load_information_from_description_file(module, mod_path=None): res = _original_load_information_from_description_file(module, mod_path=None) auto_install = res.get("auto_install", False) - modules_auto_install_enabled = config.get("modules_auto_install_enabled", []) - modules_auto_install_disabled = config.get("modules_auto_install_disabled", []) + modules_auto_install_enabled = split_strip( + config.get("modules_auto_install_enabled") + ) + modules_auto_install_disabled = split_strip( + config.get("modules_auto_install_disabled") + ) - if module in modules_auto_install_disabled and auto_install: + if auto_install and module in modules_auto_install_disabled: _logger.info("Module '%s' has been marked as not auto installable." % module) res["auto_install"] = False - if module in modules_auto_install_enabled and not auto_install: + if not auto_install and module in modules_auto_install_enabled: _logger.info("Module '%s' has been marked as auto installable." % module) res["auto_install"] = True @@ -32,6 +54,7 @@ def _overload_load_information_from_description_file(module, mod_path=None): def post_load(): + _logger.info("Applying patch module_change_auto_intall") modules.module.load_information_from_description_file = ( _overload_load_information_from_description_file ) From 323ca861175c423dd177477feb04e6961df0da4e Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 26 May 2021 20:50:15 +0000 Subject: [PATCH 006/770] module_change_auto_install 14.0.1.0.2 --- module_change_auto_install/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module_change_auto_install/__manifest__.py b/module_change_auto_install/__manifest__.py index 6a6220a2a16..c3d3cfdc8f7 100644 --- a/module_change_auto_install/__manifest__.py +++ b/module_change_auto_install/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Change auto installable modules", "summary": "Customize auto installables modules by configuration", - "version": "14.0.1.0.1", + "version": "14.0.1.0.2", "category": "Tools", "maintainers": ["legalsylvain"], "author": "GRAP, Odoo Community Association (OCA)", From 3fbb0053fc2c9ba4e4f4b6fad957fa90184a0c7a Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Wed, 22 Dec 2021 16:14:56 +0100 Subject: [PATCH 007/770] [FIX] module_change_auto_install : auto_install is now a set of dependencies. (before, was boolean) --- module_change_auto_install/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module_change_auto_install/patch.py b/module_change_auto_install/patch.py index 14dc00fa675..259dfc23910 100644 --- a/module_change_auto_install/patch.py +++ b/module_change_auto_install/patch.py @@ -48,7 +48,7 @@ def _overload_load_information_from_description_file(module, mod_path=None): if not auto_install and module in modules_auto_install_enabled: _logger.info("Module '%s' has been marked as auto installable." % module) - res["auto_install"] = True + res["auto_install"] = set(res["depends"]) return res From ea7668d8644729928a6f101d2c8dc174858587a1 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 22 Dec 2021 17:48:51 +0000 Subject: [PATCH 008/770] module_change_auto_install 14.0.1.0.3 --- module_change_auto_install/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module_change_auto_install/__manifest__.py b/module_change_auto_install/__manifest__.py index c3d3cfdc8f7..5507116876f 100644 --- a/module_change_auto_install/__manifest__.py +++ b/module_change_auto_install/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Change auto installable modules", "summary": "Customize auto installables modules by configuration", - "version": "14.0.1.0.2", + "version": "14.0.1.0.3", "category": "Tools", "maintainers": ["legalsylvain"], "author": "GRAP, Odoo Community Association (OCA)", From 9fcfcd68d15cf51d08c7a3bb6cb1ca0408499695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dept=2E=20T=C3=A9cnico?= Date: Mon, 14 Feb 2022 11:41:41 +0000 Subject: [PATCH 009/770] Added translation using Weblate (Catalan) --- module_change_auto_install/i18n/ca.po | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 module_change_auto_install/i18n/ca.po diff --git a/module_change_auto_install/i18n/ca.po b/module_change_auto_install/i18n/ca.po new file mode 100644 index 00000000000..c038a7eecbd --- /dev/null +++ b/module_change_auto_install/i18n/ca.po @@ -0,0 +1,14 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\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" From 7151c4873bdfd27bdfb535643fe00e19c8f30b3c Mon Sep 17 00:00:00 2001 From: Fai Date: Mon, 19 Sep 2022 09:54:50 +0800 Subject: [PATCH 010/770] [MIG] module_change_auto_install: Migration to 15.0 --- module_change_auto_install/README.rst | 10 +++++----- module_change_auto_install/__manifest__.py | 2 +- module_change_auto_install/i18n/ca.po | 2 +- .../i18n/module_change_auto_install.pot | 2 +- .../static/description/index.html | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/module_change_auto_install/README.rst b/module_change_auto_install/README.rst index 725a259e429..47e665c1ae2 100644 --- a/module_change_auto_install/README.rst +++ b/module_change_auto_install/README.rst @@ -14,13 +14,13 @@ Change auto installable modules :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--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/14.0/module_change_auto_install + :target: https://github.com/OCA/server-tools/tree/15.0/module_change_auto_install :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-14-0/server-tools-14-0-module_change_auto_install + :target: https://translation.odoo-community.org/projects/server-tools-15-0/server-tools-15-0-module_change_auto_install :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/149/14.0 + :target: https://runbot.odoo-community.org/runbot/149/15.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -105,7 +105,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -143,6 +143,6 @@ Current `maintainer `__: |maintainer-legalsylvain| -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/module_change_auto_install/__manifest__.py b/module_change_auto_install/__manifest__.py index 5507116876f..1e183fb16d3 100644 --- a/module_change_auto_install/__manifest__.py +++ b/module_change_auto_install/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Change auto installable modules", "summary": "Customize auto installables modules by configuration", - "version": "14.0.1.0.3", + "version": "15.0.1.0.0", "category": "Tools", "maintainers": ["legalsylvain"], "author": "GRAP, Odoo Community Association (OCA)", diff --git a/module_change_auto_install/i18n/ca.po b/module_change_auto_install/i18n/ca.po index c038a7eecbd..6ff2ef5f332 100644 --- a/module_change_auto_install/i18n/ca.po +++ b/module_change_auto_install/i18n/ca.po @@ -3,7 +3,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" diff --git a/module_change_auto_install/i18n/module_change_auto_install.pot b/module_change_auto_install/i18n/module_change_auto_install.pot index 4d8b20f912f..a11baf5cd77 100644 --- a/module_change_auto_install/i18n/module_change_auto_install.pot +++ b/module_change_auto_install/i18n/module_change_auto_install.pot @@ -3,7 +3,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" diff --git a/module_change_auto_install/static/description/index.html b/module_change_auto_install/static/description/index.html index 462cf550bdb..0dbf4816e7d 100644 --- a/module_change_auto_install/static/description/index.html +++ b/module_change_auto_install/static/description/index.html @@ -367,7 +367,7 @@

Change auto installable modules

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

+

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

In odoo, by default some modules are marked as auto installable by the auto_install key present in the manifest.

    @@ -448,7 +448,7 @@

    Bug Tracker

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

    +feedback.

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

    @@ -474,7 +474,7 @@

    Maintainers

    promote its widespread use.

    Current maintainer:

    legalsylvain

    -

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

    +

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

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

    From 96b9b414fd0a39e3eeb30268b28b36f87f3088c9 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Thu, 13 Oct 2022 14:24:43 +0200 Subject: [PATCH 011/770] [PORT] module_change_auto_install from 15.0 to 16.0 --- module_change_auto_install/__manifest__.py | 2 +- module_change_auto_install/i18n/fr.po | 15 ++++ module_change_auto_install/patch.py | 90 ++++++++++++------- .../readme/CONFIGURE.rst | 42 +++++++-- module_change_auto_install/tests/__init__.py | 1 + .../tests/test_module.py | 30 +++++++ 6 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 module_change_auto_install/i18n/fr.po create mode 100644 module_change_auto_install/tests/__init__.py create mode 100644 module_change_auto_install/tests/test_module.py diff --git a/module_change_auto_install/__manifest__.py b/module_change_auto_install/__manifest__.py index 1e183fb16d3..c5e4106a4c3 100644 --- a/module_change_auto_install/__manifest__.py +++ b/module_change_auto_install/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Change auto installable modules", "summary": "Customize auto installables modules by configuration", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "category": "Tools", "maintainers": ["legalsylvain"], "author": "GRAP, Odoo Community Association (OCA)", diff --git a/module_change_auto_install/i18n/fr.po b/module_change_auto_install/i18n/fr.po new file mode 100644 index 00000000000..a07e4484b9a --- /dev/null +++ b/module_change_auto_install/i18n/fr.po @@ -0,0 +1,15 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-10-13 13:04+0000\n" +"PO-Revision-Date: 2022-10-13 13:04+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/module_change_auto_install/patch.py b/module_change_auto_install/patch.py index 259dfc23910..b77b3ee1403 100644 --- a/module_change_auto_install/patch.py +++ b/module_change_auto_install/patch.py @@ -8,56 +8,84 @@ from odoo.tools import config _logger = logging.getLogger(__name__) -_original_load_information_from_description_file = ( - modules.module.load_information_from_description_file -) +_original_load_manifest = modules.module.load_manifest -def split_strip(s): - """Split string and strip each component sep by comma +def _get_modules_dict_auto_install_config(config_value): + """Given a configuration parameter name, return a dict of + {module_name: modules_list or False} - >>> split_strip("foo, bar,") - ['foo', 'bar'] + if the odoo.cfg file contains + + modules_auto_install_enabled = + web_responsive:web, + base_technical_features:, + point_of_sale:sale/purchase, + account_usability + + >>> split_strip('modules_auto_install_enabled') + { + 'web_responsive': ['web'], + 'base_technical_features': [], + 'point_of_sale': ['sale', 'purchase'], + 'account_usability': False, + } - >>> split_strip("") - [] - >>> split_strip(None) - [] """ - s = (s or "").strip(" ,") - if not s: - return [] - return [x.strip() for x in s.split(",")] + res = {} + config_value = (config_value or "").strip(" ,") + config_list = [x.strip() for x in config_value.split(",")] + for item in config_list: + if ":" in item: + res[item.split(":")[0]] = ( + item.split(":")[1] and item.split(":")[1].split("/") or [] + ) + else: + res[item] = True + return res -def _overload_load_information_from_description_file(module, mod_path=None): - res = _original_load_information_from_description_file(module, mod_path=None) +def _overload_load_manifest(module, mod_path=None): + + res = _original_load_manifest(module, mod_path=None) auto_install = res.get("auto_install", False) - modules_auto_install_enabled = split_strip( + modules_auto_install_enabled_dict = _get_modules_dict_auto_install_config( config.get("modules_auto_install_enabled") ) - modules_auto_install_disabled = split_strip( + modules_auto_install_disabled_dict = _get_modules_dict_auto_install_config( config.get("modules_auto_install_disabled") ) - if auto_install and module in modules_auto_install_disabled: - _logger.info("Module '%s' has been marked as not auto installable." % module) + if auto_install and module in modules_auto_install_disabled_dict.keys(): + _logger.info("Module '%s' has been marked as NOT auto installable." % module) res["auto_install"] = False - if not auto_install and module in modules_auto_install_enabled: - _logger.info("Module '%s' has been marked as auto installable." % module) - res["auto_install"] = set(res["depends"]) + if not auto_install and module in modules_auto_install_enabled_dict.keys(): + specific_dependencies = modules_auto_install_enabled_dict.get(module) + if type(specific_dependencies) is bool: + # Classical case + _logger.info("Module '%s' has been marked as auto installable." % module) + res["auto_install"] = set(res["depends"]) + else: + if specific_dependencies: + _logger.info( + "Module '%s' has been marked as auto installable if '%s' are installed" + % (module, ",".join(specific_dependencies)) + ) + else: + _logger.info( + "Module '%s' has been marked as auto installable in ALL CASES." + % module + ) + + res["auto_install"] = set(specific_dependencies) return res def post_load(): - _logger.info("Applying patch module_change_auto_intall") - modules.module.load_information_from_description_file = ( - _overload_load_information_from_description_file - ) - modules.load_information_from_description_file = ( - _overload_load_information_from_description_file - ) + _logger.info("Aplying patch module_change_auto_intall ...") + modules.module.load_manifest = _overload_load_manifest + modules.load_manifest = _overload_load_manifest diff --git a/module_change_auto_install/readme/CONFIGURE.rst b/module_change_auto_install/readme/CONFIGURE.rst index e766d4315d3..7bb8d37bd63 100644 --- a/module_change_auto_install/readme/CONFIGURE.rst +++ b/module_change_auto_install/readme/CONFIGURE.rst @@ -16,18 +16,44 @@ server_wide_modules = web,module_change_auto_install - modules_auto_install_disabled = partner_autocomplete,iap,mail_bot,account_edi,account_edi_facturx,account_edi_ubl + modules_auto_install_disabled = + partner_autocomplete, + iap, + mail_bot - modules_auto_install_enabled = web_responsive,web_no_bubble,base_technical_features,disable_odoo_online,account_menu + modules_auto_install_enabled = + web_responsive:web, + base_technical_features, + disable_odoo_online, + account_usability Run your instance and check logs. Modules that has been altered should be present in your log, at the load of your instance: .. code-block:: shell - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_facturx' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_ubl' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as NOT auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as NOT auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as NOT auto installable. INFO db_name odoo.modules.loading: 42 modules loaded in 0.32s, 0 queries (+0 extra) + +**Advanced Configuration Possibilities** + +if your ``odoo.cfg`` file contains the following configuration: + +.. code-block:: shell + + modules_auto_install_enabled = + account_usability, + web_responsive:web, + base_technical_features:, + point_of_sale:sale/purchase + +The behaviour will be the following: + +* ``account_usability`` module will be installed as soon as all the default dependencies are installed. (here ``account``) + +* ``web_responsive`` module will be installed as soon as ``web`` is installed. (Althought ``web_responsive`` depends on ``web`` and ``mail``) + +* ``base_technical_features`` will be ALWAYS installed + +* ``point_of_sale`` module will be installed as soon as ``sale`` and ``purchase`` module are installed. diff --git a/module_change_auto_install/tests/__init__.py b/module_change_auto_install/tests/__init__.py new file mode 100644 index 00000000000..d9b96c4fa5a --- /dev/null +++ b/module_change_auto_install/tests/__init__.py @@ -0,0 +1 @@ +from . import test_module diff --git a/module_change_auto_install/tests/test_module.py b/module_change_auto_install/tests/test_module.py new file mode 100644 index 00000000000..7302d0cb157 --- /dev/null +++ b/module_change_auto_install/tests/test_module.py @@ -0,0 +1,30 @@ +# Copyright 2015-2017 Camptocamp SA +# Copyright 2020 Onestein () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + +from odoo.addons.module_change_auto_install.patch import ( + _get_modules_dict_auto_install_config, +) + +# from ..models.base import disable_changeset + + +class TestModule(TransactionCase): + + _EXPECTED_RESULTS = { + "web_responsive": {"web_responsive": True}, + "sale, purchase,": {"sale": True, "purchase": True}, + "web_responsive:web,base_technical_features:," + "point_of_sale:sale/purchase,account_usability": { + "web_responsive": ["web"], + "base_technical_features": [], + "point_of_sale": ["sale", "purchase"], + "account_usability": True, + }, + } + + def test_config_parsing(self): + for k, v in self._EXPECTED_RESULTS.items(): + self.assertEqual(_get_modules_dict_auto_install_config(k), v) From 7a2c4e5a134d5dbf82f8e75f24f0ea82d697442c Mon Sep 17 00:00:00 2001 From: oca-ci Date: Tue, 8 Nov 2022 10:47:23 +0000 Subject: [PATCH 012/770] [UPD] Update module_change_auto_install.pot --- module_change_auto_install/i18n/module_change_auto_install.pot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module_change_auto_install/i18n/module_change_auto_install.pot b/module_change_auto_install/i18n/module_change_auto_install.pot index a11baf5cd77..78d58d53fe0 100644 --- a/module_change_auto_install/i18n/module_change_auto_install.pot +++ b/module_change_auto_install/i18n/module_change_auto_install.pot @@ -3,7 +3,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 15.0\n" +"Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" From 91eb0fac136b29efb5f4bd5e6a6c46cee47a3be1 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 8 Nov 2022 10:50:18 +0000 Subject: [PATCH 013/770] [UPD] README.rst --- module_change_auto_install/README.rst | 52 ++++++++++++++----- .../static/description/index.html | 42 +++++++++++---- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/module_change_auto_install/README.rst b/module_change_auto_install/README.rst index 47e665c1ae2..1c3c6df6325 100644 --- a/module_change_auto_install/README.rst +++ b/module_change_auto_install/README.rst @@ -14,13 +14,13 @@ Change auto installable modules :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--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/15.0/module_change_auto_install + :target: https://github.com/OCA/server-tools/tree/16.0/module_change_auto_install :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-15-0/server-tools-15-0-module_change_auto_install + :target: https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-module_change_auto_install :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/149/15.0 + :target: https://runbot.odoo-community.org/runbot/149/16.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -75,22 +75,48 @@ Configuration server_wide_modules = web,module_change_auto_install - modules_auto_install_disabled = partner_autocomplete,iap,mail_bot,account_edi,account_edi_facturx,account_edi_ubl + modules_auto_install_disabled = + partner_autocomplete, + iap, + mail_bot - modules_auto_install_enabled = web_responsive,web_no_bubble,base_technical_features,disable_odoo_online,account_menu + modules_auto_install_enabled = + web_responsive:web, + base_technical_features, + disable_odoo_online, + account_usability Run your instance and check logs. Modules that has been altered should be present in your log, at the load of your instance: .. code-block:: shell - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_facturx' has been marked as not auto installable. - INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_ubl' has been marked as not auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as NOT auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as NOT auto installable. + INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as NOT auto installable. INFO db_name odoo.modules.loading: 42 modules loaded in 0.32s, 0 queries (+0 extra) +**Advanced Configuration Possibilities** + +if your ``odoo.cfg`` file contains the following configuration: + +.. code-block:: shell + + modules_auto_install_enabled = + account_usability, + web_responsive:web, + base_technical_features:, + point_of_sale:sale/purchase + +The behaviour will be the following: + +* ``account_usability`` module will be installed as soon as all the default dependencies are installed. (here ``account``) + +* ``web_responsive`` module will be installed as soon as ``web`` is installed. (Althought ``web_responsive`` depends on ``web`` and ``mail``) + +* ``base_technical_features`` will be ALWAYS installed + +* ``point_of_sale`` module will be installed as soon as ``sale`` and ``purchase`` module are installed. + Development =========== @@ -105,7 +131,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -143,6 +169,6 @@ Current `maintainer `__: |maintainer-legalsylvain| -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/module_change_auto_install/static/description/index.html b/module_change_auto_install/static/description/index.html index 0dbf4816e7d..1a2103e11e9 100644 --- a/module_change_auto_install/static/description/index.html +++ b/module_change_auto_install/static/description/index.html @@ -367,7 +367,7 @@

    Change auto installable modules

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

    +

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

    In odoo, by default some modules are marked as auto installable by the auto_install key present in the manifest.

      @@ -421,20 +421,40 @@

      Configuration

       server_wide_modules = web,module_change_auto_install
       
      -modules_auto_install_disabled = partner_autocomplete,iap,mail_bot,account_edi,account_edi_facturx,account_edi_ubl
      +modules_auto_install_disabled =
      +    partner_autocomplete,
      +    iap,
      +    mail_bot
       
      -modules_auto_install_enabled = web_responsive,web_no_bubble,base_technical_features,disable_odoo_online,account_menu
      +modules_auto_install_enabled =
      +    web_responsive:web,
      +    base_technical_features,
      +    disable_odoo_online,
      +    account_usability
       

      Run your instance and check logs. Modules that has been altered should be present in your log, at the load of your instance:

      -INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as not auto installable.
      -INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as not auto installable.
      -INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as not auto installable.
      -INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi' has been marked as not auto installable.
      -INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_facturx' has been marked as not auto installable.
      -INFO db_name odoo.addons.module_change_auto_install.patch: Module 'account_edi_ubl' has been marked as not auto installable.
      +INFO db_name odoo.addons.module_change_auto_install.patch: Module 'iap' has been marked as NOT auto installable.
      +INFO db_name odoo.addons.module_change_auto_install.patch: Module 'mail_bot' has been marked as NOT auto installable.
      +INFO db_name odoo.addons.module_change_auto_install.patch: Module 'partner_autocomplete' has been marked as NOT auto installable.
       INFO db_name odoo.modules.loading: 42 modules loaded in 0.32s, 0 queries (+0 extra)
       
      +

      Advanced Configuration Possibilities

      +

      if your odoo.cfg file contains the following configuration:

      +
      +modules_auto_install_enabled =
      +    account_usability,
      +    web_responsive:web,
      +    base_technical_features:,
      +    point_of_sale:sale/purchase
      +
      +

      The behaviour will be the following:

      +
        +
      • account_usability module will be installed as soon as all the default dependencies are installed. (here account)
      • +
      • web_responsive module will be installed as soon as web is installed. (Althought web_responsive depends on web and mail)
      • +
      • base_technical_features will be ALWAYS installed
      • +
      • point_of_sale module will be installed as soon as sale and purchase module are installed.
      • +

      Development

      @@ -448,7 +468,7 @@

      Bug Tracker

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

      +feedback.

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

      @@ -474,7 +494,7 @@

      Maintainers

      promote its widespread use.

      Current maintainer:

      legalsylvain

      -

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

      +

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

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

      From d13e74c5b5e5f0c6648a0bd82b12c14cfa5f70da Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 8 Nov 2022 12:29:13 +0000 Subject: [PATCH 014/770] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: server-tools-16.0/server-tools-16.0-module_change_auto_install Translate-URL: https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-module_change_auto_install/ --- module_change_auto_install/i18n/ca.po | 14 -------------- module_change_auto_install/i18n/fr.po | 15 --------------- 2 files changed, 29 deletions(-) diff --git a/module_change_auto_install/i18n/ca.po b/module_change_auto_install/i18n/ca.po index 6ff2ef5f332..e69de29bb2d 100644 --- a/module_change_auto_install/i18n/ca.po +++ b/module_change_auto_install/i18n/ca.po @@ -1,14 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 15.0\n" -"Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\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" diff --git a/module_change_auto_install/i18n/fr.po b/module_change_auto_install/i18n/fr.po index a07e4484b9a..e69de29bb2d 100644 --- a/module_change_auto_install/i18n/fr.po +++ b/module_change_auto_install/i18n/fr.po @@ -1,15 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-13 13:04+0000\n" -"PO-Revision-Date: 2022-10-13 13:04+0000\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" From 3381637ca98d6bf239887b798be831aee1915527 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Sun, 3 Sep 2023 16:49:46 +0000 Subject: [PATCH 015/770] [UPD] README.rst --- module_change_auto_install/README.rst | 15 ++-- .../static/description/index.html | 90 ++++++++++--------- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/module_change_auto_install/README.rst b/module_change_auto_install/README.rst index 1c3c6df6325..e0ac8ff0884 100644 --- a/module_change_auto_install/README.rst +++ b/module_change_auto_install/README.rst @@ -2,10 +2,13 @@ Change auto installable modules =============================== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:98a3d5d558f2f0c3c28e4da83aab64faf557fe10382a6faa7d577666177b9fd0 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ Change auto installable modules .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :target: https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-module_change_auto_install :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/149/16.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| In odoo, by default some modules are marked as auto installable by the ``auto_install`` key present in the manifest. @@ -130,7 +133,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +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. diff --git a/module_change_auto_install/static/description/index.html b/module_change_auto_install/static/description/index.html index 1a2103e11e9..4efe2212bc8 100644 --- a/module_change_auto_install/static/description/index.html +++ b/module_change_auto_install/static/description/index.html @@ -1,20 +1,20 @@ - + - + Change auto installable modules -
      -

      Change auto installable modules

      +
      + + +Odoo Community Association + +
      +

      Change auto installable modules

      -

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

      +

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

      In odoo, by default some modules are marked as auto installable by the auto_install key present in the manifest.

        @@ -401,7 +406,7 @@

        Change auto installable modules

      -

      Installation

      +

      Installation

      You don’t have to install this module. To make the features working :

      • make the module module_change_auto_install available in your @@ -411,7 +416,7 @@

        Installation

      -

      Configuration

      +

      Configuration

      • Edit your odoo.cfg configuration file:
      • Add the module module_change_auto_install in the @@ -481,14 +486,14 @@

        Configuration

      -

      Development

      +

      Development

      If you upgrade your odoo Instance from a major version to another, using the OCA Free Software project “OpenUpgrade”, you can also use this module during the upgrade process, to avoid the installation of useless new modules.

      -

      Bug Tracker

      +

      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 @@ -496,15 +501,15 @@

      Bug Tracker

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

      -

      Credits

      +

      Credits

      -

      Authors

      +

      Authors

      • GRAP
      -

      Contributors

      +

      Contributors

      -

      Maintainers

      +

      Maintainers

      This module is maintained by the OCA.

      Odoo Community Association @@ -529,5 +534,6 @@

      Maintainers

      +
      From 7249ad4a2204119a9437b8de482c87d2e79a34b0 Mon Sep 17 00:00:00 2001 From: RUS Date: Tue, 30 Sep 2025 22:12:50 +0530 Subject: [PATCH 036/770] Update to latest copier template --- .copier-answers.yml | 2 +- .pylintrc | 32 -------------------------------- .pylintrc-mandatory | 32 -------------------------------- 3 files changed, 1 insertion(+), 65 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index 3e544ccd081..9ed319c7e7f 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Do NOT update manually; changes here will be overwritten by Copier -_commit: v1.34 +_commit: v1.35 _src_path: git+https://github.com/OCA/oca-addons-repo-template additional_ruff_rules: [] ci: GitHub diff --git a/.pylintrc b/.pylintrc index 16996cb36fb..f3d017a8f5a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -23,21 +23,12 @@ disable=all # config as a blocking check. enable=anomalous-backslash-in-string, - api-one-deprecated, - api-one-multi-together, assignment-from-none, attribute-deprecated, - class-camelcase, dangerous-default-value, - dangerous-view-replace-wo-priority, development-status-allowed, - duplicate-id-csv, duplicate-key, - duplicate-xml-fields, - duplicate-xml-record-id, - eval-referenced, eval-used, - incoherent-interpreter-exec-perm, license-allowed, manifest-author-string, manifest-deprecated-key, @@ -48,56 +39,33 @@ enable=anomalous-backslash-in-string, method-inverse, method-required-super, method-search, - openerp-exception-warning, pointless-statement, pointless-string-statement, print-used, redundant-keyword-arg, - redundant-modulename-xml, reimported, - relative-import, return-in-init, - rst-syntax-error, sql-injection, too-few-format-args, translation-field, translation-required, unreachable, use-vim-comment, - wrong-tabs-instead-of-spaces, - xml-syntax-error, attribute-string-redundant, - character-not-valid-in-resource-link, consider-merging-classes-inherited, context-overridden, - create-user-wo-reset-password, - dangerous-filter-wo-user, - dangerous-qweb-replace-wo-priority, - deprecated-data-xml-node, - deprecated-openerp-xml-node, - duplicate-po-message-definition, except-pass, - file-not-used, invalid-commit, manifest-maintainers-list, - missing-newline-extrafiles, missing-readme, missing-return, odoo-addons-relative-import, - old-api7-method-defined, - po-msgstr-variables, - po-syntax-error, renamed-field-parameter, resource-not-exist, - str-format-used, test-folder-imported, translation-contains-variable, translation-positional-used, - unnecessary-utf8-coding-comment, website-manifest-key-not-valid-uri, - xml-attribute-translatable, - xml-deprecated-qweb-directive, - xml-deprecated-tree-attribute, external-request-timeout, bad-builtin-groupby, category-allowed, diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory index dc9b71ed305..80567de1e77 100644 --- a/.pylintrc-mandatory +++ b/.pylintrc-mandatory @@ -15,21 +15,12 @@ valid-odoo-versions=19.0 disable=all enable=anomalous-backslash-in-string, - api-one-deprecated, - api-one-multi-together, assignment-from-none, attribute-deprecated, - class-camelcase, dangerous-default-value, - dangerous-view-replace-wo-priority, development-status-allowed, - duplicate-id-csv, duplicate-key, - duplicate-xml-fields, - duplicate-xml-record-id, - eval-referenced, eval-used, - incoherent-interpreter-exec-perm, license-allowed, manifest-author-string, manifest-deprecated-key, @@ -40,56 +31,33 @@ enable=anomalous-backslash-in-string, method-inverse, method-required-super, method-search, - openerp-exception-warning, pointless-statement, pointless-string-statement, print-used, redundant-keyword-arg, - redundant-modulename-xml, reimported, - relative-import, return-in-init, - rst-syntax-error, sql-injection, too-few-format-args, translation-field, translation-required, unreachable, use-vim-comment, - wrong-tabs-instead-of-spaces, - xml-syntax-error, attribute-string-redundant, - character-not-valid-in-resource-link, consider-merging-classes-inherited, context-overridden, - create-user-wo-reset-password, - dangerous-filter-wo-user, - dangerous-qweb-replace-wo-priority, - deprecated-data-xml-node, - deprecated-openerp-xml-node, - duplicate-po-message-definition, except-pass, - file-not-used, invalid-commit, manifest-maintainers-list, - missing-newline-extrafiles, missing-readme, missing-return, odoo-addons-relative-import, - old-api7-method-defined, - po-msgstr-variables, - po-syntax-error, renamed-field-parameter, resource-not-exist, - str-format-used, test-folder-imported, translation-contains-variable, translation-positional-used, - unnecessary-utf8-coding-comment, website-manifest-key-not-valid-uri, - xml-attribute-translatable, - xml-deprecated-qweb-directive, - xml-deprecated-tree-attribute, external-request-timeout, bad-builtin-groupby, category-allowed, From 494a9b7d342256d25eeb8441c2488bfa6c09dfcb Mon Sep 17 00:00:00 2001 From: Raf Ven Date: Mon, 22 Sep 2025 16:17:51 +0200 Subject: [PATCH 037/770] [MIG] module_change_auto_install: Migration to 19.0 --- module_change_auto_install/README.rst | 90 ++++++++++--------- module_change_auto_install/__manifest__.py | 2 +- module_change_auto_install/patch.py | 89 +++++++++++------- .../readme/CONFIGURE.md | 12 +-- .../static/description/index.html | 28 +++--- module_change_auto_install/tests/__init__.py | 1 + .../tests/test_module.py | 2 - .../tests/test_patch.py | 76 ++++++++++++++++ 8 files changed, 205 insertions(+), 95 deletions(-) create mode 100644 module_change_auto_install/tests/test_patch.py diff --git a/module_change_auto_install/README.rst b/module_change_auto_install/README.rst index 886d0fc4a7b..7df89b60bb9 100644 --- a/module_change_auto_install/README.rst +++ b/module_change_auto_install/README.rst @@ -21,13 +21,13 @@ Change auto installable modules :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--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/18.0/module_change_auto_install + :target: https://github.com/OCA/server-tools/tree/19.0/module_change_auto_install :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-module_change_auto_install + :target: https://translation.odoo-community.org/projects/server-tools-19-0/server-tools-19-0-module_change_auto_install :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-tools&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=19.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -35,15 +35,15 @@ Change auto installable modules In odoo, by default some modules are marked as auto installable by the ``auto_install`` key present in the manifest. -- This feature is very useful for "glue" modules that allow two modules - to work together. (A typical example is ``sale_stock`` which allows - ``sale`` and ``stock`` modules to work together). -- However, Odoo SA also marks some modules as auto installable, even - though this is not technically required. This can happen for modules - the company wants to promote like ``iap``, modules with a big wow - effect like ``partner_autocomplete``, or some modules they consider - useful by default like ``account_edi``. See the discussion: - https://github.com/odoo/odoo/issues/71190 +- This feature is very useful for "glue" modules that allow two modules + to work together. (A typical example is ``sale_stock`` which allows + ``sale`` and ``stock`` modules to work together). +- However, Odoo SA also marks some modules as auto installable, even + though this is not technically required. This can happen for modules + the company wants to promote like ``iap``, modules with a big wow + effect like ``partner_autocomplete``, or some modules they consider + useful by default like ``account_edi``. See the discussion: + https://github.com/odoo/odoo/issues/71190 This module allows to change by configuration, the list of auto installable modules, adding or removing some modules to auto install. @@ -58,25 +58,27 @@ Installation You don't have to install this module. To make the features working : -- make the module ``module_change_auto_install`` available in your - addons path -- either update your ``odoo.cfg`` or set the environment variables - following the "Configure" section +- make the module ``module_change_auto_install`` available in your + addons path +- either update your ``odoo.cfg`` or set the environment variables + following the "Configure" section Configuration ============= -- Edit your ``odoo.cfg`` configuration file: -- Add the module ``module_change_auto_install`` in the - ``server_wide_modules`` list. -- (optional) Add a new entry ``modules_auto_install_disabled`` to mark a - list of modules as NOT auto installable. The environment variable - ``ODOO_MODULES_AUTO_INSTALL_DISABLED`` can also be set. -- (optional) Add a new entry ``modules_auto_install_enabled`` to mark a - list of modules as auto installable. This feature can be usefull for - companies that are hosting a lot of Odoo instances for many customers, - and want some modules to be always installed. The environment variable - ``ODOO_MODULES_AUTO_INSTALL_ENABLED`` can also be set. +- Edit your ``odoo.cfg`` configuration file: +- Add the module ``module_change_auto_install`` in the + ``server_wide_modules`` list. +- (optional) Add a new entry ``modules_disabled`` beneath a new section + ``[module_change_auto_install]`` to mark a list of modules as NOT + auto installable. The environment variable + ``ODOO_MODULES_AUTO_INSTALL_DISABLED`` can also be set. +- (optional) Add a new entry ``modules_enabled`` beneath a new section + ``[module_change_auto_install]`` to mark a list of modules as auto + installable. This feature can be usefull for companies that are + hosting a lot of Odoo instances for many customers, and want some + modules to be always installed. The environment variable + ``ODOO_MODULES_AUTO_INSTALL_ENABLED`` can also be set. The values in the configuration file takes precedence over the environment variable values. @@ -87,12 +89,13 @@ environment variable values. server_wide_modules = web,module_change_auto_install - modules_auto_install_disabled = + [module_change_auto_install] + modules_disabled = partner_autocomplete, iap, mail_bot - modules_auto_install_enabled = + modules_enabled = web_responsive:web, base_technical_features, disable_odoo_online, @@ -121,7 +124,8 @@ if your ``odoo.cfg`` file contains the following configuration: .. code:: cfg - modules_auto_install_enabled = + [module_change_auto_install] + modules_enabled = account_usability, web_responsive:web, base_technical_features:, @@ -129,14 +133,14 @@ if your ``odoo.cfg`` file contains the following configuration: The behaviour will be the following: -- ``account_usability`` module will be installed as soon as all the - default dependencies are installed. (here ``account``) -- ``web_responsive`` module will be installed as soon as ``web`` is - installed. (Althought ``web_responsive`` depends on ``web`` and - ``mail``) -- ``base_technical_features`` will be ALWAYS installed -- ``point_of_sale`` module will be installed as soon as ``sale`` and - ``purchase`` module are installed. +- ``account_usability`` module will be installed as soon as all the + default dependencies are installed. (here ``account``) +- ``web_responsive`` module will be installed as soon as ``web`` is + installed. (Althought ``web_responsive`` depends on ``web`` and + ``mail``) +- ``base_technical_features`` will be ALWAYS installed +- ``point_of_sale`` module will be installed as soon as ``sale`` and + ``purchase`` module are installed. When using environment variables, the same configuration is: @@ -158,7 +162,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -173,11 +177,11 @@ Authors Contributors ------------ -- Sylvain LE GAL +- Sylvain LE GAL -- XCG Consulting, part of `Orbeet `__: +- XCG Consulting, part of `Orbeet `__: - - Vincent Hatakeyama + - Vincent Hatakeyama Maintainers ----------- @@ -200,6 +204,6 @@ Current `maintainer `__: |maintainer-legalsylvain| -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/module_change_auto_install/__manifest__.py b/module_change_auto_install/__manifest__.py index 0120d9f38e2..92534d516ba 100644 --- a/module_change_auto_install/__manifest__.py +++ b/module_change_auto_install/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Change auto installable modules", "summary": "Customize auto installables modules by configuration", - "version": "18.0.1.0.3", + "version": "19.0.1.0.0", "category": "Tools", "maintainers": ["legalsylvain"], "author": "GRAP, Odoo Community Association (OCA)", diff --git a/module_change_auto_install/patch.py b/module_change_auto_install/patch.py index 6adbb0fc6b5..e4dcf83e689 100644 --- a/module_change_auto_install/patch.py +++ b/module_change_auto_install/patch.py @@ -2,14 +2,15 @@ # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import configparser import logging import os -from odoo import modules +from odoo.modules.module import Manifest from odoo.tools import config _logger = logging.getLogger(__name__) -_original_load_manifest = modules.module.load_manifest +_original_init = Manifest.__init__ def _get_modules_dict_auto_install_config(config_value): @@ -17,14 +18,14 @@ def _get_modules_dict_auto_install_config(config_value): {module_name: modules_list or False} if the odoo.cfg file contains - - modules_auto_install_enabled = + [module_change_auto_install] + modules_enabled = web_responsive:web, base_technical_features:, point_of_sale:sale/purchase, account_usability - >>> split_strip('modules_auto_install_enabled') + >>> split_strip('modules_enabled') { 'web_responsive': ['web'], 'base_technical_features': [], @@ -36,49 +37,52 @@ def _get_modules_dict_auto_install_config(config_value): """ res = {} config_value = (config_value or "").strip(" ,") - config_list = [x.strip() for x in config_value.split(",")] - for item in config_list: - if ":" in item: - res[item.split(":")[0]] = ( - item.split(":")[1] and item.split(":")[1].split("/") or [] - ) - else: - res[item] = True + if config_value: + config_list = [x.strip() for x in config_value.split(",")] + for item in config_list: + if ":" in item: + res[item.split(":")[0]] = ( + item.split(":")[1] and item.split(":")[1].split("/") or [] + ) + else: + res[item] = True return res -def _overload_load_manifest(module, mod_path=None): - res = _original_load_manifest(module, mod_path=None) - if not res: - # Specific case where a previously available module marked as auto installable - # is NOT available in the addons path. - # In that case, avoid to crash when trying to get 'depends' key. - return res - auto_install = res.get("auto_install", False) - - modules_auto_install_enabled_dict = _get_modules_dict_auto_install_config( +def _get_modules_auto_install_enabled_dict(): + return _get_modules_dict_auto_install_config( config.get( - "modules_auto_install_enabled", + "module_change_auto_install.modules_enabled", os.environ.get("ODOO_MODULES_AUTO_INSTALL_ENABLED"), ) ) - modules_auto_install_disabled_dict = _get_modules_dict_auto_install_config( + + +def _get_modules_auto_install_disabled_dict(): + return _get_modules_dict_auto_install_config( config.get( - "modules_auto_install_disabled", + "module_change_auto_install.modules_disabled", os.environ.get("ODOO_MODULES_AUTO_INSTALL_DISABLED"), ) ) + +def _get_auto_install_flag(self): + modules_auto_install_enabled_dict = _get_modules_auto_install_enabled_dict() + modules_auto_install_disabled_dict = _get_modules_auto_install_disabled_dict() + auto_install = self._Manifest__manifest_cached["auto_install"] + module = self.name + if auto_install and module in modules_auto_install_disabled_dict.keys(): _logger.info(f"Module '{module}' has been marked as NOT auto installable.") - res["auto_install"] = False + return False if not auto_install and module in modules_auto_install_enabled_dict.keys(): specific_dependencies = modules_auto_install_enabled_dict.get(module) if isinstance(specific_dependencies, bool): # Classical case _logger.info(f"Module '{module}' has been marked as auto installable.") - res["auto_install"] = set(res["depends"]) + return set(self._Manifest__manifest_cached["depends"]) else: if specific_dependencies: _logger.info( @@ -91,12 +95,33 @@ def _overload_load_manifest(module, mod_path=None): f"ALL CASES." ) - res["auto_install"] = set(specific_dependencies) + return set(specific_dependencies) + return auto_install - return res + +def _patched_init(self, *, path: str, manifest_content: dict): + _original_init(self, path=path, manifest_content=manifest_content) + # Post-process before cached_property kicks in + self.auto_install = _get_auto_install_flag(self) + if "auto_install" in self._Manifest__manifest_cached: + self._Manifest__manifest_cached["auto_install"] = self.auto_install + + +def _load_module_change_auto_install_options(rcfile): + """Load custom [module_change_auto_install] section into config.""" + cp = configparser.ConfigParser() + cp.read([rcfile]) + + if cp.has_section("module_change_auto_install"): + for key, value in cp.items("module_change_auto_install"): + # Store with prefix to avoid collisions + config[f"module_change_auto_install.{key}"] = value + _logger.debug("Loaded custom option %s=%s", key, value) def post_load(): _logger.info("Applying patch module_change_auto_install ...") - modules.module.load_manifest = _overload_load_manifest - modules.load_manifest = _overload_load_manifest + Manifest.__init__ = _patched_init + rcfile = config.get("config") + if rcfile: + _load_module_change_auto_install_options(rcfile) diff --git a/module_change_auto_install/readme/CONFIGURE.md b/module_change_auto_install/readme/CONFIGURE.md index 753ee5b1d36..38bd38c55bb 100644 --- a/module_change_auto_install/readme/CONFIGURE.md +++ b/module_change_auto_install/readme/CONFIGURE.md @@ -1,10 +1,10 @@ - Edit your `odoo.cfg` configuration file: - Add the module `module_change_auto_install` in the `server_wide_modules` list. -- (optional) Add a new entry `modules_auto_install_disabled` to mark a +- (optional) Add a new entry `modules_disabled` beneath a new section `[module_change_auto_install]` to mark a list of modules as NOT auto installable. The environment variable ``ODOO_MODULES_AUTO_INSTALL_DISABLED`` can also be set. -- (optional) Add a new entry `modules_auto_install_enabled` to mark a +- (optional) Add a new entry `modules_enabled` beneath a new section `[module_change_auto_install]` to mark a list of modules as auto installable. This feature can be usefull for companies that are hosting a lot of Odoo instances for many customers, and want some modules to be always installed. @@ -18,12 +18,13 @@ values. ``` cfg server_wide_modules = web,module_change_auto_install -modules_auto_install_disabled = +[module_change_auto_install] +modules_disabled = partner_autocomplete, iap, mail_bot -modules_auto_install_enabled = +modules_enabled = web_responsive:web, base_technical_features, disable_odoo_online, @@ -53,7 +54,8 @@ INFO db_name odoo.modules.loading: 42 modules loaded in 0.32s, 0 queries (+0 ext if your `odoo.cfg` file contains the following configuration: ``` cfg -modules_auto_install_enabled = +[module_change_auto_install] +modules_enabled = account_usability, web_responsive:web, base_technical_features:, diff --git a/module_change_auto_install/static/description/index.html b/module_change_auto_install/static/description/index.html index c5f4f92d350..bfc67ab53aa 100644 --- a/module_change_auto_install/static/description/index.html +++ b/module_change_auto_install/static/description/index.html @@ -374,7 +374,7 @@

      Change auto installable modules

      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:162ad195483bc21079128fc8075e1cf29eba3e177cffd0fbfa8d42f28a27ceab !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

      +

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

      In odoo, by default some modules are marked as auto installable by the auto_install key present in the manifest.

        @@ -421,13 +421,15 @@

        Configuration

      • Edit your odoo.cfg configuration file:
      • Add the module module_change_auto_install in the server_wide_modules list.
      • -
      • (optional) Add a new entry modules_auto_install_disabled to mark a -list of modules as NOT auto installable. The environment variable +
      • (optional) Add a new entry modules_disabled beneath a new section +[module_change_auto_install] to mark a list of modules as NOT +auto installable. The environment variable ODOO_MODULES_AUTO_INSTALL_DISABLED can also be set.
      • -
      • (optional) Add a new entry modules_auto_install_enabled to mark a -list of modules as auto installable. This feature can be usefull for -companies that are hosting a lot of Odoo instances for many customers, -and want some modules to be always installed. The environment variable +
      • (optional) Add a new entry modules_enabled beneath a new section +[module_change_auto_install] to mark a list of modules as auto +installable. This feature can be usefull for companies that are +hosting a lot of Odoo instances for many customers, and want some +modules to be always installed. The environment variable ODOO_MODULES_AUTO_INSTALL_ENABLED can also be set.

      The values in the configuration file takes precedence over the @@ -436,12 +438,13 @@

      Configuration

       server_wide_modules = web,module_change_auto_install
       
      -modules_auto_install_disabled =
      +[module_change_auto_install]
      +modules_disabled =
           partner_autocomplete,
           iap,
           mail_bot
       
      -modules_auto_install_enabled =
      +modules_enabled =
           web_responsive:web,
           base_technical_features,
           disable_odoo_online,
      @@ -463,7 +466,8 @@ 

      Configuration

      Advanced Configuration Possibilities

      if your odoo.cfg file contains the following configuration:

      -modules_auto_install_enabled =
      +[module_change_auto_install]
      +modules_enabled =
           account_usability,
           web_responsive:web,
           base_technical_features:,
      @@ -497,7 +501,7 @@ 

      Bug Tracker

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

      +feedback.

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

      @@ -529,7 +533,7 @@

      Maintainers

      promote its widespread use.

      Current maintainer:

      legalsylvain

      -

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

      +

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

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

      diff --git a/module_change_auto_install/tests/__init__.py b/module_change_auto_install/tests/__init__.py index d9b96c4fa5a..f7aa81860ba 100644 --- a/module_change_auto_install/tests/__init__.py +++ b/module_change_auto_install/tests/__init__.py @@ -1 +1,2 @@ from . import test_module +from . import test_patch diff --git a/module_change_auto_install/tests/test_module.py b/module_change_auto_install/tests/test_module.py index 7719edb98d6..a2a265b1231 100644 --- a/module_change_auto_install/tests/test_module.py +++ b/module_change_auto_install/tests/test_module.py @@ -8,8 +8,6 @@ _get_modules_dict_auto_install_config, ) -# from ..models.base import disable_changeset - class TestModule(TransactionCase): _EXPECTED_RESULTS = { diff --git a/module_change_auto_install/tests/test_patch.py b/module_change_auto_install/tests/test_patch.py new file mode 100644 index 00000000000..01a134f0c11 --- /dev/null +++ b/module_change_auto_install/tests/test_patch.py @@ -0,0 +1,76 @@ +import logging +import os +import tempfile +from unittest.mock import patch + +from odoo.modules.module import Manifest +from odoo.tests.common import TransactionCase +from odoo.tools import config + +import odoo.addons.module_change_auto_install as mcai + +_logger = logging.getLogger(__name__) + + +def make_manifest(name, depends=None, auto_install=False): + tmpdir = tempfile.mkdtemp() + module_path = os.path.join(tmpdir, name) + os.makedirs(module_path, exist_ok=True) + + manifest_content = { + "name": name, + "author": "Author", + "license": "AGPL-3", + "depends": depends or [], + "auto_install": auto_install, + } + return Manifest(path=module_path, manifest_content=manifest_content) + + +class TestModuleChangeAutoInstall(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Apply patch once for all tests + mcai.post_load() + + def test_default_auto_install(self): + m = make_manifest("test_module", ["base"], auto_install=False) + self.assertFalse(m._Manifest__manifest_cached["auto_install"]) + + @patch.dict( + config.options, + { + "module_change_auto_install.modules_disabled": "test_module", + }, + ) + def test_disabled_module(self): + m = make_manifest("test_module", ["base"], auto_install=True) + self.assertTrue(m._Manifest__manifest_cached["auto_install"] is False) + + @patch.dict( + config.options, {"module_change_auto_install.modules_enabled": "test_module"} + ) + def test_enabled_module_unconditional(self): + m = make_manifest("test_module", ["base"], auto_install=False) + # Should return its dependencies as auto-install condition + self.assertEqual(m._Manifest__manifest_cached["auto_install"], set(["base"])) + + @patch.dict( + config.options, + { + "module_change_auto_install.modules_enabled": "test_module:dep1/dep2", + }, + ) + def test_enabled_module_with_specific_dependencies(self): + m = make_manifest("test_module", ["base"], auto_install=False) + self.assertEqual( + m._Manifest__manifest_cached["auto_install"], set(["dep1", "dep2"]) + ) + + @patch.dict( + config.options, {"module_change_auto_install.modules_enabled": "test_module:"} + ) + def test_enabled_module_all_cases(self): + m = make_manifest("test_module", ["base"], auto_install=False) + self.assertEqual(m._Manifest__manifest_cached["auto_install"], set()) From 3c73a740303ad81c811a08dabb7cfb51260225a3 Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Tue, 28 Jan 2014 22:09:41 +0100 Subject: [PATCH 038/770] [ADD] Database cleanup module --- database_cleanup/__init__.py | 1 + database_cleanup/__openerp__.py | 54 +++++++++ database_cleanup/model/__init__.py | 5 + database_cleanup/model/purge_columns.py | 131 ++++++++++++++++++++++ database_cleanup/model/purge_models.py | 115 +++++++++++++++++++ database_cleanup/model/purge_modules.py | 91 +++++++++++++++ database_cleanup/model/purge_tables.py | 137 +++++++++++++++++++++++ database_cleanup/model/purge_wizard.py | 63 +++++++++++ database_cleanup/static/src/img/icon.png | Bin 0 -> 30647 bytes database_cleanup/view/menu.xml | 41 +++++++ database_cleanup/view/purge_columns.xml | 37 ++++++ database_cleanup/view/purge_models.xml | 36 ++++++ database_cleanup/view/purge_modules.xml | 36 ++++++ database_cleanup/view/purge_tables.xml | 36 ++++++ 14 files changed, 783 insertions(+) create mode 100644 database_cleanup/__init__.py create mode 100644 database_cleanup/__openerp__.py create mode 100644 database_cleanup/model/__init__.py create mode 100644 database_cleanup/model/purge_columns.py create mode 100644 database_cleanup/model/purge_models.py create mode 100644 database_cleanup/model/purge_modules.py create mode 100644 database_cleanup/model/purge_tables.py create mode 100644 database_cleanup/model/purge_wizard.py create mode 100644 database_cleanup/static/src/img/icon.png create mode 100644 database_cleanup/view/menu.xml create mode 100644 database_cleanup/view/purge_columns.xml create mode 100644 database_cleanup/view/purge_models.xml create mode 100644 database_cleanup/view/purge_modules.xml create mode 100644 database_cleanup/view/purge_tables.xml diff --git a/database_cleanup/__init__.py b/database_cleanup/__init__.py new file mode 100644 index 00000000000..9186ee3ad24 --- /dev/null +++ b/database_cleanup/__init__.py @@ -0,0 +1 @@ +from . import model diff --git a/database_cleanup/__openerp__.py b/database_cleanup/__openerp__.py new file mode 100644 index 00000000000..c752ae48e11 --- /dev/null +++ b/database_cleanup/__openerp__.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Database cleanup', + 'version': '0.1', + 'author': 'Therp BV', + 'depends': ['base'], + 'license': 'AGPL-3', + 'category': 'Tools', + 'data': [ + 'view/purge_modules.xml', + 'view/purge_models.xml', + 'view/purge_columns.xml', + 'view/purge_tables.xml', + 'view/menu.xml', + ], + 'description': """\ +Clean your OpenERP database from remnants of modules, models, columns and +tables left by uninstalled modules (prior to 7.0) or a homebrew database upgrade +to a new major version of OpenERP. + +After installation of this module, go to the Settings menu -> Technical -> +Database cleanup. Go through the modules, models, columns and tables +entries under this menu (in that order) and find out if there is orphaned data +in your database. You can either delete entries by line, or sweep all entries +in one big step (if you are *really* confident). + +Caution! This module is potentially harmful and can *easily* destroy the +integrity of your data. Do not use if you are not entirely comfortable +with the technical details of the OpenERP data model of *all* the modules +that have ever been installed on your database, and do not purge any module, +model, column or table if you do not know exactly what you are doing. +""", + +} diff --git a/database_cleanup/model/__init__.py b/database_cleanup/model/__init__.py new file mode 100644 index 00000000000..9b366b62bf3 --- /dev/null +++ b/database_cleanup/model/__init__.py @@ -0,0 +1,5 @@ +from . import purge_wizard +from . import purge_modules +from . import purge_models +from . import purge_columns +from . import purge_tables diff --git a/database_cleanup/model/purge_columns.py b/database_cleanup/model/purge_columns.py new file mode 100644 index 00000000000..5266ab160d6 --- /dev/null +++ b/database_cleanup/model/purge_columns.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields +from openerp.tools.translate import _ + + +class CleanupPurgeLineColumn(orm.TransientModel): + _inherit = 'cleanup.purge.line' + _name = 'cleanup.purge.line.column' + + _columns = { + 'model_id': fields.many2one( + 'ir.model', 'Model', + required=True, ondelete='CASCADE'), + 'wizard_id': fields.many2one( + 'cleanup.purge.wizard.column', 'Purge Wizard', readonly=True), + } + + def purge(self, cr, uid, ids, context=None): + """ + Unlink columns upon manual confirmation. + """ + for line in self.browse(cr, uid, ids, context=context): + if line.purged: + continue + + model_pool = self.pool[line.model_id.model] + + # Check whether the column actually still exists. + # Inheritance such as stock.picking.in from stock.picking + # can lead to double attempts at removal + cr.execute( + 'SELECT count(attname) FROM pg_attribute ' + 'WHERE attrelid = ' + '( SELECT oid FROM pg_class WHERE relname = %s ) ' + 'AND attname = %s', + (model_pool._table, line.name)); + if not cr.fetchone()[0]: + continue + + self.logger.info( + 'Dropping column %s from table %s', + line.name, model_pool._table) + cr.execute( + """ + ALTER TABLE "%s" DROP COLUMN "%s" + """ % (model_pool._table, line.name)) + line.write({'purged': True}) + cr.commit() + return True + +class CleanupPurgeWizardColumn(orm.TransientModel): + _inherit = 'cleanup.purge.wizard' + _name = 'cleanup.purge.wizard.column' + + def default_get(self, cr, uid, fields, context=None): + res = super(CleanupPurgeWizardColumn, self).default_get( + cr, uid, fields, context=context) + if 'name' in fields: + res['name'] = _('Purge columns') + return res + + def get_orphaned_columns(self, cr, uid, model_pool, context=None): + """ + From openobject-server/openerp/osv/orm.py + Iterate on the database columns to identify columns + of fields which have been removed + """ + columns = [ + c for c in model_pool._columns + if not (isinstance(model_pool._columns[c], fields.function) + and not model_pool._columns[c].store)] + columns += orm.MAGIC_COLUMNS + cr.execute("SELECT a.attname" + " FROM pg_class c, pg_attribute a" + " WHERE c.relname=%s" + " AND c.oid=a.attrelid" + " AND a.attisdropped=%s" + " AND pg_catalog.format_type(a.atttypid, a.atttypmod)" + " NOT IN ('cid', 'tid', 'oid', 'xid')" + " AND a.attname NOT IN %s", + (model_pool._table, False, tuple(columns))), + return [column[0] for column in cr.fetchall()] + + def find(self, cr, uid, context=None): + """ + Search for columns that are not in the corresponding model. + """ + res = [] + model_pool = self.pool['ir.model'] + model_ids = model_pool.search(cr, uid, [], context=context) + line_pool = self.pool['cleanup.purge.line.column'] + for model in model_pool.browse(cr, uid, model_ids, context=context): + model_pool = self.pool.get(model.model) + if not model_pool or not model_pool._auto: + continue + for column in self.get_orphaned_columns( + cr, uid, model_pool, context=context): + res.append((0, 0, { + 'name': column, + 'model_id': model.id})) + if not res: + raise orm.except_orm( + _('Nothing to do'), + _('No orphaned columns found')) + return res + + _columns = { + 'purge_line_ids': fields.one2many( + 'cleanup.purge.line.column', + 'wizard_id', 'Columns to purge'), + } diff --git a/database_cleanup/model/purge_models.py b/database_cleanup/model/purge_models.py new file mode 100644 index 00000000000..8355b4921dd --- /dev/null +++ b/database_cleanup/model/purge_models.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields +from openerp.tools.translate import _ +from openerp.addons.base.ir.ir_model import MODULE_UNINSTALL_FLAG + + +class IrModel(orm.Model): + _inherit = 'ir.model' + + def _drop_table(self, cr, uid, ids, context=None): + # Allow to skip this step during model unlink + # The super method crashes if the model cannot be instantiated + if context and context.get('no_drop_table'): + return True + return super(IrModel, self)._drop_table(cr, uid, ids, context=context) + + +class CleanupPurgeLineModel(orm.TransientModel): + _inherit = 'cleanup.purge.line' + _name = 'cleanup.purge.line.model' + + _columns = { + 'wizard_id': fields.many2one( + 'cleanup.purge.wizard.model', 'Purge Wizard', readonly=True), + } + + def purge(self, cr, uid, ids, context=None): + """ + Unlink models upon manual confirmation. + """ + model_pool = self.pool['ir.model'] + attachment_pool = self.pool['ir.attachment'] + constraint_pool = self.pool['ir.model.constraint'] + + local_context=(context or {}).copy() + local_context.update({ + MODULE_UNINSTALL_FLAG: True, + 'no_drop_table': True, + }) + + for line in self.browse(cr, uid, ids, context=context): + cr.execute( + "SELECT id, model from ir_model WHERE model = %s", + (line.name,)) + row = cr.fetchone() + if row: + self.logger.info('Purging model %s', row[1]) + attachment_ids = attachment_pool.search( + cr, uid, [('res_model', '=', line.name)], context=context) + if attachment_ids: + attachment_pool.write( + cr, uid, attachment_ids, {'res_model': False}, + context=context) + constraint_ids = constraint_pool.search( + cr, uid, [('model', '=', line.name)], context=context) + if constraint_ids: + constraint_pool.unlink( + cr, uid, constraint_ids, context=context) + model_pool.unlink(cr, uid, [row[0]], context=local_context) + line.write({'purged': True}) + cr.commit() + return True + + +class CleanupPurgeWizardModel(orm.TransientModel): + _inherit = 'cleanup.purge.wizard' + _name = 'cleanup.purge.wizard.model' + + def default_get(self, cr, uid, fields, context=None): + res = super(CleanupPurgeWizardModel, self).default_get( + cr, uid, fields, context=context) + if 'name' in fields: + res['name'] = _('Purge models') + return res + + def find(self, cr, uid, context=None): + """ + Search for models that cannot be instantiated. + """ + res = [] + cr.execute("SELECT model from ir_model") + for (model,) in cr.fetchall(): + if not self.pool.get(model): + res.append((0, 0, {'name': model})) + if not res: + raise orm.except_orm( + _('Nothing to do'), + _('No orphaned models found')) + return res + + _columns = { + 'purge_line_ids': fields.one2many( + 'cleanup.purge.line.model', + 'wizard_id', 'Models to purge'), + } diff --git a/database_cleanup/model/purge_modules.py b/database_cleanup/model/purge_modules.py new file mode 100644 index 00000000000..b62a037a4fa --- /dev/null +++ b/database_cleanup/model/purge_modules.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import pooler +from openerp.osv import orm, fields +from openerp.modules.module import get_module_path +from openerp.tools.translate import _ + + +class CleanupPurgeLineModule(orm.TransientModel): + _inherit = 'cleanup.purge.line' + _name = 'cleanup.purge.line.module' + + _columns = { + 'wizard_id': fields.many2one( + 'cleanup.purge.wizard.module', 'Purge Wizard', readonly=True), + } + + def purge(self, cr, uid, ids, context=None): + """ + Uninstall modules upon manual confirmation, then reload + the database. + """ + module_pool = self.pool['ir.module.module'] + lines = self.browse(cr, uid, ids, context=context) + module_names = [line.name for line in lines if not line.purged] + module_ids = module_pool.search( + cr, uid, [('name', 'in', module_names)], context=context) + if not module_ids: + return True + self.logger.info('Purging modules %s', ', '.join(module_names)) + module_pool.write( + cr, uid, module_ids, {'state': 'to remove'}, context=context) + cr.commit() + _db, _pool = pooler.restart_pool(cr.dbname, update_module=True) + module_pool.unlink(cr, uid, module_ids, context=context) + return self.write(cr, uid, ids, {'purged': True}, context=context) + + +class CleanupPurgeWizardModule(orm.TransientModel): + _inherit = 'cleanup.purge.wizard' + _name = 'cleanup.purge.wizard.module' + + def default_get(self, cr, uid, fields, context=None): + res = super(CleanupPurgeWizardModule, self).default_get( + cr, uid, fields, context=context) + if 'name' in fields: + res['name'] = _('Purge modules') + return res + + def find(self, cr, uid, context=None): + module_pool = self.pool['ir.module.module'] + module_ids = module_pool.search(cr, uid, [], context=context) + res = [] + for module in module_pool.browse(cr, uid, module_ids, context=context): + if get_module_path(module.name): + continue + if module.state == 'uninstalled': + module_pool.unlink(cr, uid, module.id, context=context) + continue + res.append((0, 0, {'name': module.name})) + + if not res: + raise orm.except_orm( + _('Nothing to do'), + _('No modules found to purge')) + return res + + _columns = { + 'purge_line_ids': fields.one2many( + 'cleanup.purge.line.module', + 'wizard_id', 'Modules to purge'), + } diff --git a/database_cleanup/model/purge_tables.py b/database_cleanup/model/purge_tables.py new file mode 100644 index 00000000000..8fb6120e23f --- /dev/null +++ b/database_cleanup/model/purge_tables.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields +from openerp.tools.translate import _ + + +class CleanupPurgeLineTable(orm.TransientModel): + _inherit = 'cleanup.purge.line' + _name = 'cleanup.purge.line.table' + + _columns = { + 'wizard_id': fields.many2one( + 'cleanup.purge.wizard.table', 'Purge Wizard', readonly=True), + } + + def purge(self, cr, uid, ids, context=None): + """ + Unlink tables upon manual confirmation. + """ + lines = self.browse(cr, uid, ids, context=context) + tables = [line.name for line in lines] + for line in lines: + if line.purged: + continue + + # Retrieve constraints on the tables to be dropped + # This query is referenced in numerous places + # on the Internet but credits probably go to Tom Lane + # in this post http://www.postgresql.org/\ + # message-id/22895.1226088573@sss.pgh.pa.us + # Only using the constraint name and the source table, + # but I'm leaving the rest in for easier debugging + cr.execute( + """ + SELECT conname, confrelid::regclass, af.attname AS fcol, + conrelid::regclass, a.attname AS col + FROM pg_attribute af, pg_attribute a, + (SELECT conname, conrelid, confrelid,conkey[i] AS conkey, + confkey[i] AS confkey + FROM (select conname, conrelid, confrelid, conkey, confkey, + generate_series(1,array_upper(conkey,1)) AS i + FROM pg_constraint WHERE contype = 'f') ss) ss2 + WHERE af.attnum = confkey AND af.attrelid = confrelid AND + a.attnum = conkey AND a.attrelid = conrelid + AND confrelid::regclass = '%s'::regclass; + """ % line.name) + + for constraint in cr.fetchall(): + if constraint[3] in tables: + self.logger.info( + 'Dropping constraint %s on table %s (to be dropped)', + constraint[0], constraint[3]) + cr.execute( + "ALTER TABLE %s DROP CONSTRAINT %s" % ( + constraint[3], constraint[0])) + + self.logger.info( + 'Dropping table %s', line.name) + cr.execute("DROP TABLE \"%s\"" % (line.name,)) + line.write({'purged': True}) + cr.commit() + return True + +class CleanupPurgeWizardTable(orm.TransientModel): + _inherit = 'cleanup.purge.wizard' + _name = 'cleanup.purge.wizard.table' + + def default_get(self, cr, uid, fields, context=None): + res = super(CleanupPurgeWizardTable, self).default_get( + cr, uid, fields, context=context) + if 'name' in fields: + res['name'] = _('Purge modules') + return res + + def find(self, cr, uid, context=None): + """ + Search for tables that cannot be instantiated. + Ignore views for now. + """ + model_ids = self.pool['ir.model'].search(cr, uid, [], context=context) + line_pool = self.pool['cleanup.purge.line.table'] + known_tables = [] + for model in self.pool['ir.model'].browse( + cr, uid, model_ids, context=context): + + model_pool = self.pool.get(model.model) + if not model_pool: + continue + known_tables.append(model_pool._table) + known_tables += [ + column._sql_names(model_pool)[0] + for column in model_pool._columns.values() + if column._type == 'many2many' + # unstored function fields of type m2m don't have _rel + and hasattr(column, '_rel') + ] + + # Cannot pass table names as a psycopg argument + known_tables_repr = ",".join( + [("'%s'" % table) for table in known_tables]) + cr.execute( + """ + SELECT table_name FROM information_schema.tables + WHERE table_schema = 'public' AND table_type = 'BASE TABLE' + AND table_name NOT IN (%s)""" % known_tables_repr) + + res = [(0, 0, {'name': row[0]}) for row in cr.fetchall()] + if not res: + raise orm.except_orm( + _('Nothing to do'), + _('No orphaned tables found')) + return res + + _columns = { + 'purge_line_ids': fields.one2many( + 'cleanup.purge.line.table', + 'wizard_id', 'Tables to purge'), + } diff --git a/database_cleanup/model/purge_wizard.py b/database_cleanup/model/purge_wizard.py new file mode 100644 index 00000000000..542ac1507f3 --- /dev/null +++ b/database_cleanup/model/purge_wizard.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import logging +from openerp.osv import orm, fields + + +class CleanupPurgeLine(orm.AbstractModel): + """ Abstract base class for the purge wizard lines """ + _name = 'cleanup.purge.line' + _columns = { + 'name': fields.char('Name', size=256, readonly=True), + 'purged': fields.boolean('Purged', readonly=True), + } + + logger = logging.getLogger('openerp.addons.database_cleanup') + + def purge(self, cr, uid, ids, context=None): + raise NotImplementedError + +class PurgeWizard(orm.AbstractModel): + """ Abstract base class for the purge wizards """ + _name = 'cleanup.purge.wizard' + + def default_get(self, cr, uid, fields, context=None): + res = super(PurgeWizard, self).default_get( + cr, uid, fields, context=context) + if 'purge_line_ids' in fields: + res['purge_line_ids'] = self.find(cr, uid, context=None) + return res + + def find(self, cr, uid, ids, context=None): + raise NotImplementedError + + def purge_all(self, cr, uid, ids, context=None): + line_pool = self.pool[self._columns['purge_line_ids']._obj] + for wizard in self.browse(cr, uid, ids, context=context): + line_pool.purge( + cr, uid, [line.id for line in wizard.purge_line_ids], + context=context) + return True + + _columns = { + 'name': fields.char('Name', size=64, readonly=True), + } diff --git a/database_cleanup/static/src/img/icon.png b/database_cleanup/static/src/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6980d05de2f0a9bd93df4207cc0245eb566f818a GIT binary patch literal 30647 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 G3WqHI+Gl@?7q9)=zQdDv^3R>v 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 aO#eUJ;J(3(mu?6E0000 + + + + + Database cleanup + + + + + + + Purge obsolete modules + + + + + + + Purge obsolete models + + + + + + + Purge obsolete columns + + + + + + + Purge obsolete tables + + + + + + + diff --git a/database_cleanup/view/purge_columns.xml b/database_cleanup/view/purge_columns.xml new file mode 100644 index 00000000000..40ed4a4f6fd --- /dev/null +++ b/database_cleanup/view/purge_columns.xml @@ -0,0 +1,37 @@ + + + + + + Form view for purge columns wizard + cleanup.purge.wizard.column + +
      +

      + +

      +
      + +
      +
      + + + Create missing indexes + ir.actions.server + code + + action = self.get_wizard_action(cr, uid, context=context) + + + + cleanup.create_indexes.line + + primary + + + + + + + Create + ir.actions.server + code + + self.purge(cr, uid, context.get('active_ids', []), context) + + + + Create indexes + action + client_action_multi + cleanup.create_indexes.line + + + diff --git a/database_cleanup/views/menu.xml b/database_cleanup/views/menu.xml index 0796f907b07..c669ddfb875 100644 --- a/database_cleanup/views/menu.xml +++ b/database_cleanup/views/menu.xml @@ -52,5 +52,19 @@ + + Create missing indexes + + + + + + + Purge obsolete properties + + + + +
      diff --git a/database_cleanup/views/purge_properties.xml b/database_cleanup/views/purge_properties.xml new file mode 100644 index 00000000000..cf9b8c456a3 --- /dev/null +++ b/database_cleanup/views/purge_properties.xml @@ -0,0 +1,49 @@ + + + + + cleanup.purge.wizard.property + + primary + + + + + + + Purge properties + ir.actions.server + code + + action = self.get_wizard_action(cr, uid, context=context) + + + + cleanup.purge.line.property + + primary + + + + + + + + + Purge + ir.actions.server + code + + self.purge(cr, uid, context.get('active_ids', []), context) + + + + Purge + action + client_action_multi + cleanup.purge.line.property + + + + + diff --git a/database_cleanup/views/purge_wizard.xml b/database_cleanup/views/purge_wizard.xml index 40417f3a813..0fd88e793e2 100644 --- a/database_cleanup/views/purge_wizard.xml +++ b/database_cleanup/views/purge_wizard.xml @@ -9,7 +9,7 @@
    -

    Usage

    +

    Usage

    After installation of this module, go to the Settings menu -> Technical -> Database cleanup. This menu is only available to members of the Access Rights group. Go through the modules, models, columns and @@ -403,7 +408,7 @@

    Usage

    Try me on Runbot

    -

    Bug Tracker

    +

    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 @@ -411,15 +416,15 @@

    Bug Tracker

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

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • Therp BV
    -

    Contributors

    +

    Contributors

    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association @@ -444,5 +449,6 @@

    Maintainers

    + From 8af4eda6202eab11f686c1842b065c6c89677505 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 8 Dec 2025 15:28:38 +0000 Subject: [PATCH 115/770] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: server-tools-18.0/server-tools-18.0-database_cleanup Translate-URL: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-database_cleanup/ --- database_cleanup/i18n/am.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/ar.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/bg.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/bs.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/ca.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/cs.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/cs_CZ.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/da.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/de.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/el_GR.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/en_GB.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_AR.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_CL.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_CO.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_CR.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_DO.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_EC.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_ES.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_MX.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_PE.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_PY.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/es_VE.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/et.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/eu.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/fa.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/fi.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/fr.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/fr_CA.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/fr_CH.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/gl.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/gl_ES.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/he.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/hr.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/hr_HR.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/hu.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/id.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/it.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/ja.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/ko.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/lt.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/lt_LT.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/lv.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/mk.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/mn.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/nb.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/nb_NO.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/nl.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/nl_BE.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/nl_NL.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/pl.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/pt.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/pt_BR.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/pt_PT.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/ro.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/ru.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/sk.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/sl.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/sr.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/sr@latin.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/sv.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/th.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/tr.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/tr_TR.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/uk.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/vi.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/vi_VN.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/zh_CN.po | 32 ++++++++++++++++++++++++++++++- database_cleanup/i18n/zh_TW.po | 32 ++++++++++++++++++++++++++++++- 69 files changed, 2139 insertions(+), 69 deletions(-) diff --git a/database_cleanup/i18n/am.po b/database_cleanup/i18n/am.po index ee9b77110ca..1e0da7964d3 100644 --- a/database_cleanup/i18n/am.po +++ b/database_cleanup/i18n/am.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/ar.po b/database_cleanup/i18n/ar.po index 0c2a351126c..e3277f47b54 100644 --- a/database_cleanup/i18n/ar.po +++ b/database_cleanup/i18n/ar.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/bg.po b/database_cleanup/i18n/bg.po index 2525d0a9397..6979b1a5cbe 100644 --- a/database_cleanup/i18n/bg.po +++ b/database_cleanup/i18n/bg.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/bs.po b/database_cleanup/i18n/bs.po index be2ce585cb7..04896f60df2 100644 --- a/database_cleanup/i18n/bs.po +++ b/database_cleanup/i18n/bs.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/ca.po b/database_cleanup/i18n/ca.po index 18833beaf42..f1d3b8a85e4 100644 --- a/database_cleanup/i18n/ca.po +++ b/database_cleanup/i18n/ca.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "Purgar taules" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Purgar aquest model" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/cs.po b/database_cleanup/i18n/cs.po index 8e844f4be59..1b6dad6ce5e 100644 --- a/database_cleanup/i18n/cs.po +++ b/database_cleanup/i18n/cs.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/cs_CZ.po b/database_cleanup/i18n/cs_CZ.po index b06c062d238..641d3894701 100644 --- a/database_cleanup/i18n/cs_CZ.po +++ b/database_cleanup/i18n/cs_CZ.po @@ -472,10 +472,40 @@ msgid "Purge tables" msgstr "Vyčištění tabulek" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Vyčistěte tento model" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/da.po b/database_cleanup/i18n/da.po index 485de1db8f6..d1960265a75 100644 --- a/database_cleanup/i18n/da.po +++ b/database_cleanup/i18n/da.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/de.po b/database_cleanup/i18n/de.po index a6101a1b187..7cd9ef9f759 100644 --- a/database_cleanup/i18n/de.po +++ b/database_cleanup/i18n/de.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "Lösche Tabellen" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Lösche dieses Modell" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/el_GR.po b/database_cleanup/i18n/el_GR.po index 4d56bbce46a..37820e70e96 100644 --- a/database_cleanup/i18n/el_GR.po +++ b/database_cleanup/i18n/el_GR.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/en_GB.po b/database_cleanup/i18n/en_GB.po index a4854e3d222..ffa9b9a1442 100644 --- a/database_cleanup/i18n/en_GB.po +++ b/database_cleanup/i18n/en_GB.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es.po b/database_cleanup/i18n/es.po index 7c243523abb..875c76cb25e 100644 --- a/database_cleanup/i18n/es.po +++ b/database_cleanup/i18n/es.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "Limpiar tablas" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Limpiar este modelo" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_AR.po b/database_cleanup/i18n/es_AR.po index 26c67077031..a6ef9f287a7 100644 --- a/database_cleanup/i18n/es_AR.po +++ b/database_cleanup/i18n/es_AR.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "Limpiar tablas" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Limpiar este modelo" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_CL.po b/database_cleanup/i18n/es_CL.po index 409561ed3ed..1d08dfc296d 100644 --- a/database_cleanup/i18n/es_CL.po +++ b/database_cleanup/i18n/es_CL.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_CO.po b/database_cleanup/i18n/es_CO.po index 311ca13d7f9..8f39534e971 100644 --- a/database_cleanup/i18n/es_CO.po +++ b/database_cleanup/i18n/es_CO.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_CR.po b/database_cleanup/i18n/es_CR.po index 91e34dc6968..ea576dedf05 100644 --- a/database_cleanup/i18n/es_CR.po +++ b/database_cleanup/i18n/es_CR.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_DO.po b/database_cleanup/i18n/es_DO.po index 2d13a0dec2b..1d9df8e8200 100644 --- a/database_cleanup/i18n/es_DO.po +++ b/database_cleanup/i18n/es_DO.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_EC.po b/database_cleanup/i18n/es_EC.po index 5cd6c06915a..53c07ca5538 100644 --- a/database_cleanup/i18n/es_EC.po +++ b/database_cleanup/i18n/es_EC.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_ES.po b/database_cleanup/i18n/es_ES.po index 6dc2e611dd6..014f980582b 100644 --- a/database_cleanup/i18n/es_ES.po +++ b/database_cleanup/i18n/es_ES.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_MX.po b/database_cleanup/i18n/es_MX.po index 524c0fc2f04..23ec309ab44 100644 --- a/database_cleanup/i18n/es_MX.po +++ b/database_cleanup/i18n/es_MX.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_PE.po b/database_cleanup/i18n/es_PE.po index 8db4dd81563..989f74e3722 100644 --- a/database_cleanup/i18n/es_PE.po +++ b/database_cleanup/i18n/es_PE.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_PY.po b/database_cleanup/i18n/es_PY.po index fd22c74c957..a6ea8d8e969 100644 --- a/database_cleanup/i18n/es_PY.po +++ b/database_cleanup/i18n/es_PY.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/es_VE.po b/database_cleanup/i18n/es_VE.po index 330ec739d4f..9d539fcf55a 100644 --- a/database_cleanup/i18n/es_VE.po +++ b/database_cleanup/i18n/es_VE.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/et.po b/database_cleanup/i18n/et.po index 36dce4a2035..881c97ed978 100644 --- a/database_cleanup/i18n/et.po +++ b/database_cleanup/i18n/et.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/eu.po b/database_cleanup/i18n/eu.po index 3c9791e72ac..e2241179a9b 100644 --- a/database_cleanup/i18n/eu.po +++ b/database_cleanup/i18n/eu.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/fa.po b/database_cleanup/i18n/fa.po index 0c4dd620814..53841255504 100644 --- a/database_cleanup/i18n/fa.po +++ b/database_cleanup/i18n/fa.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/fi.po b/database_cleanup/i18n/fi.po index 568616def10..06b6e63feda 100644 --- a/database_cleanup/i18n/fi.po +++ b/database_cleanup/i18n/fi.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/fr.po b/database_cleanup/i18n/fr.po index fe6bfbeb445..6dff84bf912 100644 --- a/database_cleanup/i18n/fr.po +++ b/database_cleanup/i18n/fr.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/fr_CA.po b/database_cleanup/i18n/fr_CA.po index 14bd36f4c9e..3399155040e 100644 --- a/database_cleanup/i18n/fr_CA.po +++ b/database_cleanup/i18n/fr_CA.po @@ -472,10 +472,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/fr_CH.po b/database_cleanup/i18n/fr_CH.po index 2e0b74c1524..12ec3a47dc0 100644 --- a/database_cleanup/i18n/fr_CH.po +++ b/database_cleanup/i18n/fr_CH.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/gl.po b/database_cleanup/i18n/gl.po index 89ac49553f5..a8db5ca64d7 100644 --- a/database_cleanup/i18n/gl.po +++ b/database_cleanup/i18n/gl.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/gl_ES.po b/database_cleanup/i18n/gl_ES.po index 141b17325e0..6a97a34fe05 100644 --- a/database_cleanup/i18n/gl_ES.po +++ b/database_cleanup/i18n/gl_ES.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/he.po b/database_cleanup/i18n/he.po index 265e2ab0357..c64c48cc124 100644 --- a/database_cleanup/i18n/he.po +++ b/database_cleanup/i18n/he.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/hr.po b/database_cleanup/i18n/hr.po index f69a38d968a..58e3d80b47b 100644 --- a/database_cleanup/i18n/hr.po +++ b/database_cleanup/i18n/hr.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/hr_HR.po b/database_cleanup/i18n/hr_HR.po index 029e5b641bd..fa595ef7bbf 100644 --- a/database_cleanup/i18n/hr_HR.po +++ b/database_cleanup/i18n/hr_HR.po @@ -472,10 +472,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/hu.po b/database_cleanup/i18n/hu.po index 1794c1939f3..b0f9564879f 100644 --- a/database_cleanup/i18n/hu.po +++ b/database_cleanup/i18n/hu.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/id.po b/database_cleanup/i18n/id.po index 850858f8f31..359f182a4df 100644 --- a/database_cleanup/i18n/id.po +++ b/database_cleanup/i18n/id.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/it.po b/database_cleanup/i18n/it.po index a364a212bac..204221560db 100644 --- a/database_cleanup/i18n/it.po +++ b/database_cleanup/i18n/it.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "Pulisci tabelle" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Pulisci questo modello" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/ja.po b/database_cleanup/i18n/ja.po index ea1866eafa2..e4c2120f60a 100644 --- a/database_cleanup/i18n/ja.po +++ b/database_cleanup/i18n/ja.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/ko.po b/database_cleanup/i18n/ko.po index 8bbb24ccfbd..d473206c8d6 100644 --- a/database_cleanup/i18n/ko.po +++ b/database_cleanup/i18n/ko.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/lt.po b/database_cleanup/i18n/lt.po index a83df3f39c9..56acdd1217c 100644 --- a/database_cleanup/i18n/lt.po +++ b/database_cleanup/i18n/lt.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/lt_LT.po b/database_cleanup/i18n/lt_LT.po index f9aa26a042b..17fdbc70f8e 100644 --- a/database_cleanup/i18n/lt_LT.po +++ b/database_cleanup/i18n/lt_LT.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/lv.po b/database_cleanup/i18n/lv.po index c2058702c9f..16a0a08a905 100644 --- a/database_cleanup/i18n/lv.po +++ b/database_cleanup/i18n/lv.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/mk.po b/database_cleanup/i18n/mk.po index 47967a90349..0f35c1fa219 100644 --- a/database_cleanup/i18n/mk.po +++ b/database_cleanup/i18n/mk.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/mn.po b/database_cleanup/i18n/mn.po index 149384cf578..c73ceb0603b 100644 --- a/database_cleanup/i18n/mn.po +++ b/database_cleanup/i18n/mn.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/nb.po b/database_cleanup/i18n/nb.po index b6bc3ca4b1e..4babc061929 100644 --- a/database_cleanup/i18n/nb.po +++ b/database_cleanup/i18n/nb.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/nb_NO.po b/database_cleanup/i18n/nb_NO.po index 0cd2d7e1864..ffbf3346b45 100644 --- a/database_cleanup/i18n/nb_NO.po +++ b/database_cleanup/i18n/nb_NO.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/nl.po b/database_cleanup/i18n/nl.po index 47fa67e152d..e6a688c4269 100644 --- a/database_cleanup/i18n/nl.po +++ b/database_cleanup/i18n/nl.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/nl_BE.po b/database_cleanup/i18n/nl_BE.po index 34830150905..3ffc86c025d 100644 --- a/database_cleanup/i18n/nl_BE.po +++ b/database_cleanup/i18n/nl_BE.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/nl_NL.po b/database_cleanup/i18n/nl_NL.po index 0dd053a6738..cacd38c5981 100644 --- a/database_cleanup/i18n/nl_NL.po +++ b/database_cleanup/i18n/nl_NL.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/pl.po b/database_cleanup/i18n/pl.po index fc8760493ee..bd6f874cca6 100644 --- a/database_cleanup/i18n/pl.po +++ b/database_cleanup/i18n/pl.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/pt.po b/database_cleanup/i18n/pt.po index 551fc02848f..c50ea44f265 100644 --- a/database_cleanup/i18n/pt.po +++ b/database_cleanup/i18n/pt.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/pt_BR.po b/database_cleanup/i18n/pt_BR.po index 92a8b332295..9e128d1afd5 100644 --- a/database_cleanup/i18n/pt_BR.po +++ b/database_cleanup/i18n/pt_BR.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "Excluir tabelas" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Excluir este modelo" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/pt_PT.po b/database_cleanup/i18n/pt_PT.po index 2a129cef2af..1d9c8278880 100644 --- a/database_cleanup/i18n/pt_PT.po +++ b/database_cleanup/i18n/pt_PT.po @@ -473,10 +473,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/ro.po b/database_cleanup/i18n/ro.po index 3b32ef535be..edb9b15cc09 100644 --- a/database_cleanup/i18n/ro.po +++ b/database_cleanup/i18n/ro.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/ru.po b/database_cleanup/i18n/ru.po index ffc9bf75748..f013f027ecc 100644 --- a/database_cleanup/i18n/ru.po +++ b/database_cleanup/i18n/ru.po @@ -472,10 +472,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/sk.po b/database_cleanup/i18n/sk.po index 77bc47f06d0..046a4ec518f 100644 --- a/database_cleanup/i18n/sk.po +++ b/database_cleanup/i18n/sk.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/sl.po b/database_cleanup/i18n/sl.po index f34a6f6756c..67ca280462f 100644 --- a/database_cleanup/i18n/sl.po +++ b/database_cleanup/i18n/sl.po @@ -472,10 +472,40 @@ msgid "Purge tables" msgstr "Očiščenje tabel" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Očisti ta model" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/sr.po b/database_cleanup/i18n/sr.po index 419cafd4246..59940633317 100644 --- a/database_cleanup/i18n/sr.po +++ b/database_cleanup/i18n/sr.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/sr@latin.po b/database_cleanup/i18n/sr@latin.po index 91fdd5e3f0b..9930948a8e8 100644 --- a/database_cleanup/i18n/sr@latin.po +++ b/database_cleanup/i18n/sr@latin.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/sv.po b/database_cleanup/i18n/sv.po index b4e04d674bc..07b1317936a 100644 --- a/database_cleanup/i18n/sv.po +++ b/database_cleanup/i18n/sv.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/th.po b/database_cleanup/i18n/th.po index 9ca4039fc0f..fca03bbb0b9 100644 --- a/database_cleanup/i18n/th.po +++ b/database_cleanup/i18n/th.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/tr.po b/database_cleanup/i18n/tr.po index 7bbd98b7c19..653f9df2806 100644 --- a/database_cleanup/i18n/tr.po +++ b/database_cleanup/i18n/tr.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "Bu modülü sil" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/tr_TR.po b/database_cleanup/i18n/tr_TR.po index 61897f429bf..67a6ab6403c 100644 --- a/database_cleanup/i18n/tr_TR.po +++ b/database_cleanup/i18n/tr_TR.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/uk.po b/database_cleanup/i18n/uk.po index c7809249c86..e49afba0764 100644 --- a/database_cleanup/i18n/uk.po +++ b/database_cleanup/i18n/uk.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/vi.po b/database_cleanup/i18n/vi.po index 308d461c00b..b16fbea7e5a 100644 --- a/database_cleanup/i18n/vi.po +++ b/database_cleanup/i18n/vi.po @@ -469,10 +469,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/vi_VN.po b/database_cleanup/i18n/vi_VN.po index 4874ab6fcf7..068e45ffd2b 100644 --- a/database_cleanup/i18n/vi_VN.po +++ b/database_cleanup/i18n/vi_VN.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/zh_CN.po b/database_cleanup/i18n/zh_CN.po index ac9bc8c9da5..2748450a3cd 100644 --- a/database_cleanup/i18n/zh_CN.po +++ b/database_cleanup/i18n/zh_CN.po @@ -471,10 +471,40 @@ msgid "Purge tables" msgstr "清除表" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "清除此模型" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged diff --git a/database_cleanup/i18n/zh_TW.po b/database_cleanup/i18n/zh_TW.po index 0a5e92f9419..4182b48d8b9 100644 --- a/database_cleanup/i18n/zh_TW.po +++ b/database_cleanup/i18n/zh_TW.po @@ -470,10 +470,40 @@ msgid "Purge tables" msgstr "" #. module: database_cleanup -#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_column_line_tree +msgid "Purge this column" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_field_line_tree +msgid "Purge this field" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_menu_line_tree +msgid "Purge this menu" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_model_line_tree msgid "Purge this model" msgstr "" +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_module_line_tree +msgid "Purge this module" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.tree_purge_line +msgid "Purge this record" +msgstr "" + +#. module: database_cleanup +#: model_terms:ir.ui.view,arch_db:database_cleanup.purge_table_line_tree +msgid "Purge this table" +msgstr "" + #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__purged #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__purged From 9a5810d7694cd3603368b295f08183377ca3518d Mon Sep 17 00:00:00 2001 From: Ruchir Shukla Date: Tue, 9 Dec 2025 10:47:56 +0530 Subject: [PATCH 116/770] [IMP] database_cleanup: pre-commit auto fixes --- database_cleanup/models/create_indexes.py | 3 +-- database_cleanup/models/purge_models.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/database_cleanup/models/create_indexes.py b/database_cleanup/models/create_indexes.py index cee600d3d20..af642975709 100644 --- a/database_cleanup/models/create_indexes.py +++ b/database_cleanup/models/create_indexes.py @@ -58,8 +58,7 @@ def find(self): model = self.env[field.model] name = f"{model._table}__{field.name}_index" self.env.cr.execute( - "select indexname from pg_indexes " - "where indexname=%s and tablename=%s", + "select indexname from pg_indexes where indexname=%s and tablename=%s", (name, model._table), ) if self.env.cr.rowcount: diff --git a/database_cleanup/models/purge_models.py b/database_cleanup/models/purge_models.py index a5da9ee910b..60df4781f2b 100644 --- a/database_cleanup/models/purge_models.py +++ b/database_cleanup/models/purge_models.py @@ -73,7 +73,7 @@ def purge(self): ) if attachments: self.env.cr.execute( - "UPDATE ir_attachment SET res_model = NULL " "WHERE id in %s", + "UPDATE ir_attachment SET res_model = NULL WHERE id in %s", (tuple(attachments.ids),), ) self.env["ir.model.constraint"].search( From 8a176f5b8cb70a8b5211aeae6a0ba55b2f8f04c5 Mon Sep 17 00:00:00 2001 From: Ruchir Shukla Date: Tue, 9 Dec 2025 20:17:30 +0530 Subject: [PATCH 117/770] [MIG] database_cleanup: Migration to 19.0 --- database_cleanup/README.rst | 10 +- database_cleanup/__init__.py | 1 + database_cleanup/__manifest__.py | 2 +- database_cleanup/models/__init__.py | 6 - database_cleanup/models/purge_models.py | 104 +---------------- database_cleanup/models/purge_modules.py | 58 +--------- database_cleanup/models/purge_wizard.py | 8 +- .../static/description/index.html | 6 +- database_cleanup/tests/common.py | 9 +- database_cleanup/tests/test_create_indexes.py | 5 +- .../tests/test_identifier_adapter.py | 5 + database_cleanup/tests/test_purge_columns.py | 16 ++- database_cleanup/tests/test_purge_data.py | 5 +- database_cleanup/tests/test_purge_fields.py | 26 ++--- database_cleanup/tests/test_purge_menus.py | 7 +- database_cleanup/tests/test_purge_models.py | 17 +-- database_cleanup/tests/test_purge_modules.py | 34 +++++- database_cleanup/views/menu.xml | 2 +- database_cleanup/wizards/__init__.py | 8 ++ .../{models => wizards}/create_indexes.py | 0 .../{models => wizards}/purge_columns.py | 8 +- .../{models => wizards}/purge_data.py | 4 +- .../{models => wizards}/purge_fields.py | 4 +- .../{models => wizards}/purge_menus.py | 4 +- database_cleanup/wizards/purge_models.py | 107 ++++++++++++++++++ database_cleanup/wizards/purge_modules.py | 61 ++++++++++ .../{models => wizards}/purge_tables.py | 7 +- 27 files changed, 293 insertions(+), 231 deletions(-) create mode 100644 database_cleanup/wizards/__init__.py rename database_cleanup/{models => wizards}/create_indexes.py (100%) rename database_cleanup/{models => wizards}/purge_columns.py (94%) rename database_cleanup/{models => wizards}/purge_data.py (96%) rename database_cleanup/{models => wizards}/purge_fields.py (97%) rename database_cleanup/{models => wizards}/purge_menus.py (95%) create mode 100644 database_cleanup/wizards/purge_models.py create mode 100644 database_cleanup/wizards/purge_modules.py rename database_cleanup/{models => wizards}/purge_tables.py (95%) diff --git a/database_cleanup/README.rst b/database_cleanup/README.rst index 5886eeaf47a..3df10b81943 100644 --- a/database_cleanup/README.rst +++ b/database_cleanup/README.rst @@ -21,13 +21,13 @@ Database cleanup :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--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/18.0/database_cleanup + :target: https://github.com/OCA/server-tools/tree/19.0/database_cleanup :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-database_cleanup + :target: https://translation.odoo-community.org/projects/server-tools-19-0/server-tools-19-0-database_cleanup :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-tools&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=19.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -69,7 +69,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -104,6 +104,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/database_cleanup/__init__.py b/database_cleanup/__init__.py index b19ab1b0961..eb8ffa9afeb 100644 --- a/database_cleanup/__init__.py +++ b/database_cleanup/__init__.py @@ -1,3 +1,4 @@ # Copyright 2014-2016 Therp BV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import models +from . import wizards diff --git a/database_cleanup/__manifest__.py b/database_cleanup/__manifest__.py index c4726e6443f..e39a02238a3 100644 --- a/database_cleanup/__manifest__.py +++ b/database_cleanup/__manifest__.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Database cleanup", - "version": "18.0.1.0.2", + "version": "19.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-tools", "depends": ["base"], diff --git a/database_cleanup/models/__init__.py b/database_cleanup/models/__init__.py index 4052f59006a..a44e22b66f3 100644 --- a/database_cleanup/models/__init__.py +++ b/database_cleanup/models/__init__.py @@ -1,9 +1,3 @@ from . import purge_wizard from . import purge_modules from . import purge_models -from . import purge_fields -from . import purge_columns -from . import purge_tables -from . import purge_data -from . import purge_menus -from . import create_indexes diff --git a/database_cleanup/models/purge_models.py b/database_cleanup/models/purge_models.py index 60df4781f2b..928b0dc688d 100644 --- a/database_cleanup/models/purge_models.py +++ b/database_cleanup/models/purge_models.py @@ -2,14 +2,7 @@ # Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -import logging - -from odoo import _, api, fields, models -from odoo.exceptions import UserError - -from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG - -_logger = logging.getLogger(__name__) +from odoo import api, models class IrModel(models.Model): @@ -34,98 +27,3 @@ def _prepare_update(self): """this function crashes for undefined models""" self = self.filtered(lambda x: x.model in self.env) return super()._prepare_update() - - -class CleanupPurgeLineModel(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.model" - _description = "Cleanup Purge Line Model" - - wizard_id = fields.Many2one( - "cleanup.purge.wizard.model", "Purge Wizard", readonly=True - ) - - def purge(self): - """ - Unlink models upon manual confirmation. - """ - context_flags = { - MODULE_UNINSTALL_FLAG: True, - "purge": True, - } - - if self: - objs = self - else: - objs = self.env["cleanup.purge.line.model"].browse( - self._context.get("active_ids") - ) - for line in objs: - self.env.cr.execute( - "SELECT id, model from ir_model WHERE model = %s", (line.name,) - ) - row = self.env.cr.fetchone() - if not row: - continue - self.logger.info("Purging model %s", row[1]) - attachments = self.env["ir.attachment"].search( - [("res_model", "=", line.name)] - ) - if attachments: - self.env.cr.execute( - "UPDATE ir_attachment SET res_model = NULL WHERE id in %s", - (tuple(attachments.ids),), - ) - self.env["ir.model.constraint"].search( - [ - ("model", "=", line.name), - ] - ).unlink() - relations = ( - self.env["ir.model.fields"] - .search( - [ - ("relation", "=", row[1]), - ] - ) - .with_context(**context_flags) - ) - for relation in relations: - try: - # Fails if the model on the target side - # cannot be instantiated - relation.unlink() - except KeyError: - _logger.error("") - except AttributeError: - _logger.error("") - self.env["ir.model.relation"].search( - [("model", "=", line.name)] - ).with_context(**context_flags).unlink() - self.env["ir.model"].browse([row[0]]).with_context(**context_flags).unlink() - line.write({"purged": True}) - return True - - -class CleanupPurgeWizardModel(models.TransientModel): - _inherit = "cleanup.purge.wizard" - _name = "cleanup.purge.wizard.model" - _description = "Purge models" - - @api.model - def find(self): - """ - Search for models that cannot be instantiated. - """ - res = [] - self.env.cr.execute("SELECT model from ir_model") - for (model,) in self.env.cr.fetchall(): - if model not in self.env: - res.append((0, 0, {"name": model})) - if not res: - raise UserError(_("No orphaned models found")) - return res - - purge_line_ids = fields.One2many( - "cleanup.purge.line.model", "wizard_id", "Models to purge" - ) diff --git a/database_cleanup/models/purge_modules.py b/database_cleanup/models/purge_modules.py index 255a3f9c99f..fa45cc741f9 100644 --- a/database_cleanup/models/purge_modules.py +++ b/database_cleanup/models/purge_modules.py @@ -2,9 +2,7 @@ # Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -from odoo import _, api, fields, models -from odoo.exceptions import UserError -from odoo.modules.module import get_module_path +from odoo import api, models from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG @@ -29,57 +27,3 @@ def _module_data_uninstall(self, modules_to_remove): if this.model not in self.env: this.unlink() return super()._module_data_uninstall(modules_to_remove) - - -class CleanupPurgeLineModule(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.module" - _description = "Cleanup Purge Line Module" - - wizard_id = fields.Many2one( - "cleanup.purge.wizard.module", "Purge Wizard", readonly=True - ) - - def purge(self): - """ - Uninstall modules upon manual confirmation, then reload - the database. - """ - module_names = self.filtered(lambda x: not x.purged).mapped("name") - modules = self.env["ir.module.module"].search([("name", "in", module_names)]) - if not modules: - return True - self.logger.info("Purging modules %s", ", ".join(module_names)) - installed = modules.filtered(lambda x: x.state in ("installed", "to upgrade")) - to_remove = modules - installed - to_remove += to_remove.downstream_dependencies() - to_remove.write({"state": "to remove"}) - installed.button_immediate_uninstall() - with self.env.registry.cursor() as new_cr: - self.env(cr=new_cr)["ir.module.module"].browse(modules.ids).unlink() - return self.write({"purged": True}) - - -class CleanupPurgeWizardModule(models.TransientModel): - _inherit = "cleanup.purge.wizard" - _name = "cleanup.purge.wizard.module" - _description = "Purge modules" - - @api.model - def find(self): - res = [] - IrModule = self.env["ir.module.module"] - for module in IrModule.search( - [("to_buy", "=", False), ("name", "!=", "studio_customization")] - ): - if get_module_path(module.name, display_warning=False): - continue - res.append((0, 0, {"name": module.name})) - - if not res: - raise UserError(_("No modules found to purge")) - return res - - purge_line_ids = fields.One2many( - "cleanup.purge.line.module", "wizard_id", "Modules to purge" - ) diff --git a/database_cleanup/models/purge_wizard.py b/database_cleanup/models/purge_wizard.py index e14c2d16fe4..51d3ab96740 100644 --- a/database_cleanup/models/purge_wizard.py +++ b/database_cleanup/models/purge_wizard.py @@ -5,7 +5,7 @@ import logging -from odoo import _, api, fields, models +from odoo import api, fields, models from odoo.exceptions import AccessDenied @@ -28,7 +28,7 @@ def purge(self): @api.model_create_multi def create(self, values): # make sure the user trying this is actually supposed to do it - if self.env.ref("base.group_erp_manager") not in self.env.user.groups_id: + if self.env.ref("base.group_erp_manager") not in self.env.user.group_ids: raise AccessDenied return super().create(values) @@ -71,7 +71,7 @@ def get_wizard_action(self): def select_lines(self): return { "type": "ir.actions.act_window", - "name": _("Select lines to purge"), + "name": self.env._("Select lines to purge"), "views": [(False, "list"), (False, "form")], "res_model": self._fields["purge_line_ids"].comodel_name, "domain": [("wizard_id", "in", self.ids)], @@ -84,7 +84,7 @@ def _compute_display_name(self): @api.model_create_multi def create(self, values): # make sure the user trying this is actually supposed to do it - if self.env.ref("base.group_erp_manager") not in self.env.user.groups_id: + if self.env.ref("base.group_erp_manager") not in self.env.user.group_ids: raise AccessDenied return super().create(values) diff --git a/database_cleanup/static/description/index.html b/database_cleanup/static/description/index.html index 58ce7e47f42..18d6ea8648c 100644 --- a/database_cleanup/static/description/index.html +++ b/database_cleanup/static/description/index.html @@ -374,7 +374,7 @@

    Database cleanup

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:1bc48f79f4a199586b709106c87ff617584036cc2158f711b91e0335489dcc88 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

    +

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

    Clean your Odoo database from remnants of modules, models, columns and tables left by uninstalled modules (prior to 7.0) or a homebrew database upgrade to a new major version of Odoo.

    @@ -412,7 +412,7 @@

    Bug Tracker

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

    +feedback.

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

    @@ -444,7 +444,7 @@

    Maintainers

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

    -

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

    +

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

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

    diff --git a/database_cleanup/tests/common.py b/database_cleanup/tests/common.py index 6ab78a32482..095ccee9fcc 100644 --- a/database_cleanup/tests/common.py +++ b/database_cleanup/tests/common.py @@ -17,11 +17,14 @@ def environment(): """ registry = odoo.modules.registry.Registry(common.get_db_name()) with registry.cursor() as cr: - yield odoo.api.Environment(cr, ADMIN_USER_ID, {}) + env = odoo.api.Environment(cr, ADMIN_USER_ID, {}) + env.user.group_ids |= env.ref("base.group_erp_manager") + yield env # Use post_install to get all models loaded more info: odoo/odoo#13458 @tagged("post_install", "-at_install") class Common(BaseCase): - def setUp(self): - super().setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() diff --git a/database_cleanup/tests/test_create_indexes.py b/database_cleanup/tests/test_create_indexes.py index eed4e5334bb..bd28c78d6b4 100644 --- a/database_cleanup/tests/test_create_indexes.py +++ b/database_cleanup/tests/test_create_indexes.py @@ -9,8 +9,9 @@ # Use post_install to get all models loaded more info: odoo/odoo#13458 @tagged("post_install", "-at_install") class TestCreateIndexesLine(Common): - def setUp(self): - super().setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() with environment() as env: # delete some index and check if our module recreated it env.cr.execute("drop index res_partner__name_index") diff --git a/database_cleanup/tests/test_identifier_adapter.py b/database_cleanup/tests/test_identifier_adapter.py index bc1a972febc..dc024bf0f7f 100644 --- a/database_cleanup/tests/test_identifier_adapter.py +++ b/database_cleanup/tests/test_identifier_adapter.py @@ -4,6 +4,11 @@ class TestIdentifierAdapter(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + def test_column_name_with_spaces(self): """Spaces in column names are preserved except in unquoted identifiers.""" self.assertEqual( diff --git a/database_cleanup/tests/test_purge_columns.py b/database_cleanup/tests/test_purge_columns.py index d8ddbada96c..075fbdc128b 100644 --- a/database_cleanup/tests/test_purge_columns.py +++ b/database_cleanup/tests/test_purge_columns.py @@ -11,8 +11,9 @@ # Use post_install to get all models loaded more info: odoo/odoo#13458 @tagged("post_install", "-at_install") class TestCleanupPurgeLineColumn(Common): - def setUp(self): - super().setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() with environment() as env: # create an orphaned column env.cr.execute( @@ -25,7 +26,16 @@ def test_empty_column(self): partner_model = env["ir.model"].search( [("model", "=", "res.partner")], limit=1 ) - wizard = env["cleanup.purge.wizard.column"].create( + wizard = env["cleanup.purge.wizard.column"].create({}) + result = wizard.find() + self.assertTrue(result, "find() should return at least one orphaned column") + found_column_names = [line[2]["name"] for line in result] + self.assertIn( + "database_cleanup_test", + found_column_names, + "The test orphaned column should be found by find()", + ) + wizard.write( { "purge_line_ids": [ ( diff --git a/database_cleanup/tests/test_purge_data.py b/database_cleanup/tests/test_purge_data.py index 4006568f4d3..c000129d296 100644 --- a/database_cleanup/tests/test_purge_data.py +++ b/database_cleanup/tests/test_purge_data.py @@ -9,8 +9,9 @@ # Use post_install to get all models loaded more info: odoo/odoo#13458 @tagged("post_install", "-at_install") class TestCleanupPurgeLineData(Common): - def setUp(self): - super().setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() with environment() as env: # create a data entry pointing nowhere env.cr.execute("select max(id) + 1 from res_users") diff --git a/database_cleanup/tests/test_purge_fields.py b/database_cleanup/tests/test_purge_fields.py index efb33716938..9db7d5bd45a 100644 --- a/database_cleanup/tests/test_purge_fields.py +++ b/database_cleanup/tests/test_purge_fields.py @@ -11,37 +11,37 @@ @tagged("post_install", "-at_install") class TestCleanupPurgeFields(Common): @classmethod - def setUpClass(self): + def setUpClass(cls): super().setUpClass() with environment() as env: # create a nonexistent model - self.model_name = "x_database.cleanup.test.field.model" - self.model_values = { + cls.model_name = "x_database.cleanup.test.field.model" + cls.model_values = { "name": "Database cleanup test field-model", - "model": self.model_name, + "model": cls.model_name, } - self.model = env["ir.model"].create(self.model_values) + cls.model = env["ir.model"].create(cls.model_values) env.cr.execute( "insert into ir_attachment (name, res_model, res_id, type) values " "('test attachment', %s, 42, 'binary')", - [self.model_name], + [cls.model_name], ) # create a nonexistent field - self.field_name = "x_database_cleanup_test_field" - self.field_values = { - "name": self.field_name, - "model_id": self.model.id, + cls.field_name = "x_database_cleanup_test_field" + cls.field_values = { + "name": cls.field_name, + "model_id": cls.model.id, "field_description": "Database cleanup test field", "ttype": "boolean", } - self.field = env["ir.model.fields"].create(self.field_values) + cls.field = env["ir.model.fields"].create(cls.field_values) env.cr.execute( "update ir_model_fields set state = 'base' where id = %s ", - [self.field.id], + [cls.field.id], ) - env.registry.models[self.model_name]._fields.pop(self.field_name) + env.registry.models[cls.model_name]._fields__.pop(cls.field_name) def test_empty_field(self): with environment() as env: diff --git a/database_cleanup/tests/test_purge_menus.py b/database_cleanup/tests/test_purge_menus.py index 81aaf158de3..3632952ee58 100644 --- a/database_cleanup/tests/test_purge_menus.py +++ b/database_cleanup/tests/test_purge_menus.py @@ -9,11 +9,12 @@ # Use post_install to get all models loaded more info: odoo/odoo#13458 @tagged("post_install", "-at_install") class TestCleanupPurgeLineMenu(Common): - def setUp(self): - super().setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() with environment() as env: # create a new empty menu - self.menu = env["ir.ui.menu"].create({"name": "database_cleanup_test"}) + cls.menu = env["ir.ui.menu"].create({"name": "database_cleanup_test"}) def test_empty_menu(self): with environment() as env: diff --git a/database_cleanup/tests/test_purge_models.py b/database_cleanup/tests/test_purge_models.py index b37fccd9864..d4fe4b251c0 100644 --- a/database_cleanup/tests/test_purge_models.py +++ b/database_cleanup/tests/test_purge_models.py @@ -9,22 +9,23 @@ # Use post_install to get all models loaded more info: odoo/odoo#13458 @tagged("post_install", "-at_install") class TestCleanupPurgeLineColumn(Common): - def setUp(self): - super().setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() with environment() as env: # create a nonexistent model - self.model_name = "x_database.cleanup.test.model" - self.model_values = { + cls.model_name = "x_database.cleanup.test.model" + cls.model_values = { "name": "Database cleanup test model", - "model": self.model_name, + "model": cls.model_name, } - self.model = env["ir.model"].create(self.model_values) + cls.model = env["ir.model"].create(cls.model_values) env.cr.execute( "insert into ir_attachment (name, res_model, res_id, type) values " "('test attachment', %s, 42, 'binary')", - [self.model_name], + [cls.model_name], ) - env.registry.models.pop(self.model_name) + env.registry.models.pop(cls.model_name) def test_empty_model(self): with environment() as env: diff --git a/database_cleanup/tests/test_purge_modules.py b/database_cleanup/tests/test_purge_modules.py index 2b05b1972fe..98766e25716 100644 --- a/database_cleanup/tests/test_purge_modules.py +++ b/database_cleanup/tests/test_purge_modules.py @@ -10,17 +10,27 @@ @tagged("post_install", "-at_install") class TestCleanupPurgeLineModule(Common): @classmethod - def setUpClass(self): + def setUpClass(cls): super().setUpClass() - self.model_name = "database_cleanup_test" + cls.model_name = "database_cleanup_test" with environment() as env: # create a nonexistent module - self.module = env["ir.module.module"].create( + cls.module = env["ir.module.module"].create( { - "name": self.model_name, + "name": cls.model_name, "state": "to upgrade", } ) + # create an ir.model.data pointing to a non-existent field + cls.orphan_field_data = env["ir.model.data"].create( + { + "name": "x_orphan_field", + "module": cls.model_name, + "model": "ir.model.fields", + "res_id": 999999, # nonexistent record + "noupdate": True, + } + ) def test_remove_to_upgrade_module(self): with environment() as env: @@ -30,6 +40,22 @@ def test_remove_to_upgrade_module(self): ).mapped("name") self.assertTrue(self.model_name in module_names) + def test_module_data_uninstall_removes_orphans(self): + with environment() as env: + IrModelData = env["ir.model.data"] + + self.assertTrue( + IrModelData.browse(self.orphan_field_data.id).exists(), + "orphan field data should exist before uninstall", + ) + + IrModelData._module_data_uninstall([self.model_name]) + + self.assertFalse( + IrModelData.browse(self.orphan_field_data.id).exists(), + "orphan field data should be removed after uninstall", + ) + @classmethod def tearDownClass(self): super().tearDownClass() diff --git a/database_cleanup/views/menu.xml b/database_cleanup/views/menu.xml index 9618986c738..67e15686b7b 100644 --- a/database_cleanup/views/menu.xml +++ b/database_cleanup/views/menu.xml @@ -5,7 +5,7 @@ - + diff --git a/database_cleanup/wizards/__init__.py b/database_cleanup/wizards/__init__.py new file mode 100644 index 00000000000..5e000bf3d5e --- /dev/null +++ b/database_cleanup/wizards/__init__.py @@ -0,0 +1,8 @@ +from . import create_indexes +from . import purge_columns +from . import purge_data +from . import purge_tables +from . import purge_menus +from . import purge_modules +from . import purge_models +from . import purge_fields diff --git a/database_cleanup/models/create_indexes.py b/database_cleanup/wizards/create_indexes.py similarity index 100% rename from database_cleanup/models/create_indexes.py rename to database_cleanup/wizards/create_indexes.py diff --git a/database_cleanup/models/purge_columns.py b/database_cleanup/wizards/purge_columns.py similarity index 94% rename from database_cleanup/models/purge_columns.py rename to database_cleanup/wizards/purge_columns.py index e9b96639340..a4ca7c1b866 100644 --- a/database_cleanup/models/purge_columns.py +++ b/database_cleanup/wizards/purge_columns.py @@ -2,7 +2,7 @@ # Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -from odoo import _, api, fields, models +from odoo import api, fields, models from odoo.exceptions import UserError from ..identifier_adapter import IdentifierAdapter @@ -113,8 +113,8 @@ def find(self): # mapping of tables to tuples (model id, [pool1, pool2, ...]) table2model = {} - - for model in self.env["ir.model"].search([]): + models_in_registry = list(self.env.registry.models.keys()) + for model in self.env["ir.model"].search([("model", "in", models_in_registry)]): if model.model not in self.env: continue model_pool = self.env[model.model] @@ -128,7 +128,7 @@ def find(self): for column in self.get_orphaned_columns(model_spec[1]): res.append((0, 0, {"name": column, "model_id": model_spec[0]})) if not res: - raise UserError(_("No orphaned columns found")) + raise UserError(self.env._("No orphaned columns found")) return res purge_line_ids = fields.One2many( diff --git a/database_cleanup/models/purge_data.py b/database_cleanup/wizards/purge_data.py similarity index 96% rename from database_cleanup/models/purge_data.py rename to database_cleanup/wizards/purge_data.py index c7321e97b05..da17ae111a6 100644 --- a/database_cleanup/models/purge_data.py +++ b/database_cleanup/wizards/purge_data.py @@ -1,7 +1,7 @@ # Copyright 2014-2016 Therp BV # Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import _, api, fields, models +from odoo import api, fields, models from odoo.exceptions import UserError from ..identifier_adapter import IdentifierAdapter @@ -84,7 +84,7 @@ def find(self): ) ) if not res: - raise UserError(_("No orphaned data entries found")) + raise UserError(self.env._("No orphaned data entries found")) return res purge_line_ids = fields.One2many( diff --git a/database_cleanup/models/purge_fields.py b/database_cleanup/wizards/purge_fields.py similarity index 97% rename from database_cleanup/models/purge_fields.py rename to database_cleanup/wizards/purge_fields.py index cee343b79ff..a62e2c3f762 100644 --- a/database_cleanup/models/purge_fields.py +++ b/database_cleanup/wizards/purge_fields.py @@ -1,7 +1,7 @@ # Copyright 2014-2016 Therp BV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -from odoo import _, api, fields, models +from odoo import api, fields, models from odoo.exceptions import UserError from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG @@ -122,7 +122,7 @@ def find(self): ) ) if not res: - raise UserError(_("No orphaned fields found")) + raise UserError(self.env._("No orphaned fields found")) return res purge_line_ids = fields.One2many( diff --git a/database_cleanup/models/purge_menus.py b/database_cleanup/wizards/purge_menus.py similarity index 95% rename from database_cleanup/models/purge_menus.py rename to database_cleanup/wizards/purge_menus.py index f73d918de9b..de29b8f5e9d 100644 --- a/database_cleanup/models/purge_menus.py +++ b/database_cleanup/wizards/purge_menus.py @@ -2,7 +2,7 @@ # Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -from odoo import _, api, fields, models +from odoo import api, fields, models from odoo.exceptions import UserError @@ -60,7 +60,7 @@ def find(self): ) ) if not res: - raise UserError(_("No dangling menu entries found")) + raise UserError(self.env._("No dangling menu entries found")) return res purge_line_ids = fields.One2many( diff --git a/database_cleanup/wizards/purge_models.py b/database_cleanup/wizards/purge_models.py new file mode 100644 index 00000000000..e6f2a673dad --- /dev/null +++ b/database_cleanup/wizards/purge_models.py @@ -0,0 +1,107 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +import logging + +from odoo import api, fields, models +from odoo.exceptions import UserError + +from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG + +_logger = logging.getLogger(__name__) + + +class CleanupPurgeLineModel(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.model" + _description = "Cleanup Purge Line Model" + + wizard_id = fields.Many2one( + "cleanup.purge.wizard.model", "Purge Wizard", readonly=True + ) + + def purge(self): + """ + Unlink models upon manual confirmation. + """ + context_flags = { + MODULE_UNINSTALL_FLAG: True, + "purge": True, + } + + if self: + objs = self + else: + objs = self.env["cleanup.purge.line.model"].browse( + self._context.get("active_ids") + ) + for line in objs: + self.env.cr.execute( + "SELECT id, model from ir_model WHERE model = %s", (line.name,) + ) + row = self.env.cr.fetchone() + if not row: + continue + self.logger.info("Purging model %s", row[1]) + attachments = self.env["ir.attachment"].search( + [("res_model", "=", line.name)] + ) + if attachments: + self.env.cr.execute( + "UPDATE ir_attachment SET res_model = NULL WHERE id in %s", + (tuple(attachments.ids),), + ) + self.env["ir.model.constraint"].search( + [ + ("model", "=", line.name), + ] + ).unlink() + relations = ( + self.env["ir.model.fields"] + .search( + [ + ("relation", "=", row[1]), + ] + ) + .with_context(**context_flags) + ) + for relation in relations: + try: + # Fails if the model on the target side + # cannot be instantiated + relation.unlink() + except KeyError: + _logger.error("") + except AttributeError: + _logger.error("") + self.env["ir.model.relation"].search( + [("model", "=", line.name)] + ).with_context(**context_flags).unlink() + self.env["ir.model"].browse([row[0]]).with_context(**context_flags).unlink() + line.write({"purged": True}) + return True + + +class CleanupPurgeWizardModel(models.TransientModel): + _inherit = "cleanup.purge.wizard" + _name = "cleanup.purge.wizard.model" + _description = "Purge models" + + @api.model + def find(self): + """ + Search for models that cannot be instantiated. + """ + res = [] + self.env.cr.execute("SELECT model from ir_model") + for (model,) in self.env.cr.fetchall(): + if model not in self.env: + res.append((0, 0, {"name": model})) + if not res: + raise UserError(self.env._("No orphaned models found")) + return res + + purge_line_ids = fields.One2many( + "cleanup.purge.line.model", "wizard_id", "Models to purge" + ) diff --git a/database_cleanup/wizards/purge_modules.py b/database_cleanup/wizards/purge_modules.py new file mode 100644 index 00000000000..c1d9b5e81bf --- /dev/null +++ b/database_cleanup/wizards/purge_modules.py @@ -0,0 +1,61 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from odoo import api, fields, models +from odoo.exceptions import UserError +from odoo.modules.module import get_module_path + + +class CleanupPurgeLineModule(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.module" + _description = "Cleanup Purge Line Module" + + wizard_id = fields.Many2one( + "cleanup.purge.wizard.module", "Purge Wizard", readonly=True + ) + + def purge(self): + """ + Uninstall modules upon manual confirmation, then reload + the database. + """ + module_names = self.filtered(lambda x: not x.purged).mapped("name") + modules = self.env["ir.module.module"].search([("name", "in", module_names)]) + if not modules: + return True + self.logger.info("Purging modules %s", ", ".join(module_names)) + installed = modules.filtered(lambda x: x.state in ("installed", "to upgrade")) + to_remove = modules - installed + to_remove += to_remove.downstream_dependencies() + to_remove.write({"state": "to remove"}) + installed.button_immediate_uninstall() + with self.env.registry.cursor() as new_cr: + self.env(cr=new_cr)["ir.module.module"].browse(modules.ids).unlink() + return self.write({"purged": True}) + + +class CleanupPurgeWizardModule(models.TransientModel): + _inherit = "cleanup.purge.wizard" + _name = "cleanup.purge.wizard.module" + _description = "Purge modules" + + @api.model + def find(self): + res = [] + IrModule = self.env["ir.module.module"] + for module in IrModule.search( + [("to_buy", "=", False), ("name", "!=", "studio_customization")] + ): + if get_module_path(module.name, display_warning=False): + continue + res.append((0, 0, {"name": module.name})) + + if not res: + raise UserError(self.env._("No modules found to purge")) + return res + + purge_line_ids = fields.One2many( + "cleanup.purge.line.module", "wizard_id", "Modules to purge" + ) diff --git a/database_cleanup/models/purge_tables.py b/database_cleanup/wizards/purge_tables.py similarity index 95% rename from database_cleanup/models/purge_tables.py rename to database_cleanup/wizards/purge_tables.py index 1b5db6d3b7f..2ae713e08fc 100644 --- a/database_cleanup/models/purge_tables.py +++ b/database_cleanup/wizards/purge_tables.py @@ -4,7 +4,7 @@ # pylint: disable=consider-merging-classes-inherited from psycopg2.extensions import AsIs -from odoo import _, api, fields, models +from odoo import api, fields, models from odoo.exceptions import UserError from ..identifier_adapter import IdentifierAdapter @@ -106,7 +106,8 @@ def find(self): Search for tables and views that cannot be instantiated. """ known_tables = list(self.blacklist) - for model in self.env["ir.model"].search([]): + models_in_registry = list(self.env.registry.models.keys()) + for model in self.env["ir.model"].search([("model", "in", models_in_registry)]): if model.model not in self.env: continue model_pool = self.env[model.model] @@ -137,7 +138,7 @@ def find(self): for row in self.env.cr.fetchall() ] if not res: - raise UserError(_("No orphaned tables found")) + raise UserError(self.env._("No orphaned tables found")) return res purge_line_ids = fields.One2many( From 218d2af0637bb5bc492c359ecf5c09c9ddc3a8e1 Mon Sep 17 00:00:00 2001 From: Ruchir Shukla Date: Tue, 9 Dec 2025 20:18:23 +0530 Subject: [PATCH 118/770] [IMP] database_cleanup: Improved the file structure --- database_cleanup/__manifest__.py | 27 ++++-- database_cleanup/models/__init__.py | 6 +- .../models/{purge_models.py => ir_model.py} | 9 -- .../{purge_modules.py => ir_model_data.py} | 0 database_cleanup/models/ir_model_fields.py | 14 +++ database_cleanup/models/purge_line.py | 33 +++++++ database_cleanup/models/purge_wizard.py | 25 ------ database_cleanup/views/purge_line.xml | 19 ++++ database_cleanup/views/purge_wizard.xml | 16 ---- database_cleanup/wizards/__init__.py | 14 ++- database_cleanup/wizards/create_indexes.py | 31 ------- .../{views => wizards}/create_indexes.xml | 27 ------ .../wizards/create_indexes_line.py | 36 ++++++++ .../wizards/create_indexes_line.xml | 29 ++++++ database_cleanup/wizards/purge_columns.py | 53 ----------- database_cleanup/wizards/purge_columns.xml | 26 ++++++ database_cleanup/wizards/purge_data.py | 24 ----- database_cleanup/wizards/purge_data.xml | 23 +++++ database_cleanup/wizards/purge_fields.py | 86 ------------------ database_cleanup/wizards/purge_fields.xml | 24 +++++ .../wizards/purge_line_columns.py | 58 ++++++++++++ .../purge_line_columns.xml} | 24 ----- database_cleanup/wizards/purge_line_data.py | 28 ++++++ .../purge_line_data.xml} | 21 ----- database_cleanup/wizards/purge_line_fields.py | 90 +++++++++++++++++++ .../purge_line_fields.xml} | 22 ----- database_cleanup/wizards/purge_line_menus.py | 29 ++++++ .../purge_line_menus.xml} | 19 ---- database_cleanup/wizards/purge_line_models.py | 82 +++++++++++++++++ .../purge_line_models.xml} | 22 ----- .../wizards/purge_line_modules.py | 34 +++++++ .../purge_line_modules.xml} | 22 ----- database_cleanup/wizards/purge_line_tables.py | 90 +++++++++++++++++++ .../purge_line_tables.xml} | 22 ----- database_cleanup/wizards/purge_menus.py | 24 ----- database_cleanup/wizards/purge_menus.xml | 21 +++++ database_cleanup/wizards/purge_models.py | 77 ---------------- database_cleanup/wizards/purge_models.xml | 24 +++++ database_cleanup/wizards/purge_modules.py | 29 ------ database_cleanup/wizards/purge_modules.xml | 24 +++++ database_cleanup/wizards/purge_tables.py | 81 ----------------- database_cleanup/wizards/purge_tables.xml | 24 +++++ 42 files changed, 741 insertions(+), 648 deletions(-) rename database_cleanup/models/{purge_models.py => ir_model.py} (73%) rename database_cleanup/models/{purge_modules.py => ir_model_data.py} (100%) create mode 100644 database_cleanup/models/ir_model_fields.py create mode 100644 database_cleanup/models/purge_line.py create mode 100644 database_cleanup/views/purge_line.xml rename database_cleanup/{views => wizards}/create_indexes.xml (52%) create mode 100644 database_cleanup/wizards/create_indexes_line.py create mode 100644 database_cleanup/wizards/create_indexes_line.xml create mode 100644 database_cleanup/wizards/purge_columns.xml create mode 100644 database_cleanup/wizards/purge_data.xml create mode 100644 database_cleanup/wizards/purge_fields.xml create mode 100644 database_cleanup/wizards/purge_line_columns.py rename database_cleanup/{views/purge_columns.xml => wizards/purge_line_columns.xml} (55%) create mode 100644 database_cleanup/wizards/purge_line_data.py rename database_cleanup/{views/purge_data.xml => wizards/purge_line_data.xml} (51%) create mode 100644 database_cleanup/wizards/purge_line_fields.py rename database_cleanup/{views/purge_fields.xml => wizards/purge_line_fields.xml} (60%) create mode 100644 database_cleanup/wizards/purge_line_menus.py rename database_cleanup/{views/purge_menus.xml => wizards/purge_line_menus.xml} (56%) create mode 100644 database_cleanup/wizards/purge_line_models.py rename database_cleanup/{views/purge_models.xml => wizards/purge_line_models.xml} (55%) create mode 100644 database_cleanup/wizards/purge_line_modules.py rename database_cleanup/{views/purge_modules.xml => wizards/purge_line_modules.xml} (55%) create mode 100644 database_cleanup/wizards/purge_line_tables.py rename database_cleanup/{views/purge_tables.xml => wizards/purge_line_tables.xml} (58%) create mode 100644 database_cleanup/wizards/purge_menus.xml create mode 100644 database_cleanup/wizards/purge_models.xml create mode 100644 database_cleanup/wizards/purge_modules.xml create mode 100644 database_cleanup/wizards/purge_tables.xml diff --git a/database_cleanup/__manifest__.py b/database_cleanup/__manifest__.py index e39a02238a3..5efffaf5a38 100644 --- a/database_cleanup/__manifest__.py +++ b/database_cleanup/__manifest__.py @@ -10,17 +10,26 @@ "license": "AGPL-3", "category": "Tools", "data": [ + "security/ir.model.access.csv", "views/purge_wizard.xml", - "views/purge_menus.xml", - "views/purge_modules.xml", - "views/purge_models.xml", - "views/purge_fields.xml", - "views/purge_columns.xml", - "views/purge_tables.xml", - "views/purge_data.xml", - "views/create_indexes.xml", + "views/purge_line.xml", + "wizards/purge_menus.xml", + "wizards/purge_line_menus.xml", + "wizards/purge_modules.xml", + "wizards/purge_line_modules.xml", + "wizards/purge_models.xml", + "wizards/purge_line_models.xml", + "wizards/purge_fields.xml", + "wizards/purge_line_fields.xml", + "wizards/purge_columns.xml", + "wizards/purge_line_columns.xml", + "wizards/purge_tables.xml", + "wizards/purge_line_tables.xml", + "wizards/purge_data.xml", + "wizards/purge_line_data.xml", + "wizards/create_indexes.xml", + "wizards/create_indexes_line.xml", "views/menu.xml", - "security/ir.model.access.csv", ], "installable": True, } diff --git a/database_cleanup/models/__init__.py b/database_cleanup/models/__init__.py index a44e22b66f3..ba8d234814a 100644 --- a/database_cleanup/models/__init__.py +++ b/database_cleanup/models/__init__.py @@ -1,3 +1,5 @@ from . import purge_wizard -from . import purge_modules -from . import purge_models +from . import purge_line +from . import ir_model +from . import ir_model_fields +from . import ir_model_data diff --git a/database_cleanup/models/purge_models.py b/database_cleanup/models/ir_model.py similarity index 73% rename from database_cleanup/models/purge_models.py rename to database_cleanup/models/ir_model.py index 928b0dc688d..f1a37cc6634 100644 --- a/database_cleanup/models/purge_models.py +++ b/database_cleanup/models/ir_model.py @@ -18,12 +18,3 @@ def _inherited_models(self): """this function crashes for undefined models""" self = self.filtered(lambda x: x.model in self.env) return super()._inherited_models() - - -class IrModelFields(models.Model): - _inherit = "ir.model.fields" - - def _prepare_update(self): - """this function crashes for undefined models""" - self = self.filtered(lambda x: x.model in self.env) - return super()._prepare_update() diff --git a/database_cleanup/models/purge_modules.py b/database_cleanup/models/ir_model_data.py similarity index 100% rename from database_cleanup/models/purge_modules.py rename to database_cleanup/models/ir_model_data.py diff --git a/database_cleanup/models/ir_model_fields.py b/database_cleanup/models/ir_model_fields.py new file mode 100644 index 00000000000..abe4e503bd1 --- /dev/null +++ b/database_cleanup/models/ir_model_fields.py @@ -0,0 +1,14 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from odoo import models + + +class IrModelFields(models.Model): + _inherit = "ir.model.fields" + + def _prepare_update(self): + """this function crashes for undefined models""" + self = self.filtered(lambda x: x.model in self.env) + return super()._prepare_update() diff --git a/database_cleanup/models/purge_line.py b/database_cleanup/models/purge_line.py new file mode 100644 index 00000000000..c556a9878ea --- /dev/null +++ b/database_cleanup/models/purge_line.py @@ -0,0 +1,33 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited + +import logging + +from odoo import api, fields, models +from odoo.exceptions import AccessDenied + + +class CleanupPurgeLine(models.AbstractModel): + """Abstract base class for the purge wizard lines""" + + _name = "cleanup.purge.line" + _order = "name" + _description = "Purge Column Abstract Wizard" + + name = fields.Char(readonly=True) + purged = fields.Boolean(readonly=True) + wizard_id = fields.Many2one("cleanup.purge.wizard") + + logger = logging.getLogger("odoo.addons.database_cleanup") + + def purge(self): + raise NotImplementedError + + @api.model_create_multi + def create(self, values): + # make sure the user trying this is actually supposed to do it + if self.env.ref("base.group_erp_manager") not in self.env.user.group_ids: + raise AccessDenied + return super().create(values) diff --git a/database_cleanup/models/purge_wizard.py b/database_cleanup/models/purge_wizard.py index 51d3ab96740..ec90dff0bf1 100644 --- a/database_cleanup/models/purge_wizard.py +++ b/database_cleanup/models/purge_wizard.py @@ -3,36 +3,11 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -import logging from odoo import api, fields, models from odoo.exceptions import AccessDenied -class CleanupPurgeLine(models.AbstractModel): - """Abstract base class for the purge wizard lines""" - - _name = "cleanup.purge.line" - _order = "name" - _description = "Purge Column Abstract Wizard" - - name = fields.Char(readonly=True) - purged = fields.Boolean(readonly=True) - wizard_id = fields.Many2one("cleanup.purge.wizard") - - logger = logging.getLogger("odoo.addons.database_cleanup") - - def purge(self): - raise NotImplementedError - - @api.model_create_multi - def create(self, values): - # make sure the user trying this is actually supposed to do it - if self.env.ref("base.group_erp_manager") not in self.env.user.group_ids: - raise AccessDenied - return super().create(values) - - class PurgeWizard(models.AbstractModel): """Abstract base class for the purge wizards""" diff --git a/database_cleanup/views/purge_line.xml b/database_cleanup/views/purge_line.xml new file mode 100644 index 00000000000..3660b71d66d --- /dev/null +++ b/database_cleanup/views/purge_line.xml @@ -0,0 +1,19 @@ + + + + cleanup.purge.line + + + + + - - - - - Create - ir.actions.server - code - - records.purge() - - diff --git a/database_cleanup/wizards/create_indexes_line.py b/database_cleanup/wizards/create_indexes_line.py new file mode 100644 index 00000000000..99ed892bb19 --- /dev/null +++ b/database_cleanup/wizards/create_indexes_line.py @@ -0,0 +1,36 @@ +# Copyright 2017 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from odoo import fields, models + +from ..identifier_adapter import IdentifierAdapter + + +class CreateIndexesLine(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.create_indexes.line" + _description = "Cleanup Create Indexes line" + + purged = fields.Boolean("Created") + wizard_id = fields.Many2one("cleanup.create_indexes.wizard") + field_id = fields.Many2one("ir.model.fields", required=True) + + def purge(self): + for field in self.mapped("field_id"): + model = self.env[field.model] + name = f"{model._table}__{field.name}_index" + self.env.cr.execute( + "create index %s ON %s (%s)", + ( + IdentifierAdapter(name, quote=False), + IdentifierAdapter(model._table), + IdentifierAdapter(field.name), + ), + ) + self.env.cr.execute("analyze %s", (IdentifierAdapter(model._table),)) + self.write( + { + "purged": True, + } + ) diff --git a/database_cleanup/wizards/create_indexes_line.xml b/database_cleanup/wizards/create_indexes_line.xml new file mode 100644 index 00000000000..47a68e6ac87 --- /dev/null +++ b/database_cleanup/wizards/create_indexes_line.xml @@ -0,0 +1,29 @@ + + + + cleanup.create_indexes.line + + primary + + + + + + + Create + ir.actions.server + code + + records.purge() + + + diff --git a/database_cleanup/wizards/purge_columns.py b/database_cleanup/wizards/purge_columns.py index a4ca7c1b866..ae0d05951bb 100644 --- a/database_cleanup/wizards/purge_columns.py +++ b/database_cleanup/wizards/purge_columns.py @@ -5,59 +5,6 @@ from odoo import api, fields, models from odoo.exceptions import UserError -from ..identifier_adapter import IdentifierAdapter - - -class CleanupPurgeLineColumn(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.column" - _description = "Cleanup Purge Line Column" - - model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="CASCADE") - wizard_id = fields.Many2one( - "cleanup.purge.wizard.column", "Purge Wizard", readonly=True - ) - - def purge(self): - """ - Unlink columns upon manual confirmation. - """ - if self: - objs = self - else: - objs = self.env["cleanup.purge.line.column"].browse( - self._context.get("active_ids") - ) - for line in objs: - if line.purged: - continue - model_pool = self.env[line.model_id.model] - # Check whether the column actually still exists. - # Inheritance such as stock.picking.in from stock.picking - # can lead to double attempts at removal - self.env.cr.execute( - "SELECT count(attname) FROM pg_attribute " - "WHERE attrelid = " - "( SELECT oid FROM pg_class WHERE relname = %s ) " - "AND attname = %s", - (model_pool._table, line.name), - ) - if not self.env.cr.fetchone()[0]: - continue - - self.logger.info( - "Dropping column %s from table %s", line.name, model_pool._table - ) - self.env.cr.execute( - "ALTER TABLE %s DROP COLUMN %s", - (IdentifierAdapter(model_pool._table), IdentifierAdapter(line.name)), - ) - line.write({"purged": True}) - # we need this commit because the ORM will deadlock if - # we still have a pending transaction - self.env.cr.commit() # pylint: disable=invalid-commit - return True - class CleanupPurgeWizardColumn(models.TransientModel): _inherit = "cleanup.purge.wizard" diff --git a/database_cleanup/wizards/purge_columns.xml b/database_cleanup/wizards/purge_columns.xml new file mode 100644 index 00000000000..2d3d22b8b0c --- /dev/null +++ b/database_cleanup/wizards/purge_columns.xml @@ -0,0 +1,26 @@ + + + + cleanup.purge.wizard.column + + primary + + + + + + + + + Purge columns + ir.actions.server + code + + + action = env.get('cleanup.purge.wizard.column').get_wizard_action() + + + diff --git a/database_cleanup/wizards/purge_data.py b/database_cleanup/wizards/purge_data.py index da17ae111a6..146e316051e 100644 --- a/database_cleanup/wizards/purge_data.py +++ b/database_cleanup/wizards/purge_data.py @@ -7,30 +7,6 @@ from ..identifier_adapter import IdentifierAdapter -class CleanupPurgeLineData(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.data" - _description = "Cleanup Purge Line Data" - - data_id = fields.Many2one("ir.model.data", "Data entry") - wizard_id = fields.Many2one( - "cleanup.purge.wizard.data", "Purge Wizard", readonly=True - ) - - def purge(self): - """Unlink data entries upon manual confirmation.""" - if self: - objs = self - else: - objs = self.env["cleanup.purge.line.data"].browse( - self._context.get("active_ids") - ) - to_unlink = objs.filtered(lambda x: not x.purged and x.data_id) - self.logger.info("Purging data entries: %s", to_unlink.mapped("name")) - to_unlink.mapped("data_id").unlink() - return to_unlink.write({"purged": True}) - - class CleanupPurgeWizardData(models.TransientModel): _inherit = "cleanup.purge.wizard" _name = "cleanup.purge.wizard.data" diff --git a/database_cleanup/wizards/purge_data.xml b/database_cleanup/wizards/purge_data.xml new file mode 100644 index 00000000000..c7309deb4c6 --- /dev/null +++ b/database_cleanup/wizards/purge_data.xml @@ -0,0 +1,23 @@ + + + + cleanup.purge.wizard.data + + primary + + + + + + + + + Purge data entries that refer to missing resources + ir.actions.server + code + + + action = env.get('cleanup.purge.wizard.data').get_wizard_action() + + + diff --git a/database_cleanup/wizards/purge_fields.py b/database_cleanup/wizards/purge_fields.py index a62e2c3f762..28bfdde3039 100644 --- a/database_cleanup/wizards/purge_fields.py +++ b/database_cleanup/wizards/purge_fields.py @@ -4,92 +4,6 @@ from odoo import api, fields, models from odoo.exceptions import UserError -from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG - -from ..identifier_adapter import IdentifierAdapter - - -class CleanupPurgeLineField(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.field" - _description = "Purge fields" - - wizard_id = fields.Many2one( - "cleanup.purge.wizard.field", "Purge Wizard", readonly=True - ) - field_id = fields.Many2one( - comodel_name="ir.model.fields", - string="Field", - ) - model_id = fields.Many2one( - comodel_name="ir.model", - related="field_id.model_id", - string="Model", - store=True, - ) - model_name = fields.Char( - related="model_id.model", - string="Model Technical Name", - store=True, - ) - - def purge(self): - """ - Unlink fields upon manual confirmation. - """ - context_flags = { - MODULE_UNINSTALL_FLAG: True, - "purge": True, - } - if self: - objs = self - else: - objs = self.env["cleanup.purge.line.action"].browse( - self._context.get("active_ids") - ) - to_unlink = objs.filtered(lambda x: not x.purged and x.field_id) - self.logger.info("Purging field entries:") - for rec in to_unlink: - self.logger.info(" - %s.%s", rec.model_name, rec.field_id.name) - field_id = rec.with_context(**context_flags).field_id - model = self.env[rec.model_name] - table_name = model._table - column_name = field_id.name - force_drop = False - # FIX: on unlink, odoo will not DROP the SQL column even if exists if the - # store attribute is set to False. - if not field_id.store and model._auto: - force_drop = True - # Odoo will internally drop the SQL column - field_id.unlink() - if force_drop: - self._drop_column(table_name, column_name) - rec.purged = True - return True - - def _drop_column(self, table, column): - # Use code from `purge_columns.py::purge()` - # Check whether the column actually still exists. - # Inheritance such as stock.picking.in from stock.picking - # can lead to double attempts at removal - self.env.cr.execute( - "SELECT count(attname) FROM pg_attribute " - "WHERE attrelid = " - "( SELECT oid FROM pg_class WHERE relname = %s ) " - "AND attname = %s", - (table, column), - ) - if not self.env.cr.fetchone()[0]: - return - self.logger.info("Dropping column %s from table %s", column, table) - self.env.cr.execute( - "ALTER TABLE %s DROP COLUMN %s", - (IdentifierAdapter(table), IdentifierAdapter(column)), - ) - # we need this commit because the ORM will deadlock if - # we still have a pending transaction - self.env.cr.commit() # pylint: disable=invalid-commit - class CleanupPurgeWizardField(models.TransientModel): _inherit = "cleanup.purge.wizard" diff --git a/database_cleanup/wizards/purge_fields.xml b/database_cleanup/wizards/purge_fields.xml new file mode 100644 index 00000000000..43d4b74bd9c --- /dev/null +++ b/database_cleanup/wizards/purge_fields.xml @@ -0,0 +1,24 @@ + + + + cleanup.purge.wizard.field + + primary + + + + + + + Purge models + ir.actions.server + code + + + action = env.get('cleanup.purge.wizard.field').get_wizard_action() + + + diff --git a/database_cleanup/wizards/purge_line_columns.py b/database_cleanup/wizards/purge_line_columns.py new file mode 100644 index 00000000000..9c0537888ca --- /dev/null +++ b/database_cleanup/wizards/purge_line_columns.py @@ -0,0 +1,58 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from odoo import fields, models + +from ..identifier_adapter import IdentifierAdapter + + +class CleanupPurgeLineColumn(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.column" + _description = "Cleanup Purge Line Column" + + model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="CASCADE") + wizard_id = fields.Many2one( + "cleanup.purge.wizard.column", "Purge Wizard", readonly=True + ) + + def purge(self): + """ + Unlink columns upon manual confirmation. + """ + if self: + objs = self + else: + objs = self.env["cleanup.purge.line.column"].browse( + self._context.get("active_ids") + ) + for line in objs: + if line.purged: + continue + model_pool = self.env[line.model_id.model] + # Check whether the column actually still exists. + # Inheritance such as stock.picking.in from stock.picking + # can lead to double attempts at removal + self.env.cr.execute( + "SELECT count(attname) FROM pg_attribute " + "WHERE attrelid = " + "( SELECT oid FROM pg_class WHERE relname = %s ) " + "AND attname = %s", + (model_pool._table, line.name), + ) + if not self.env.cr.fetchone()[0]: + continue + + self.logger.info( + "Dropping column %s from table %s", line.name, model_pool._table + ) + self.env.cr.execute( + "ALTER TABLE %s DROP COLUMN %s", + (IdentifierAdapter(model_pool._table), IdentifierAdapter(line.name)), + ) + line.write({"purged": True}) + # we need this commit because the ORM will deadlock if + # we still have a pending transaction + self.env.cr.commit() # pylint: disable=invalid-commit + return True diff --git a/database_cleanup/views/purge_columns.xml b/database_cleanup/wizards/purge_line_columns.xml similarity index 55% rename from database_cleanup/views/purge_columns.xml rename to database_cleanup/wizards/purge_line_columns.xml index 22f3593c526..546499f521d 100644 --- a/database_cleanup/views/purge_columns.xml +++ b/database_cleanup/wizards/purge_line_columns.xml @@ -1,29 +1,5 @@ - - cleanup.purge.wizard.column - - primary - - - - - - - - - Purge columns - ir.actions.server - code - - - action = env.get('cleanup.purge.wizard.column').get_wizard_action() - - - cleanup.purge.line.column diff --git a/database_cleanup/wizards/purge_line_data.py b/database_cleanup/wizards/purge_line_data.py new file mode 100644 index 00000000000..e65acb27aea --- /dev/null +++ b/database_cleanup/wizards/purge_line_data.py @@ -0,0 +1,28 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class CleanupPurgeLineData(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.data" + _description = "Cleanup Purge Line Data" + + data_id = fields.Many2one("ir.model.data", "Data entry") + wizard_id = fields.Many2one( + "cleanup.purge.wizard.data", "Purge Wizard", readonly=True + ) + + def purge(self): + """Unlink data entries upon manual confirmation.""" + if self: + objs = self + else: + objs = self.env["cleanup.purge.line.data"].browse( + self._context.get("active_ids") + ) + to_unlink = objs.filtered(lambda x: not x.purged and x.data_id) + self.logger.info("Purging data entries: %s", to_unlink.mapped("name")) + to_unlink.mapped("data_id").unlink() + return to_unlink.write({"purged": True}) diff --git a/database_cleanup/views/purge_data.xml b/database_cleanup/wizards/purge_line_data.xml similarity index 51% rename from database_cleanup/views/purge_data.xml rename to database_cleanup/wizards/purge_line_data.xml index 57dcdbf5742..f1fbc61b663 100644 --- a/database_cleanup/views/purge_data.xml +++ b/database_cleanup/wizards/purge_line_data.xml @@ -1,26 +1,5 @@ - - cleanup.purge.wizard.data - - primary - - - - - - - - - Purge data entries that refer to missing resources - ir.actions.server - code - - - action = env.get('cleanup.purge.wizard.data').get_wizard_action() - - - cleanup.purge.line.data diff --git a/database_cleanup/wizards/purge_line_fields.py b/database_cleanup/wizards/purge_line_fields.py new file mode 100644 index 00000000000..6c1fe89771b --- /dev/null +++ b/database_cleanup/wizards/purge_line_fields.py @@ -0,0 +1,90 @@ +# Copyright 2014-2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from odoo import fields, models + +from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG + +from ..identifier_adapter import IdentifierAdapter + + +class CleanupPurgeLineField(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.field" + _description = "Purge fields" + + wizard_id = fields.Many2one( + "cleanup.purge.wizard.field", "Purge Wizard", readonly=True + ) + field_id = fields.Many2one( + comodel_name="ir.model.fields", + string="Field", + ) + model_id = fields.Many2one( + comodel_name="ir.model", + related="field_id.model_id", + string="Model", + store=True, + ) + model_name = fields.Char( + related="model_id.model", + string="Model Technical Name", + store=True, + ) + + def purge(self): + """ + Unlink fields upon manual confirmation. + """ + context_flags = { + MODULE_UNINSTALL_FLAG: True, + "purge": True, + } + if self: + objs = self + else: + objs = self.env["cleanup.purge.line.action"].browse( + self._context.get("active_ids") + ) + to_unlink = objs.filtered(lambda x: not x.purged and x.field_id) + self.logger.info("Purging field entries:") + for rec in to_unlink: + self.logger.info(" - %s.%s", rec.model_name, rec.field_id.name) + field_id = rec.with_context(**context_flags).field_id + model = self.env[rec.model_name] + table_name = model._table + column_name = field_id.name + force_drop = False + # FIX: on unlink, odoo will not DROP the SQL column even if exists if the + # store attribute is set to False. + if not field_id.store and model._auto: + force_drop = True + # Odoo will internally drop the SQL column + field_id.unlink() + if force_drop: + self._drop_column(table_name, column_name) + rec.purged = True + return True + + def _drop_column(self, table, column): + # Use code from `purge_columns.py::purge()` + # Check whether the column actually still exists. + # Inheritance such as stock.picking.in from stock.picking + # can lead to double attempts at removal + self.env.cr.execute( + "SELECT count(attname) FROM pg_attribute " + "WHERE attrelid = " + "( SELECT oid FROM pg_class WHERE relname = %s ) " + "AND attname = %s", + (table, column), + ) + if not self.env.cr.fetchone()[0]: + return + self.logger.info("Dropping column %s from table %s", column, table) + self.env.cr.execute( + "ALTER TABLE %s DROP COLUMN %s", + (IdentifierAdapter(table), IdentifierAdapter(column)), + ) + # we need this commit because the ORM will deadlock if + # we still have a pending transaction + self.env.cr.commit() # pylint: disable=invalid-commit diff --git a/database_cleanup/views/purge_fields.xml b/database_cleanup/wizards/purge_line_fields.xml similarity index 60% rename from database_cleanup/views/purge_fields.xml rename to database_cleanup/wizards/purge_line_fields.xml index 87afbc0cf69..dc83a647e29 100644 --- a/database_cleanup/views/purge_fields.xml +++ b/database_cleanup/wizards/purge_line_fields.xml @@ -1,27 +1,5 @@ - - cleanup.purge.wizard.field - - primary - - - - - - - Purge models - ir.actions.server - code - - - action = env.get('cleanup.purge.wizard.field').get_wizard_action() - - - cleanup.purge.line.field diff --git a/database_cleanup/wizards/purge_line_menus.py b/database_cleanup/wizards/purge_line_menus.py new file mode 100644 index 00000000000..42cd987f9be --- /dev/null +++ b/database_cleanup/wizards/purge_line_menus.py @@ -0,0 +1,29 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from odoo import fields, models + + +class CleanupPurgeLineMenu(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.menu" + _description = "Cleanup Purge Line Menu" + + wizard_id = fields.Many2one( + "cleanup.purge.wizard.menu", "Purge Wizard", readonly=True + ) + menu_id = fields.Many2one("ir.ui.menu", "Menu entry") + + def purge(self): + """Unlink menu entries upon manual confirmation.""" + if self: + objs = self + else: + objs = self.env["cleanup.purge.line.menu"].browse( + self._context.get("active_ids") + ) + to_unlink = objs.filtered(lambda x: not x.purged and x.menu_id) + self.logger.info("Purging menu entries: %s", to_unlink.mapped("name")) + to_unlink.mapped("menu_id").unlink() + return to_unlink.write({"purged": True}) diff --git a/database_cleanup/views/purge_menus.xml b/database_cleanup/wizards/purge_line_menus.xml similarity index 56% rename from database_cleanup/views/purge_menus.xml rename to database_cleanup/wizards/purge_line_menus.xml index 53089dad4e5..2f14ad156a1 100644 --- a/database_cleanup/views/purge_menus.xml +++ b/database_cleanup/wizards/purge_line_menus.xml @@ -1,24 +1,5 @@ - - cleanup.purge.wizard.menu - - primary - - - - - - - Purge menus - ir.actions.server - code - - - action = env.get('cleanup.purge.wizard.menu').get_wizard_action() - - - cleanup.purge.line.menu diff --git a/database_cleanup/wizards/purge_line_models.py b/database_cleanup/wizards/purge_line_models.py new file mode 100644 index 00000000000..31b8b5f3e3d --- /dev/null +++ b/database_cleanup/wizards/purge_line_models.py @@ -0,0 +1,82 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +import logging + +from odoo import fields, models + +from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG + +_logger = logging.getLogger(__name__) + + +class CleanupPurgeLineModel(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.model" + _description = "Cleanup Purge Line Model" + + wizard_id = fields.Many2one( + "cleanup.purge.wizard.model", "Purge Wizard", readonly=True + ) + + def purge(self): + """ + Unlink models upon manual confirmation. + """ + context_flags = { + MODULE_UNINSTALL_FLAG: True, + "purge": True, + } + + if self: + objs = self + else: + objs = self.env["cleanup.purge.line.model"].browse( + self._context.get("active_ids") + ) + for line in objs: + self.env.cr.execute( + "SELECT id, model from ir_model WHERE model = %s", (line.name,) + ) + row = self.env.cr.fetchone() + if not row: + continue + self.logger.info("Purging model %s", row[1]) + attachments = self.env["ir.attachment"].search( + [("res_model", "=", line.name)] + ) + if attachments: + self.env.cr.execute( + "UPDATE ir_attachment SET res_model = NULL WHERE id in %s", + (tuple(attachments.ids),), + ) + self.env["ir.model.constraint"].search( + [ + ("model", "=", line.name), + ] + ).unlink() + relations = ( + self.env["ir.model.fields"] + .search( + [ + ("relation", "=", row[1]), + ] + ) + .with_context(**context_flags) + ) + for relation in relations: + try: + # Fails if the model on the target side + # cannot be instantiated + relation.unlink() + except KeyError: + _logger.error("") + except AttributeError: + _logger.error("") + self.env["ir.model.relation"].search( + [("model", "=", line.name)] + ).with_context(**context_flags).unlink() + self.env["ir.model"].browse([row[0]]).with_context(**context_flags).unlink() + line.write({"purged": True}) + return True diff --git a/database_cleanup/views/purge_models.xml b/database_cleanup/wizards/purge_line_models.xml similarity index 55% rename from database_cleanup/views/purge_models.xml rename to database_cleanup/wizards/purge_line_models.xml index 2fbb269d50e..3a1374d3112 100644 --- a/database_cleanup/views/purge_models.xml +++ b/database_cleanup/wizards/purge_line_models.xml @@ -1,27 +1,5 @@ - - cleanup.purge.wizard.model - - primary - - - - - - - Purge models - ir.actions.server - code - - - action = env.get('cleanup.purge.wizard.model').get_wizard_action() - - - cleanup.purge.line.model diff --git a/database_cleanup/wizards/purge_line_modules.py b/database_cleanup/wizards/purge_line_modules.py new file mode 100644 index 00000000000..de7f13bd318 --- /dev/null +++ b/database_cleanup/wizards/purge_line_modules.py @@ -0,0 +1,34 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from odoo import fields, models + + +class CleanupPurgeLineModule(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.module" + _description = "Cleanup Purge Line Module" + + wizard_id = fields.Many2one( + "cleanup.purge.wizard.module", "Purge Wizard", readonly=True + ) + + def purge(self): + """ + Uninstall modules upon manual confirmation, then reload + the database. + """ + module_names = self.filtered(lambda x: not x.purged).mapped("name") + modules = self.env["ir.module.module"].search([("name", "in", module_names)]) + if not modules: + return True + self.logger.info("Purging modules %s", ", ".join(module_names)) + installed = modules.filtered(lambda x: x.state in ("installed", "to upgrade")) + to_remove = modules - installed + to_remove += to_remove.downstream_dependencies() + to_remove.write({"state": "to remove"}) + installed.button_immediate_uninstall() + with self.env.registry.cursor() as new_cr: + self.env(cr=new_cr)["ir.module.module"].browse(modules.ids).unlink() + return self.write({"purged": True}) diff --git a/database_cleanup/views/purge_modules.xml b/database_cleanup/wizards/purge_line_modules.xml similarity index 55% rename from database_cleanup/views/purge_modules.xml rename to database_cleanup/wizards/purge_line_modules.xml index 3f49f70620d..9013ff40099 100644 --- a/database_cleanup/views/purge_modules.xml +++ b/database_cleanup/wizards/purge_line_modules.xml @@ -1,27 +1,5 @@ - - cleanup.purge.wizard.module - - primary - - - - - - - Purge modules - ir.actions.server - code - - - action = env.get('cleanup.purge.wizard.module').get_wizard_action() - - - cleanup.purge.line.module diff --git a/database_cleanup/wizards/purge_line_tables.py b/database_cleanup/wizards/purge_line_tables.py new file mode 100644 index 00000000000..82b7be4a0b1 --- /dev/null +++ b/database_cleanup/wizards/purge_line_tables.py @@ -0,0 +1,90 @@ +# Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited +from psycopg2.extensions import AsIs + +from odoo import fields, models + +from ..identifier_adapter import IdentifierAdapter + +_TABLE_TYPE_SELECTION = [ + ("base", "SQL Table"), + ("view", "SQL View"), +] + + +class CleanupPurgeLineTable(models.TransientModel): + _inherit = "cleanup.purge.line" + _name = "cleanup.purge.line.table" + _description = "Cleanup Purge Line Table" + + wizard_id = fields.Many2one( + "cleanup.purge.wizard.table", "Purge Wizard", readonly=True + ) + table_type = fields.Selection(selection=_TABLE_TYPE_SELECTION) + + def purge(self): + """ + Unlink tables upon manual confirmation. + """ + if self: + objs = self + else: + objs = self.env["cleanup.purge.line.table"].browse( + self._context.get("active_ids") + ) + tables = objs.mapped("name") + for line in objs: + if line.purged: + continue + + # Retrieve constraints on the tables to be dropped + # This query is referenced in numerous places + # on the Internet but credits probably go to Tom Lane + # in this post http://www.postgresql.org/\ + # message-id/22895.1226088573@sss.pgh.pa.us + # Only using the constraint name and the source table, + # but I'm leaving the rest in for easier debugging + self.env.cr.execute( + """ + SELECT conname, confrelid::regclass, af.attname AS fcol, + conrelid::regclass, a.attname AS col + FROM pg_attribute af, pg_attribute a, + (SELECT conname, conrelid, confrelid,conkey[i] AS conkey, + confkey[i] AS confkey + FROM (select conname, conrelid, confrelid, conkey, + confkey, generate_series(1,array_upper(conkey,1)) AS i + FROM pg_constraint WHERE contype = 'f') ss) ss2 + WHERE af.attnum = confkey AND af.attrelid = confrelid AND + a.attnum = conkey AND a.attrelid = conrelid + AND confrelid::regclass = '%s'::regclass; + """, + (IdentifierAdapter(line.name, quote=False),), + ) + + for constraint in self.env.cr.fetchall(): + if constraint[3] in tables: + self.logger.info( + "Dropping constraint %s on table %s (to be dropped)", + constraint[0], + constraint[3], + ) + self.env.cr.execute( + "ALTER TABLE %s DROP CONSTRAINT %s", + ( + IdentifierAdapter(constraint[3]), + IdentifierAdapter(constraint[0]), + ), + ) + + if line.table_type == "base": + _sql_type = "TABLE" + elif line.table_type == "view": + _sql_type = "VIEW" + self.logger.info("Dropping %s %s", _sql_type, line.name) + self.env.cr.execute( + "DROP %s %s", (AsIs(_sql_type), IdentifierAdapter(line.name)) + ) + line.write({"purged": True}) + return True diff --git a/database_cleanup/views/purge_tables.xml b/database_cleanup/wizards/purge_line_tables.xml similarity index 58% rename from database_cleanup/views/purge_tables.xml rename to database_cleanup/wizards/purge_line_tables.xml index 5ac8f449254..1008db7b286 100644 --- a/database_cleanup/views/purge_tables.xml +++ b/database_cleanup/wizards/purge_line_tables.xml @@ -1,27 +1,5 @@ - - cleanup.purge.wizard.table - - primary - - - - - - - Purge tables - ir.actions.server - code - - - action = env.get('cleanup.purge.wizard.table').get_wizard_action() - - - cleanup.purge.line.table diff --git a/database_cleanup/wizards/purge_menus.py b/database_cleanup/wizards/purge_menus.py index de29b8f5e9d..be3d529df25 100644 --- a/database_cleanup/wizards/purge_menus.py +++ b/database_cleanup/wizards/purge_menus.py @@ -6,30 +6,6 @@ from odoo.exceptions import UserError -class CleanupPurgeLineMenu(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.menu" - _description = "Cleanup Purge Line Menu" - - wizard_id = fields.Many2one( - "cleanup.purge.wizard.menu", "Purge Wizard", readonly=True - ) - menu_id = fields.Many2one("ir.ui.menu", "Menu entry") - - def purge(self): - """Unlink menu entries upon manual confirmation.""" - if self: - objs = self - else: - objs = self.env["cleanup.purge.line.menu"].browse( - self._context.get("active_ids") - ) - to_unlink = objs.filtered(lambda x: not x.purged and x.menu_id) - self.logger.info("Purging menu entries: %s", to_unlink.mapped("name")) - to_unlink.mapped("menu_id").unlink() - return to_unlink.write({"purged": True}) - - class CleanupPurgeWizardMenu(models.TransientModel): _inherit = "cleanup.purge.wizard" _name = "cleanup.purge.wizard.menu" diff --git a/database_cleanup/wizards/purge_menus.xml b/database_cleanup/wizards/purge_menus.xml new file mode 100644 index 00000000000..a5ed0a8974e --- /dev/null +++ b/database_cleanup/wizards/purge_menus.xml @@ -0,0 +1,21 @@ + + + + cleanup.purge.wizard.menu + + primary + + + + + + + Purge menus + ir.actions.server + code + + + action = env.get('cleanup.purge.wizard.menu').get_wizard_action() + + + diff --git a/database_cleanup/wizards/purge_models.py b/database_cleanup/wizards/purge_models.py index e6f2a673dad..9fedd91cb77 100644 --- a/database_cleanup/wizards/purge_models.py +++ b/database_cleanup/wizards/purge_models.py @@ -2,86 +2,9 @@ # Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -import logging - from odoo import api, fields, models from odoo.exceptions import UserError -from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG - -_logger = logging.getLogger(__name__) - - -class CleanupPurgeLineModel(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.model" - _description = "Cleanup Purge Line Model" - - wizard_id = fields.Many2one( - "cleanup.purge.wizard.model", "Purge Wizard", readonly=True - ) - - def purge(self): - """ - Unlink models upon manual confirmation. - """ - context_flags = { - MODULE_UNINSTALL_FLAG: True, - "purge": True, - } - - if self: - objs = self - else: - objs = self.env["cleanup.purge.line.model"].browse( - self._context.get("active_ids") - ) - for line in objs: - self.env.cr.execute( - "SELECT id, model from ir_model WHERE model = %s", (line.name,) - ) - row = self.env.cr.fetchone() - if not row: - continue - self.logger.info("Purging model %s", row[1]) - attachments = self.env["ir.attachment"].search( - [("res_model", "=", line.name)] - ) - if attachments: - self.env.cr.execute( - "UPDATE ir_attachment SET res_model = NULL WHERE id in %s", - (tuple(attachments.ids),), - ) - self.env["ir.model.constraint"].search( - [ - ("model", "=", line.name), - ] - ).unlink() - relations = ( - self.env["ir.model.fields"] - .search( - [ - ("relation", "=", row[1]), - ] - ) - .with_context(**context_flags) - ) - for relation in relations: - try: - # Fails if the model on the target side - # cannot be instantiated - relation.unlink() - except KeyError: - _logger.error("") - except AttributeError: - _logger.error("") - self.env["ir.model.relation"].search( - [("model", "=", line.name)] - ).with_context(**context_flags).unlink() - self.env["ir.model"].browse([row[0]]).with_context(**context_flags).unlink() - line.write({"purged": True}) - return True - class CleanupPurgeWizardModel(models.TransientModel): _inherit = "cleanup.purge.wizard" diff --git a/database_cleanup/wizards/purge_models.xml b/database_cleanup/wizards/purge_models.xml new file mode 100644 index 00000000000..716bce2dc5f --- /dev/null +++ b/database_cleanup/wizards/purge_models.xml @@ -0,0 +1,24 @@ + + + + cleanup.purge.wizard.model + + primary + + + + + + + Purge models + ir.actions.server + code + + + action = env.get('cleanup.purge.wizard.model').get_wizard_action() + + + diff --git a/database_cleanup/wizards/purge_modules.py b/database_cleanup/wizards/purge_modules.py index c1d9b5e81bf..db7a1c57e9e 100644 --- a/database_cleanup/wizards/purge_modules.py +++ b/database_cleanup/wizards/purge_modules.py @@ -7,35 +7,6 @@ from odoo.modules.module import get_module_path -class CleanupPurgeLineModule(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.module" - _description = "Cleanup Purge Line Module" - - wizard_id = fields.Many2one( - "cleanup.purge.wizard.module", "Purge Wizard", readonly=True - ) - - def purge(self): - """ - Uninstall modules upon manual confirmation, then reload - the database. - """ - module_names = self.filtered(lambda x: not x.purged).mapped("name") - modules = self.env["ir.module.module"].search([("name", "in", module_names)]) - if not modules: - return True - self.logger.info("Purging modules %s", ", ".join(module_names)) - installed = modules.filtered(lambda x: x.state in ("installed", "to upgrade")) - to_remove = modules - installed - to_remove += to_remove.downstream_dependencies() - to_remove.write({"state": "to remove"}) - installed.button_immediate_uninstall() - with self.env.registry.cursor() as new_cr: - self.env(cr=new_cr)["ir.module.module"].browse(modules.ids).unlink() - return self.write({"purged": True}) - - class CleanupPurgeWizardModule(models.TransientModel): _inherit = "cleanup.purge.wizard" _name = "cleanup.purge.wizard.module" diff --git a/database_cleanup/wizards/purge_modules.xml b/database_cleanup/wizards/purge_modules.xml new file mode 100644 index 00000000000..1b128652c2f --- /dev/null +++ b/database_cleanup/wizards/purge_modules.xml @@ -0,0 +1,24 @@ + + + + cleanup.purge.wizard.module + + primary + + + + + + + Purge modules + ir.actions.server + code + + + action = env.get('cleanup.purge.wizard.module').get_wizard_action() + + + diff --git a/database_cleanup/wizards/purge_tables.py b/database_cleanup/wizards/purge_tables.py index 2ae713e08fc..4376d8b41c7 100644 --- a/database_cleanup/wizards/purge_tables.py +++ b/database_cleanup/wizards/purge_tables.py @@ -2,100 +2,19 @@ # Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -from psycopg2.extensions import AsIs - from odoo import api, fields, models from odoo.exceptions import UserError -from ..identifier_adapter import IdentifierAdapter - _TABLE_TYPE_SELECTION = [ ("base", "SQL Table"), ("view", "SQL View"), ] -class CleanupPurgeLineTable(models.TransientModel): - _inherit = "cleanup.purge.line" - _name = "cleanup.purge.line.table" - _description = "Cleanup Purge Line Table" - - wizard_id = fields.Many2one( - "cleanup.purge.wizard.table", "Purge Wizard", readonly=True - ) - table_type = fields.Selection(selection=_TABLE_TYPE_SELECTION) - - def purge(self): - """ - Unlink tables upon manual confirmation. - """ - if self: - objs = self - else: - objs = self.env["cleanup.purge.line.table"].browse( - self._context.get("active_ids") - ) - tables = objs.mapped("name") - for line in objs: - if line.purged: - continue - - # Retrieve constraints on the tables to be dropped - # This query is referenced in numerous places - # on the Internet but credits probably go to Tom Lane - # in this post http://www.postgresql.org/\ - # message-id/22895.1226088573@sss.pgh.pa.us - # Only using the constraint name and the source table, - # but I'm leaving the rest in for easier debugging - self.env.cr.execute( - """ - SELECT conname, confrelid::regclass, af.attname AS fcol, - conrelid::regclass, a.attname AS col - FROM pg_attribute af, pg_attribute a, - (SELECT conname, conrelid, confrelid,conkey[i] AS conkey, - confkey[i] AS confkey - FROM (select conname, conrelid, confrelid, conkey, - confkey, generate_series(1,array_upper(conkey,1)) AS i - FROM pg_constraint WHERE contype = 'f') ss) ss2 - WHERE af.attnum = confkey AND af.attrelid = confrelid AND - a.attnum = conkey AND a.attrelid = conrelid - AND confrelid::regclass = '%s'::regclass; - """, - (IdentifierAdapter(line.name, quote=False),), - ) - - for constraint in self.env.cr.fetchall(): - if constraint[3] in tables: - self.logger.info( - "Dropping constraint %s on table %s (to be dropped)", - constraint[0], - constraint[3], - ) - self.env.cr.execute( - "ALTER TABLE %s DROP CONSTRAINT %s", - ( - IdentifierAdapter(constraint[3]), - IdentifierAdapter(constraint[0]), - ), - ) - - if line.table_type == "base": - _sql_type = "TABLE" - elif line.table_type == "view": - _sql_type = "VIEW" - self.logger.info("Dropping %s %s", _sql_type, line.name) - self.env.cr.execute( - "DROP %s %s", (AsIs(_sql_type), IdentifierAdapter(line.name)) - ) - line.write({"purged": True}) - return True - - class CleanupPurgeWizardTable(models.TransientModel): _inherit = "cleanup.purge.wizard" _name = "cleanup.purge.wizard.table" _description = "Purge tables" - blacklist = [ "endpoint_route", # web-api/endpoint_route_handler ] diff --git a/database_cleanup/wizards/purge_tables.xml b/database_cleanup/wizards/purge_tables.xml new file mode 100644 index 00000000000..9e4c9e70ed4 --- /dev/null +++ b/database_cleanup/wizards/purge_tables.xml @@ -0,0 +1,24 @@ + + + + cleanup.purge.wizard.table + + primary + + + + + + + Purge tables + ir.actions.server + code + + + action = env.get('cleanup.purge.wizard.table').get_wizard_action() + + + From 1516b4012afa7e4f1e0c0b8ae85b9d845a4597be Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 12 Dec 2025 08:35:40 +0000 Subject: [PATCH 119/770] [UPD] Update database_cleanup.pot --- database_cleanup/i18n/database_cleanup.pot | 51 ++++++++-------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/database_cleanup/i18n/database_cleanup.pot b/database_cleanup/i18n/database_cleanup.pot index 98fdf8367e2..1c6ec83a896 100644 --- a/database_cleanup/i18n/database_cleanup.pot +++ b/database_cleanup/i18n/database_cleanup.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 18.0\n" +"Project-Id-Version: Odoo Server 19.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -143,6 +143,7 @@ msgstr "" #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_create_indexes_line__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_create_indexes_wizard__display_name +#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_data__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_field__display_name @@ -150,6 +151,7 @@ msgstr "" #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_model__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_module__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_table__display_name +#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_column__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_data__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_field__display_name @@ -157,6 +159,9 @@ msgstr "" #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_model__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_module__display_name #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_table__display_name +#: model:ir.model.fields,field_description:database_cleanup.field_ir_model__display_name +#: model:ir.model.fields,field_description:database_cleanup.field_ir_model_data__display_name +#: model:ir.model.fields,field_description:database_cleanup.field_ir_model_fields__display_name msgid "Display Name" msgstr "" @@ -179,6 +184,7 @@ msgstr "" #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_create_indexes_line__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_create_indexes_wizard__id +#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_data__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_field__id @@ -186,6 +192,7 @@ msgstr "" #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_model__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_module__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_table__id +#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_column__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_data__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_field__id @@ -193,6 +200,9 @@ msgstr "" #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_model__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_module__id #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_table__id +#: model:ir.model.fields,field_description:database_cleanup.field_ir_model__id +#: model:ir.model.fields,field_description:database_cleanup.field_ir_model_data__id +#: model:ir.model.fields,field_description:database_cleanup.field_ir_model_fields__id msgid "ID" msgstr "" @@ -292,43 +302,43 @@ msgstr "" #. module: database_cleanup #. odoo-python -#: code:addons/database_cleanup/models/purge_menus.py:0 +#: code:addons/database_cleanup/wizards/purge_menus.py:0 msgid "No dangling menu entries found" msgstr "" #. module: database_cleanup #. odoo-python -#: code:addons/database_cleanup/models/purge_modules.py:0 +#: code:addons/database_cleanup/wizards/purge_modules.py:0 msgid "No modules found to purge" msgstr "" #. module: database_cleanup #. odoo-python -#: code:addons/database_cleanup/models/purge_columns.py:0 +#: code:addons/database_cleanup/wizards/purge_columns.py:0 msgid "No orphaned columns found" msgstr "" #. module: database_cleanup #. odoo-python -#: code:addons/database_cleanup/models/purge_data.py:0 +#: code:addons/database_cleanup/wizards/purge_data.py:0 msgid "No orphaned data entries found" msgstr "" #. module: database_cleanup #. odoo-python -#: code:addons/database_cleanup/models/purge_fields.py:0 +#: code:addons/database_cleanup/wizards/purge_fields.py:0 msgid "No orphaned fields found" msgstr "" #. module: database_cleanup #. odoo-python -#: code:addons/database_cleanup/models/purge_models.py:0 +#: code:addons/database_cleanup/wizards/purge_models.py:0 msgid "No orphaned models found" msgstr "" #. module: database_cleanup #. odoo-python -#: code:addons/database_cleanup/models/purge_tables.py:0 +#: code:addons/database_cleanup/wizards/purge_tables.py:0 msgid "No orphaned tables found" msgstr "" @@ -531,31 +541,6 @@ msgstr "" msgid "Select lines to purge" msgstr "" -#. module: database_cleanup -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_create_indexes_line__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_create_indexes_wizard__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_column__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_data__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_field__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_menu__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_model__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_module__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_table__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_column__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_data__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_field__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_menu__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_model__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_module__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_wizard_table__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_ir_model__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_ir_model_data__smart_search -#: model:ir.model.fields,field_description:database_cleanup.field_ir_model_fields__smart_search -msgid "Smart Search" -msgstr "" - #. module: database_cleanup #: model:ir.model.fields,field_description:database_cleanup.field_cleanup_purge_line_table__table_type msgid "Table Type" From cb60ff758727e9af0516b6ba011719a868892f8c Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 12 Dec 2025 08:37:32 +0000 Subject: [PATCH 120/770] [BOT] post-merge updates --- README.md | 1 + database_cleanup/README.rst | 2 +- database_cleanup/static/description/index.html | 2 +- setup/_metapackage/pyproject.toml | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index df3d4d38704..bc73f2a50f4 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ addon | version | maintainers | summary --- | --- | --- | --- [base_exception](base_exception/) | 19.0.1.0.0 | hparfr sebastienbeau | This module provide an abstract model to manage customizable exceptions to be applied on different models (sale order, invoice, ...) [bus_alt_connection](bus_alt_connection/) | 19.0.1.0.0 | | Needed when using PgBouncer as a connection pooler +[database_cleanup](database_cleanup/) | 19.0.1.0.0 | | Database cleanup [session_db](session_db/) | 19.0.1.0.0 | sbidoul | Store sessions in DB [//]: # (end addons) diff --git a/database_cleanup/README.rst b/database_cleanup/README.rst index 3df10b81943..389fe5135e1 100644 --- a/database_cleanup/README.rst +++ b/database_cleanup/README.rst @@ -11,7 +11,7 @@ Database cleanup !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:1bc48f79f4a199586b709106c87ff617584036cc2158f711b91e0335489dcc88 + !! source digest: sha256:cdd838e46b5647b8b7f3d34896dd7f1f1225c9a4f99a8a6c8c59cfc164aef751 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/database_cleanup/static/description/index.html b/database_cleanup/static/description/index.html index 18d6ea8648c..5cdd44a4cb1 100644 --- a/database_cleanup/static/description/index.html +++ b/database_cleanup/static/description/index.html @@ -372,7 +372,7 @@

    Database cleanup

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:1bc48f79f4a199586b709106c87ff617584036cc2158f711b91e0335489dcc88 +!! source digest: sha256:cdd838e46b5647b8b7f3d34896dd7f1f1225c9a4f99a8a6c8c59cfc164aef751 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

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

    Clean your Odoo database from remnants of modules, models, columns and diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml index 610745c2b82..52442f880bf 100644 --- a/setup/_metapackage/pyproject.toml +++ b/setup/_metapackage/pyproject.toml @@ -1,9 +1,10 @@ [project] name = "odoo-addons-oca-server-tools" -version = "19.0.20251113.0" +version = "19.0.20251212.0" dependencies = [ "odoo-addon-base_exception==19.0.*", "odoo-addon-bus_alt_connection==19.0.*", + "odoo-addon-database_cleanup==19.0.*", "odoo-addon-session_db==19.0.*", ] classifiers=[ From 8107893f7aa016e0ee1726f5b1ee3a8c99daed2b Mon Sep 17 00:00:00 2001 From: nans Date: Thu, 3 Sep 2020 09:45:29 +0200 Subject: [PATCH 121/770] [ADD] base_partition: add partition method to base model --- base_partition/README.rst | 84 ++++ base_partition/__init__.py | 1 + base_partition/__manifest__.py | 15 + base_partition/i18n/base_partition.pot | 22 + base_partition/models/__init__.py | 1 + base_partition/models/models.py | 41 ++ base_partition/readme/CONTRIBUTORS.rst | 1 + base_partition/readme/DESCRIPTION.rst | 12 + base_partition/static/description/icon.png | Bin 0 -> 9455 bytes base_partition/static/description/index.html | 428 +++++++++++++++++++ base_partition/tests/__init__.py | 1 + base_partition/tests/test_partition.py | 80 ++++ 12 files changed, 686 insertions(+) create mode 100644 base_partition/README.rst create mode 100644 base_partition/__init__.py create mode 100644 base_partition/__manifest__.py create mode 100644 base_partition/i18n/base_partition.pot create mode 100644 base_partition/models/__init__.py create mode 100644 base_partition/models/models.py create mode 100644 base_partition/readme/CONTRIBUTORS.rst create mode 100644 base_partition/readme/DESCRIPTION.rst create mode 100644 base_partition/static/description/icon.png create mode 100644 base_partition/static/description/index.html create mode 100644 base_partition/tests/__init__.py create mode 100644 base_partition/tests/test_partition.py diff --git a/base_partition/README.rst b/base_partition/README.rst new file mode 100644 index 00000000000..118427deaf0 --- /dev/null +++ b/base_partition/README.rst @@ -0,0 +1,84 @@ +============== +Base Partition +============== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/12.0/base_partition + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-base_partition + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a `partition(self, accessor)` method to every model. +It accepts for accessor any parameter that would be accepted by `mapped`, +i.e. a string `"field(.subfield)*"` or a function `(lambda x: not x.b)`. +It returns a dictionary with keys that are equal to `set(record.mapped(accessor))`, +and with values that are recordsets +(these recordsets forming a partition of the initial recordset, conveniently). + +So if we have a recordset (x | y | z ) such that x.f == True, y.f == z.f == False, +then (x | y | z ).partition("f") == {True: x, False: (y | z)}. + +It also provides a backport of `filtered_domain`, +which filters a recordset in place with a provided domain. + +**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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Acsone + +Contributors +~~~~~~~~~~~~ + +* Nans Lefebvre + +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-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_partition/__init__.py b/base_partition/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/base_partition/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_partition/__manifest__.py b/base_partition/__manifest__.py new file mode 100644 index 00000000000..fcdebbaa002 --- /dev/null +++ b/base_partition/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Acsone (http://www.acsone.eu) +# Nans Lefebvre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Base Partition", + "summary": "Base module that provide the partition method on all models", + "version": "12.0.0.0.0", + "category": "Uncategorized", + "website": "https://github.com/OCA/server-tools", + "author": "Acsone, Odoo Community Association (OCA)", + "license": "AGPL-3", + "installable": True, + "depends": ["base"], +} diff --git a/base_partition/i18n/base_partition.pot b/base_partition/i18n/base_partition.pot new file mode 100644 index 00000000000..f6210522614 --- /dev/null +++ b/base_partition/i18n/base_partition.pot @@ -0,0 +1,22 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_partition +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-09-04 13:29+0000\n" +"PO-Revision-Date: 2020-09-04 13:29+0000\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_partition +#: model:ir.model,name:base_partition.model_base +msgid "Base" +msgstr "" + diff --git a/base_partition/models/__init__.py b/base_partition/models/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/base_partition/models/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_partition/models/models.py b/base_partition/models/models.py new file mode 100644 index 00000000000..ed818ac241b --- /dev/null +++ b/base_partition/models/models.py @@ -0,0 +1,41 @@ +# © 2020 Acsone (http://www.acsone.eu) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + +class Base(models.AbstractModel): + + _inherit = 'base' + + def partition(self, accessor): + """Returns a dictionary forming a partition of self into a dictionary + value/recordset for each value obtained from the accessor. + The accessor itself can be either a string that can be passed to mapped, + or an arbitrary function. + Note that it is always at least as fast to pass a function, + hence the current implementation. + If we have a 'field.subfield' accessor such that subfield is not a relational + then the result is a list (not hashable). Then the str(key) are used. + In the general case a value could both not be hashable nor stringifiable, + in a which case this function would crash. + """ + partition = {} + + if isinstance(accessor, str): + if "." not in accessor: + func = lambda r: r[accessor] # noqa: E731 + else: + func = lambda r: r.mapped(accessor) # noqa: E731 + else: + func = accessor + + for record in self: + key = func(record) + if not key.__hash__: + key = str(key) + if key not in partition: + partition[key] = record + else: + partition[key] += record + + return partition diff --git a/base_partition/readme/CONTRIBUTORS.rst b/base_partition/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..4e30b28db7a --- /dev/null +++ b/base_partition/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Nans Lefebvre diff --git a/base_partition/readme/DESCRIPTION.rst b/base_partition/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..7b96e6257a1 --- /dev/null +++ b/base_partition/readme/DESCRIPTION.rst @@ -0,0 +1,12 @@ +This module adds a `partition(self, accessor)` method to every model. +It accepts for accessor any parameter that would be accepted by `mapped`, +i.e. a string `"field(.subfield)*"` or a function `(lambda x: not x.b)`. +It returns a dictionary with keys that are equal to `set(record.mapped(accessor))`, +and with values that are recordsets +(these recordsets forming a partition of the initial recordset, conveniently). + +So if we have a recordset (x | y | z ) such that x.f == True, y.f == z.f == False, +then (x | y | z ).partition("f") == {True: x, False: (y | z)}. + +It also provides a backport of `filtered_domain`, +which filters a recordset in place with a provided domain. diff --git a/base_partition/static/description/icon.png b/base_partition/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/base_partition/static/description/index.html b/base_partition/static/description/index.html new file mode 100644 index 00000000000..9b0627c6879 --- /dev/null +++ b/base_partition/static/description/index.html @@ -0,0 +1,428 @@ + + + + + + +Base Partition + + + +

    +

    Base Partition

    + + +

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

    +

    This module adds a partition(self, accessor) method to every model. +It accepts for accessor any parameter that would be accepted by mapped, +i.e. a string “field(.subfield)*” or a function (lambda x: not x.b). +It returns a dictionary with keys that are equal to set(record.mapped(accessor)), +and with values that are recordsets +(these recordsets forming a partition of the initial recordset, conveniently).

    +

    So if we have a recordset (x | y | z ) such that x.f == True, y.f == z.f == False, +then (x | y | z ).partition(“f”) == {True: x, False: (y | z)}.

    +

    It also provides a backport of filtered_domain, +which filters a recordset in place with a provided domain.

    +

    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 smashing it by providing a detailed and welcomed +feedback.

    +

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

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • Acsone
    • +
    +
    +
    +

    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-tools project on GitHub.

    +

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

    +
    +
    +
    + + diff --git a/base_partition/tests/__init__.py b/base_partition/tests/__init__.py new file mode 100644 index 00000000000..7b173054be4 --- /dev/null +++ b/base_partition/tests/__init__.py @@ -0,0 +1 @@ +from . import test_partition diff --git a/base_partition/tests/test_partition.py b/base_partition/tests/test_partition.py new file mode 100644 index 00000000000..942521db760 --- /dev/null +++ b/base_partition/tests/test_partition.py @@ -0,0 +1,80 @@ +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import functools +from odoo.tests.common import TransactionCase + + +class TestPartition(TransactionCase): + def setUp(self): + super(TestPartition, self).setUp() + + self.Category = self.env["res.partner.category"] + self.c1 = self.Category.create({"name": "c1"}) + self.c2 = self.Category.create({"name": "c2"}) + self.c3 = self.Category.create({"name": "c3"}) + + self.Partner = self.env['res.partner'] + self.parent1 = self.Partner.create({"name": "parent1"}) + self.parent2 = self.Partner.create({"name": "parent2"}) + self.child1 = self.Partner.create({"name": "child1"}) + self.child2 = self.Partner.create({"name": "child2"}) + self.child3 = self.Partner.create({"name": "child3"}) + self.x = self.Partner.create({ + "name": "x", + "customer": True, + "category_id": [(6, 0, [self.c1.id, self.c2.id])], + "child_ids": [(6, 0, [self.child1.id, self.child2.id])], + "parent_id": self.parent1.id, + }) + self.y = self.Partner.create({ + "name": "y", + "customer": False, + "category_id": [(6, 0, [self.c2.id, self.c3.id])], + "child_ids": [(6, 0, [self.child2.id, self.child3.id])], + "parent_id": self.parent2.id, + }) + self.z = self.Partner.create({ + "name": "z", + "customer": False, + "category_id": [(6, 0, [self.c1.id, self.c3.id])], + "child_ids": [(6, 0, [self.child1.id, self.child3.id])], + "parent_id": self.parent2.id, + }) + self.xyz = self.x + self.y + self.z + + def test_partition_many2many(self): + self.partition_field_test("category_id") + + def test_partition_many2one(self): + self.partition_field_test("parent_id") + + def test_partition_one2many(self): + self.partition_field_test("child_ids") + + def test_partition_boolean(self): + self.partition_field_test("customer", relational=False) + + def test_partition_dotdot_relational(self): + self.partition_field_test("parent_id.category_id", relational=True, dotdot=True) + + def test_partition_dotdot_nonrelational(self): + self.partition_field_test("parent_id.name", relational=False, dotdot=True) + + def partition_field_test(self, field_name, relational=True, dotdot=False): + """To check that we have a partition we need to check that: + - all field values are keys + - the set of all keys is the same + """ + partition = self.xyz.partition(field_name) + + if relational: + values = [s.mapped(field_name) for s in self.xyz] + else: + values = self.xyz.mapped(field_name) + if dotdot and not relational: + values = [str(s.mapped(field_name)) for s in self.xyz] + self.assertEqual(set(partition.keys()), set(values)) + + records = functools.reduce(sum, partition.values()) + self.assertEqual(self.xyz, records) # we get the same recordset From cce4e093b706f6bb68182dcb2bd4dbb492fe5560 Mon Sep 17 00:00:00 2001 From: nans Date: Mon, 7 Sep 2020 17:32:28 +0200 Subject: [PATCH 122/770] [IMP] models: backport filtered_domain --- base_partition/models/models.py | 120 ++++++++++++++++++++++++- base_partition/tests/test_partition.py | 108 +++++++++++++++++----- 2 files changed, 205 insertions(+), 23 deletions(-) diff --git a/base_partition/models/models.py b/base_partition/models/models.py index ed818ac241b..d96613cb3be 100644 --- a/base_partition/models/models.py +++ b/base_partition/models/models.py @@ -1,7 +1,19 @@ # © 2020 Acsone (http://www.acsone.eu) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models +import fnmatch +from odoo import fields, models +from odoo.osv import expression + +LIKE_COMPARATORS = ( + 'like', + 'ilike', + '=like', + '=ilike', + 'not ilike', + 'not like', +) + class Base(models.AbstractModel): @@ -39,3 +51,109 @@ def partition(self, accessor): partition[key] += record return partition + + def filtered_domain(self, domain): + """Backport from standard. + """ + if not domain: + return self + result = [] + for d in reversed(domain): + if d == '|': + result.append(result.pop() | result.pop()) + elif d == '!': + result.append(self - result.pop()) + elif d == '&': + result.append(result.pop() & result.pop()) + elif d == expression.TRUE_LEAF: + result.append(self) + elif d == expression.FALSE_LEAF: + result.append(self.browse()) + else: + (key, comparator, value) = d + if key.endswith('.id'): + key = key[:-3] + if key == 'id': + key = '' + # determine the field with the final type for values + field = None + if key: + model = self.browse() + for fname in key.split('.'): + field = model._fields[fname] + model = model[fname] + + if comparator in LIKE_COMPARATORS: + value_esc = ( + value.replace('_', '?') + .replace('%', '*') + .replace('[', '?') + ) + records = self.browse() + for rec in self: + data = rec.mapped(key) + if comparator in ('child_of', 'parent_of'): + records = data.search([(data._parent_name, comparator, value)]) + value = records.ids + comparator = 'in' + if isinstance(data, models.BaseModel): + v = value + if isinstance(value, (list, tuple)) and len(value): + v = value[0] + if isinstance(v, str): + data = data.mapped('display_name') + else: + data = data.ids if data else [False] + elif field and field.type in ('date', 'datetime'): + # convert all date and datetime values to datetime + normalize = fields.Datetime.to_datetime + if isinstance(value, (list, tuple)): + value = [normalize(v) for v in value] + else: + value = normalize(value) + data = [normalize(d) for d in data] + if comparator in ('in', 'not in'): + if not (isinstance(value, list) or isinstance(value, tuple)): + value = [value] + + if comparator == '=': + ok = value in data + elif comparator == 'in': + ok = any(map(lambda x: x in data, value)) + elif comparator == '<': + ok = any(map(lambda x: x is not None and x < value, data)) + elif comparator == '>': + ok = any(map(lambda x: x is not None and x > value, data)) + elif comparator == '<=': + ok = any(map(lambda x: x is not None and x <= value, data)) + elif comparator == '>=': + ok = any(map(lambda x: x is not None and x >= value, data)) + elif comparator in ('!=', '<>'): + ok = value not in data + elif comparator == 'not in': + ok = all(map(lambda x: x not in data, value)) + elif comparator == 'not ilike': + ok = all(map(lambda x: value.lower() not in x.lower(), data)) + elif comparator == 'ilike': + data = [x.lower() for x in data] + match = fnmatch.filter(data, '*'+(value_esc or '').lower()+'*') + ok = bool(match) + elif comparator == 'not like': + ok = all(map(lambda x: value not in x, data)) + elif comparator == 'like': + ok = bool(fnmatch.filter(data, value and '*'+value_esc+'*')) + elif comparator == '=?': + ok = (value in data) or not value + elif comparator == '=like': + ok = bool(fnmatch.filter(data, value_esc)) + elif comparator == '=ilike': + data = [x.lower() for x in data] + ok = bool(fnmatch.filter(data, value and value_esc.lower())) + else: + raise ValueError + if ok: + records |= rec + result.append(records) + while len(result) > 1: + result.append(result.pop() & result.pop()) + return result[0] diff --git a/base_partition/tests/test_partition.py b/base_partition/tests/test_partition.py index 942521db760..f8146d6fb2c 100644 --- a/base_partition/tests/test_partition.py +++ b/base_partition/tests/test_partition.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import functools + from odoo.tests.common import TransactionCase @@ -14,33 +15,39 @@ def setUp(self): self.c2 = self.Category.create({"name": "c2"}) self.c3 = self.Category.create({"name": "c3"}) - self.Partner = self.env['res.partner'] + self.Partner = self.env["res.partner"] self.parent1 = self.Partner.create({"name": "parent1"}) self.parent2 = self.Partner.create({"name": "parent2"}) self.child1 = self.Partner.create({"name": "child1"}) self.child2 = self.Partner.create({"name": "child2"}) self.child3 = self.Partner.create({"name": "child3"}) - self.x = self.Partner.create({ - "name": "x", - "customer": True, - "category_id": [(6, 0, [self.c1.id, self.c2.id])], - "child_ids": [(6, 0, [self.child1.id, self.child2.id])], - "parent_id": self.parent1.id, - }) - self.y = self.Partner.create({ - "name": "y", - "customer": False, - "category_id": [(6, 0, [self.c2.id, self.c3.id])], - "child_ids": [(6, 0, [self.child2.id, self.child3.id])], - "parent_id": self.parent2.id, - }) - self.z = self.Partner.create({ - "name": "z", - "customer": False, - "category_id": [(6, 0, [self.c1.id, self.c3.id])], - "child_ids": [(6, 0, [self.child1.id, self.child3.id])], - "parent_id": self.parent2.id, - }) + self.x = self.Partner.create( + { + "name": "x", + "customer": True, + "category_id": [(6, 0, [self.c1.id, self.c2.id])], + "child_ids": [(6, 0, [self.child1.id, self.child2.id])], + "parent_id": self.parent1.id, + } + ) + self.y = self.Partner.create( + { + "name": "y", + "customer": False, + "category_id": [(6, 0, [self.c2.id, self.c3.id])], + "child_ids": [(6, 0, [self.child2.id, self.child3.id])], + "parent_id": self.parent2.id, + } + ) + self.z = self.Partner.create( + { + "name": "z", + "customer": False, + "category_id": [(6, 0, [self.c1.id, self.c3.id])], + "child_ids": [(6, 0, [self.child1.id, self.child3.id])], + "parent_id": self.parent2.id, + } + ) self.xyz = self.x + self.y + self.z def test_partition_many2many(self): @@ -78,3 +85,60 @@ def partition_field_test(self, field_name, relational=True, dotdot=False): records = functools.reduce(sum, partition.values()) self.assertEqual(self.xyz, records) # we get the same recordset + + def test_filtered_domain(self): + """Initially yo satisfy the coverage tools, this test actually documents + a number of pitfalls of filtered_domain and the differences with a search. + Commented examples would cause warnings, and even though these are edge-cases + these behaviours should be known. + """ + + records = self.xyz + empty_recordset = records.browse() + + def filtered_search(domain): + search = self.xyz.search(domain) + return search.filtered(lambda r: r.id in self.xyz.ids) + + self.assertEqual(records, records.filtered_domain([])) + self.assertEqual(empty_recordset, records.filtered_domain([(0, "=", 1)])) + + for field in ["name"]: + for r in self.xyz: + domain = [(field, "=", r[field])] + self.assertEqual(self.xyz.filtered_domain(domain), r) + self.assertEqual(filtered_search(domain), r) + + domain = [(field, "in", r[field])] + self.assertTrue(self.xyz.filtered_domain(domain), r) + with self.assertRaises(ValueError): + filtered_search(domain) + + for field in ["customer"]: + for r in [self.x, self.y | self.z]: + value = r[0][field] + domain = [(field, "=", value)] + self.assertEqual(self.xyz.filtered_domain(domain), r) + self.assertEqual(filtered_search(domain), r) + # domain = [(field, "in", value)] + # self.assertEqual(self.xyz.filtered_domain(domain), r) + # expected_result = r if value else empty_recordset # ! + # self.assertEqual(filtered_search(domain), expected_result) + + for field in ["parent_id"]: + for r in [self.x, self.y | self.z]: + domain = [(field, "=", r[0][field].id)] + self.assertEqual(self.xyz.filtered_domain(domain), r) + self.assertEqual(filtered_search(domain), r) + domain = [(field, "in", r[0][field].ids)] + self.assertEqual(self.xyz.filtered_domain(domain), r) + self.assertEqual(filtered_search(domain), r) + + for r in self.xyz: + field = "category_id" + in_domain = [(field, "in", r[field].ids)] + self.assertEqual(self.xyz.filtered_domain(in_domain), self.xyz) + self.assertEqual(self.xyz.search(in_domain), self.xyz) + # eq_domain = [(field, "=", r[field].ids)] + # self.assertEqual(self.xyz.search(eq_domain), self.xyz) + # self.assertEqual(self.xyz.filtered_domain(eq_domain), empty_recordset) From a4315a4401fd7c0bfcfbef21fddb7c6d4b2caf2b Mon Sep 17 00:00:00 2001 From: nans Date: Tue, 8 Sep 2020 20:17:41 +0200 Subject: [PATCH 123/770] [IMP] base_partition: add batch method on base --- base_partition/models/models.py | 95 +++++++++++++++----------- base_partition/tests/test_partition.py | 30 ++++++++ 2 files changed, 85 insertions(+), 40 deletions(-) diff --git a/base_partition/models/models.py b/base_partition/models/models.py index d96613cb3be..8100969cded 100644 --- a/base_partition/models/models.py +++ b/base_partition/models/models.py @@ -2,22 +2,24 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import fnmatch -from odoo import fields, models + +from odoo import _, fields, models +from odoo.exceptions import UserError from odoo.osv import expression LIKE_COMPARATORS = ( - 'like', - 'ilike', - '=like', - '=ilike', - 'not ilike', - 'not like', + "like", + "ilike", + "=like", + "=ilike", + "not ilike", + "not like", ) class Base(models.AbstractModel): - _inherit = 'base' + _inherit = "base" def partition(self, accessor): """Returns a dictionary forming a partition of self into a dictionary @@ -52,6 +54,19 @@ def partition(self, accessor): return partition + def batch(self, batch_size=None): + """Yield successive batches of size batch_size, or .""" + if not (batch_size or "_default_batch_size" in dir(self)): + raise UserError( + _( + "Either set up a '_default_batch_size' on the model" + " or provide a batch_size parameter." + ) + ) + batch_size = batch_size or self._default_batch_size + for i in range(0, len(self), batch_size): + yield self[i : i + batch_size] + def filtered_domain(self, domain): """Backport from standard. """ @@ -59,11 +74,11 @@ def filtered_domain(self, domain): return self result = [] for d in reversed(domain): - if d == '|': + if d == "|": result.append(result.pop() | result.pop()) - elif d == '!': + elif d == "!": result.append(self - result.pop()) - elif d == '&': + elif d == "&": result.append(result.pop() & result.pop()) elif d == expression.TRUE_LEAF: result.append(self) @@ -71,40 +86,38 @@ def filtered_domain(self, domain): result.append(self.browse()) else: (key, comparator, value) = d - if key.endswith('.id'): + if key.endswith(".id"): key = key[:-3] - if key == 'id': - key = '' + if key == "id": + key = "" # determine the field with the final type for values field = None if key: model = self.browse() - for fname in key.split('.'): + for fname in key.split("."): field = model._fields[fname] model = model[fname] if comparator in LIKE_COMPARATORS: value_esc = ( - value.replace('_', '?') - .replace('%', '*') - .replace('[', '?') + value.replace("_", "?").replace("%", "*").replace("[", "?") ) records = self.browse() for rec in self: data = rec.mapped(key) - if comparator in ('child_of', 'parent_of'): + if comparator in ("child_of", "parent_of"): records = data.search([(data._parent_name, comparator, value)]) value = records.ids - comparator = 'in' + comparator = "in" if isinstance(data, models.BaseModel): v = value if isinstance(value, (list, tuple)) and len(value): v = value[0] if isinstance(v, str): - data = data.mapped('display_name') + data = data.mapped("display_name") else: data = data.ids if data else [False] - elif field and field.type in ('date', 'datetime'): + elif field and field.type in ("date", "datetime"): # convert all date and datetime values to datetime normalize = fields.Datetime.to_datetime if isinstance(value, (list, tuple)): @@ -112,41 +125,43 @@ def filtered_domain(self, domain): else: value = normalize(value) data = [normalize(d) for d in data] - if comparator in ('in', 'not in'): + if comparator in ("in", "not in"): if not (isinstance(value, list) or isinstance(value, tuple)): value = [value] - if comparator == '=': + if comparator == "=": ok = value in data - elif comparator == 'in': + elif comparator == "in": ok = any(map(lambda x: x in data, value)) - elif comparator == '<': + elif comparator == "<": ok = any(map(lambda x: x is not None and x < value, data)) - elif comparator == '>': + elif comparator == ">": ok = any(map(lambda x: x is not None and x > value, data)) - elif comparator == '<=': + elif comparator == "<=": ok = any(map(lambda x: x is not None and x <= value, data)) - elif comparator == '>=': + elif comparator == ">=": ok = any(map(lambda x: x is not None and x >= value, data)) - elif comparator in ('!=', '<>'): + elif comparator in ("!=", "<>"): ok = value not in data - elif comparator == 'not in': + elif comparator == "not in": ok = all(map(lambda x: x not in data, value)) - elif comparator == 'not ilike': + elif comparator == "not ilike": ok = all(map(lambda x: value.lower() not in x.lower(), data)) - elif comparator == 'ilike': + elif comparator == "ilike": data = [x.lower() for x in data] - match = fnmatch.filter(data, '*'+(value_esc or '').lower()+'*') + match = fnmatch.filter( + data, "*" + (value_esc or "").lower() + "*" + ) ok = bool(match) - elif comparator == 'not like': + elif comparator == "not like": ok = all(map(lambda x: value not in x, data)) - elif comparator == 'like': - ok = bool(fnmatch.filter(data, value and '*'+value_esc+'*')) - elif comparator == '=?': + elif comparator == "like": + ok = bool(fnmatch.filter(data, value and "*" + value_esc + "*")) + elif comparator == "=?": ok = (value in data) or not value - elif comparator == '=like': + elif comparator == "=like": ok = bool(fnmatch.filter(data, value_esc)) - elif comparator == '=ilike': + elif comparator == "=ilike": data = [x.lower() for x in data] ok = bool(fnmatch.filter(data, value and value_esc.lower())) else: diff --git a/base_partition/tests/test_partition.py b/base_partition/tests/test_partition.py index f8146d6fb2c..352ac1824dd 100644 --- a/base_partition/tests/test_partition.py +++ b/base_partition/tests/test_partition.py @@ -2,7 +2,9 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import functools +import math +from odoo.exceptions import UserError from odoo.tests.common import TransactionCase @@ -86,6 +88,34 @@ def partition_field_test(self, field_name, relational=True, dotdot=False): records = functools.reduce(sum, partition.values()) self.assertEqual(self.xyz, records) # we get the same recordset + def test_batch(self): + """The sum of all batches should be the original recordset; + an empty recordset should return no batch; + without a batch parameter, the model's _default_batch_size should be used. + """ + records = self.xyz + batch_size = 2 + + assert len(records) # only makes sense with nonempty recordset + batches = list(records.batch(batch_size)) + self.assertEqual(len(batches), math.ceil(len(records) / batch_size)) + for batch in batches[:-1]: + self.assertEqual(len(batch), batch_size) + last_batch_size = len(records) % batch_size or batch_size + self.assertEqual(len(batches[-1]), last_batch_size) + self.assertEqual(functools.reduce(sum, batches), records) + + empty_recordset = records.browse() + no_batches = list(empty_recordset.batch(batch_size)) + self.assertEqual(no_batches, []) + + with self.assertRaises(UserError): + list(records.batch()) + + records.__class__._default_batch_size = batch_size + batches_from_default = list(records.batch()) + self.assertEqual(batches_from_default, batches) + def test_filtered_domain(self): """Initially yo satisfy the coverage tools, this test actually documents a number of pitfalls of filtered_domain and the differences with a search. From 858643e887b4c69d62145b1a407e7225c8b35f7b Mon Sep 17 00:00:00 2001 From: nans Date: Sun, 11 Oct 2020 20:13:49 +0200 Subject: [PATCH 124/770] [IMP] base_partition: add read_per_record method --- base_partition/models/models.py | 8 ++++++++ base_partition/tests/test_partition.py | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/base_partition/models/models.py b/base_partition/models/models.py index 8100969cded..8e73f1d1c1f 100644 --- a/base_partition/models/models.py +++ b/base_partition/models/models.py @@ -67,6 +67,14 @@ def batch(self, batch_size=None): for i in range(0, len(self), batch_size): yield self[i : i + batch_size] + def read_per_record(self, fields=None, load="_classic_read"): + result = {} + data_list = self.read(fields=fields, load=load) + for d in data_list: + key = d.pop("id") + result[key] = d + return result + def filtered_domain(self, domain): """Backport from standard. """ diff --git a/base_partition/tests/test_partition.py b/base_partition/tests/test_partition.py index 352ac1824dd..031ed825c8d 100644 --- a/base_partition/tests/test_partition.py +++ b/base_partition/tests/test_partition.py @@ -116,6 +116,16 @@ def test_batch(self): batches_from_default = list(records.batch()) self.assertEqual(batches_from_default, batches) + def test_read_per_record(self): + categories = self.c1 | self.c2 | self.c3 + field_list = ["name"] + data = categories.read_per_record(field_list) + self.assertEqual(len(data), len(categories)) + for record in categories: + self.assertTrue(record.id in data) + record_data = data[record.id] + self.assertEqual(list(record_data.keys()), field_list) + def test_filtered_domain(self): """Initially yo satisfy the coverage tools, this test actually documents a number of pitfalls of filtered_domain and the differences with a search. From df1e578347c4a1cbfc26d5422ab3740d9c21ab35 Mon Sep 17 00:00:00 2001 From: nans Date: Sun, 11 Oct 2020 21:17:43 +0200 Subject: [PATCH 125/770] [TEST] base_partition: add coverage --- base_partition/__manifest__.py | 2 +- base_partition/tests/test_partition.py | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/base_partition/__manifest__.py b/base_partition/__manifest__.py index fcdebbaa002..a68fdf91b7a 100644 --- a/base_partition/__manifest__.py +++ b/base_partition/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Base Partition", "summary": "Base module that provide the partition method on all models", - "version": "12.0.0.0.0", + "version": "12.0.1.0.0", "category": "Uncategorized", "website": "https://github.com/OCA/server-tools", "author": "Acsone, Odoo Community Association (OCA)", diff --git a/base_partition/tests/test_partition.py b/base_partition/tests/test_partition.py index 031ed825c8d..1fcb720c8c1 100644 --- a/base_partition/tests/test_partition.py +++ b/base_partition/tests/test_partition.py @@ -3,7 +3,9 @@ import functools import math +from datetime import datetime +from odoo import fields from odoo.exceptions import UserError from odoo.tests.common import TransactionCase @@ -88,6 +90,11 @@ def partition_field_test(self, field_name, relational=True, dotdot=False): records = functools.reduce(sum, partition.values()) self.assertEqual(self.xyz, records) # we get the same recordset + def test_partition_lambda(self): + """Test an arbitrary predicate.""" + partition = (self.c1 | self.c2).partition(lambda c: "2" in c.name) + self.assertEqual(set(partition.keys()), {True, False}) + def test_batch(self): """The sum of all batches should be the original recordset; an empty recordset should return no batch; @@ -127,7 +134,7 @@ def test_read_per_record(self): self.assertEqual(list(record_data.keys()), field_list) def test_filtered_domain(self): - """Initially yo satisfy the coverage tools, this test actually documents + """Initially to satisfy the coverage tools, this test actually documents a number of pitfalls of filtered_domain and the differences with a search. Commented examples would cause warnings, and even though these are edge-cases these behaviours should be known. @@ -182,3 +189,17 @@ def filtered_search(domain): # eq_domain = [(field, "=", r[field].ids)] # self.assertEqual(self.xyz.search(eq_domain), self.xyz) # self.assertEqual(self.xyz.filtered_domain(eq_domain), empty_recordset) + + # coverage + records.filtered_domain(["!", (1, "=", 1)]) + for operator in ["<", ">", "<=", ">="]: + date_now_str = fields.Datetime.to_string(datetime.now()) + records.filtered_domain([("create_date", operator, date_now_str)]) + for operator in ["!=", "like", "not like", "=?", "ilike", "=like", "not ilike"]: + records.filtered_domain([("name", operator, "c")]) + records.filtered_domain( + ["&", ("parent_id.id", "in", [self.parent1.id]), (1, "=", 1)] + ) + records.filtered_domain(["|", ("parent_id", "child_of", [42]), (0, "=", 1)]) + with self.assertRaises(ValueError): + records.filtered_domain([("name", "===", "test")]) From 1303cdac38169e860ab6bbdac4f267322e83563c Mon Sep 17 00:00:00 2001 From: hda Date: Mon, 24 Apr 2023 17:00:45 +0200 Subject: [PATCH 126/770] [16.0][MIG] base_partition --- base_partition/README.rst | 26 ++-- base_partition/__manifest__.py | 8 +- base_partition/i18n/base_partition.pot | 18 ++- base_partition/i18n/fr_BE.pot | 32 ++++ base_partition/models/models.py | 140 ++---------------- base_partition/readme/CONTRIBUTORS.rst | 1 + base_partition/readme/DESCRIPTION.rst | 3 - base_partition/static/description/index.html | 13 +- base_partition/tests/test_partition.py | 147 +++++-------------- 9 files changed, 117 insertions(+), 271 deletions(-) create mode 100644 base_partition/i18n/fr_BE.pot diff --git a/base_partition/README.rst b/base_partition/README.rst index 118427deaf0..d66f63daa15 100644 --- a/base_partition/README.rst +++ b/base_partition/README.rst @@ -10,18 +10,18 @@ Base Partition .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/12.0/base_partition + :target: https://github.com/OCA/server-tools/tree/16.0/base_partition :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-base_partition + :target: https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-base_partition :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/149/12.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/webui/builds.html?repo=OCA/server-tools&target_branch=16.0 + :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -35,9 +35,6 @@ and with values that are recordsets So if we have a recordset (x | y | z ) such that x.f == True, y.f == z.f == False, then (x | y | z ).partition("f") == {True: x, False: (y | z)}. -It also provides a backport of `filtered_domain`, -which filters a recordset in place with a provided domain. - **Table of contents** .. contents:: @@ -49,7 +46,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -59,12 +56,13 @@ Credits Authors ~~~~~~~ -* Acsone +* Acsone SA/NV Contributors ~~~~~~~~~~~~ * Nans Lefebvre +* Hughes Damry Maintainers ~~~~~~~~~~~ @@ -79,6 +77,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_partition/__manifest__.py b/base_partition/__manifest__.py index a68fdf91b7a..9fa23c69567 100644 --- a/base_partition/__manifest__.py +++ b/base_partition/__manifest__.py @@ -1,15 +1,15 @@ # Copyright 2020 Acsone (http://www.acsone.eu) # Nans Lefebvre -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Base Partition", "summary": "Base module that provide the partition method on all models", - "version": "12.0.1.0.0", + "version": "16.0.1.0.0", "category": "Uncategorized", "website": "https://github.com/OCA/server-tools", - "author": "Acsone, Odoo Community Association (OCA)", - "license": "AGPL-3", + "author": "Acsone SA/NV, Odoo Community Association (OCA)", + "license": "LGPL-3", "installable": True, "depends": ["base"], } diff --git a/base_partition/i18n/base_partition.pot b/base_partition/i18n/base_partition.pot index f6210522614..ab5717e74b4 100644 --- a/base_partition/i18n/base_partition.pot +++ b/base_partition/i18n/base_partition.pot @@ -1,14 +1,14 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * base_partition +# * base_partition # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 16.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-04 13:29+0000\n" -"PO-Revision-Date: 2020-09-04 13:29+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2023-04-24 15:38+0000\n" +"PO-Revision-Date: 2023-04-24 15:38+0000\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,3 +20,11 @@ msgstr "" msgid "Base" msgstr "" +#. module: base_partition +#. odoo-python +#: code:addons/base_partition/models/models.py:0 +#, python-format +msgid "" +"Either set up a '_default_batch_size' on the model or provide a batch_size " +"parameter." +msgstr "" diff --git a/base_partition/i18n/fr_BE.pot b/base_partition/i18n/fr_BE.pot new file mode 100644 index 00000000000..4befdf2a7bb --- /dev/null +++ b/base_partition/i18n/fr_BE.pot @@ -0,0 +1,32 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_partition +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-04-24 15:38+0000\n" +"PO-Revision-Date: 2023-04-24 15:38+0000\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_partition +#: model:ir.model,name:base_partition.model_base +msgid "Base" +msgstr "" + +#. module: base_partition +#. odoo-python +#: code:addons/base_partition/models/models.py:0 +#, python-format +msgid "" +"Either set up a '_default_batch_size' on the model or provide a batch_size " +"parameter." +msgstr "" +"Définir '_default_batch_size' sur le modèle ou fournir une valeur de " +"batch_size en paramètre." diff --git a/base_partition/models/models.py b/base_partition/models/models.py index 8e73f1d1c1f..4adf275cec4 100644 --- a/base_partition/models/models.py +++ b/base_partition/models/models.py @@ -1,20 +1,8 @@ # © 2020 Acsone (http://www.acsone.eu) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). -import fnmatch - -from odoo import _, fields, models +from odoo import _, models from odoo.exceptions import UserError -from odoo.osv import expression - -LIKE_COMPARATORS = ( - "like", - "ilike", - "=like", - "=ilike", - "not ilike", - "not like", -) class Base(models.AbstractModel): @@ -23,15 +11,15 @@ class Base(models.AbstractModel): def partition(self, accessor): """Returns a dictionary forming a partition of self into a dictionary - value/recordset for each value obtained from the accessor. - The accessor itself can be either a string that can be passed to mapped, - or an arbitrary function. - Note that it is always at least as fast to pass a function, - hence the current implementation. - If we have a 'field.subfield' accessor such that subfield is not a relational - then the result is a list (not hashable). Then the str(key) are used. - In the general case a value could both not be hashable nor stringifiable, - in a which case this function would crash. + value/recordset for each value obtained from the accessor. + The accessor itself can be either a string that can be passed to mapped, + or an arbitrary function. + Note that it is always at least as fast to pass a function, + hence the current implementation. + If we have a 'field.subfield' accessor such that subfield is not a relational + then the result is a list (not hashable). Then the str(key) are used. + In the general case a value could both not be hashable nor stringifiable, + in a which case this function would crash. """ partition = {} @@ -74,109 +62,3 @@ def read_per_record(self, fields=None, load="_classic_read"): key = d.pop("id") result[key] = d return result - - def filtered_domain(self, domain): - """Backport from standard. - """ - if not domain: - return self - result = [] - for d in reversed(domain): - if d == "|": - result.append(result.pop() | result.pop()) - elif d == "!": - result.append(self - result.pop()) - elif d == "&": - result.append(result.pop() & result.pop()) - elif d == expression.TRUE_LEAF: - result.append(self) - elif d == expression.FALSE_LEAF: - result.append(self.browse()) - else: - (key, comparator, value) = d - if key.endswith(".id"): - key = key[:-3] - if key == "id": - key = "" - # determine the field with the final type for values - field = None - if key: - model = self.browse() - for fname in key.split("."): - field = model._fields[fname] - model = model[fname] - - if comparator in LIKE_COMPARATORS: - value_esc = ( - value.replace("_", "?").replace("%", "*").replace("[", "?") - ) - records = self.browse() - for rec in self: - data = rec.mapped(key) - if comparator in ("child_of", "parent_of"): - records = data.search([(data._parent_name, comparator, value)]) - value = records.ids - comparator = "in" - if isinstance(data, models.BaseModel): - v = value - if isinstance(value, (list, tuple)) and len(value): - v = value[0] - if isinstance(v, str): - data = data.mapped("display_name") - else: - data = data.ids if data else [False] - elif field and field.type in ("date", "datetime"): - # convert all date and datetime values to datetime - normalize = fields.Datetime.to_datetime - if isinstance(value, (list, tuple)): - value = [normalize(v) for v in value] - else: - value = normalize(value) - data = [normalize(d) for d in data] - if comparator in ("in", "not in"): - if not (isinstance(value, list) or isinstance(value, tuple)): - value = [value] - - if comparator == "=": - ok = value in data - elif comparator == "in": - ok = any(map(lambda x: x in data, value)) - elif comparator == "<": - ok = any(map(lambda x: x is not None and x < value, data)) - elif comparator == ">": - ok = any(map(lambda x: x is not None and x > value, data)) - elif comparator == "<=": - ok = any(map(lambda x: x is not None and x <= value, data)) - elif comparator == ">=": - ok = any(map(lambda x: x is not None and x >= value, data)) - elif comparator in ("!=", "<>"): - ok = value not in data - elif comparator == "not in": - ok = all(map(lambda x: x not in data, value)) - elif comparator == "not ilike": - ok = all(map(lambda x: value.lower() not in x.lower(), data)) - elif comparator == "ilike": - data = [x.lower() for x in data] - match = fnmatch.filter( - data, "*" + (value_esc or "").lower() + "*" - ) - ok = bool(match) - elif comparator == "not like": - ok = all(map(lambda x: value not in x, data)) - elif comparator == "like": - ok = bool(fnmatch.filter(data, value and "*" + value_esc + "*")) - elif comparator == "=?": - ok = (value in data) or not value - elif comparator == "=like": - ok = bool(fnmatch.filter(data, value_esc)) - elif comparator == "=ilike": - data = [x.lower() for x in data] - ok = bool(fnmatch.filter(data, value and value_esc.lower())) - else: - raise ValueError - if ok: - records |= rec - result.append(records) - while len(result) > 1: - result.append(result.pop() & result.pop()) - return result[0] diff --git a/base_partition/readme/CONTRIBUTORS.rst b/base_partition/readme/CONTRIBUTORS.rst index 4e30b28db7a..e2702f0485c 100644 --- a/base_partition/readme/CONTRIBUTORS.rst +++ b/base_partition/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Nans Lefebvre +* Hughes Damry diff --git a/base_partition/readme/DESCRIPTION.rst b/base_partition/readme/DESCRIPTION.rst index 7b96e6257a1..bd33c61d613 100644 --- a/base_partition/readme/DESCRIPTION.rst +++ b/base_partition/readme/DESCRIPTION.rst @@ -7,6 +7,3 @@ and with values that are recordsets So if we have a recordset (x | y | z ) such that x.f == True, y.f == z.f == False, then (x | y | z ).partition("f") == {True: x, False: (y | z)}. - -It also provides a backport of `filtered_domain`, -which filters a recordset in place with a provided domain. diff --git a/base_partition/static/description/index.html b/base_partition/static/description/index.html index 9b0627c6879..c65a625bfef 100644 --- a/base_partition/static/description/index.html +++ b/base_partition/static/description/index.html @@ -3,7 +3,7 @@ - + Base Partition -
    -

    Base Partition

    +
    + + +Odoo Community Association + +
    +

    Base Partition

    -

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

    +

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

    This module adds a partition(self, accessor) method to every model. It accepts for accessor any parameter that would be accepted by mapped, i.e. a string “field(.subfield)*” or a function (lambda x: not x.b). It @@ -392,30 +397,30 @@

    Base Partition

-

Bug Tracker

+

Bug Tracker

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

+feedback.

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Acsone SA/NV
-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -423,10 +428,11 @@

Maintainers

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

-

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

+

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

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

+ From d6b7343c21f1b56ee88a2a6a91bb53651be79f3b Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Sun, 18 Dec 2016 06:51:27 +0100 Subject: [PATCH 145/770] [ADD] base_view_inheritance_extension --- base_view_inheritance_extension/README.rst | 83 ++++++++++++ base_view_inheritance_extension/__init__.py | 4 + .../__openerp__.py | 17 +++ .../demo/ir_ui_view.xml | 22 ++++ .../models/__init__.py | 4 + .../models/ir_ui_view.py | 124 ++++++++++++++++++ .../static/description/icon.png | Bin 0 -> 9455 bytes .../tests/__init__.py | 4 + .../test_base_view_inheritance_extension.py | 30 +++++ 9 files changed, 288 insertions(+) create mode 100644 base_view_inheritance_extension/README.rst create mode 100644 base_view_inheritance_extension/__init__.py create mode 100644 base_view_inheritance_extension/__openerp__.py create mode 100644 base_view_inheritance_extension/demo/ir_ui_view.xml create mode 100644 base_view_inheritance_extension/models/__init__.py create mode 100644 base_view_inheritance_extension/models/ir_ui_view.py create mode 100644 base_view_inheritance_extension/static/description/icon.png create mode 100644 base_view_inheritance_extension/tests/__init__.py create mode 100644 base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst new file mode 100644 index 00000000000..ddfb8bafe4f --- /dev/null +++ b/base_view_inheritance_extension/README.rst @@ -0,0 +1,83 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +========================= +Extended view inheritance +========================= + +This module was written to make it simple to add custom operators for view inheritance. + +Usage +===== + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/149/8.0 + +Change a python dictionary (context for example) +------------------------------------------------ + +.. code-block:: xml + + + $new_value + + +Note that views are subject to evaluation of xmlids anyways, so if you need to refer to some xmlid, say ``%(xmlid)s``. + +Move an element in the view +--------------------------- + +.. code-block:: xml + + + +This can also be used to wrap some element into another, create the target element first, then move the node youwant to wrap there. + +Known issues / Roadmap +====================== + +* add ``$value`` +* add ``$index`` +* add ``$value`` +* support ```` +* support an ``eval`` attribute for our new node types + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Holger Brunn + +Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list `_ or the `appropriate specialized mailinglist `_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues. + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/base_view_inheritance_extension/__init__.py b/base_view_inheritance_extension/__init__.py new file mode 100644 index 00000000000..7eda98a232c --- /dev/null +++ b/base_view_inheritance_extension/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# © 2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/base_view_inheritance_extension/__openerp__.py b/base_view_inheritance_extension/__openerp__.py new file mode 100644 index 00000000000..bec0cfdfee0 --- /dev/null +++ b/base_view_inheritance_extension/__openerp__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# © 2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Extended view inheritance", + "version": "8.0.1.0.0", + "author": "Therp BV,Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Hidden/Dependency", + "summary": "Adds more operators for view inheritance", + "depends": [ + 'base', + ], + "demo": [ + "demo/ir_ui_view.xml", + ], +} diff --git a/base_view_inheritance_extension/demo/ir_ui_view.xml b/base_view_inheritance_extension/demo/ir_ui_view.xml new file mode 100644 index 00000000000..648a19cb558 --- /dev/null +++ b/base_view_inheritance_extension/demo/ir_ui_view.xml @@ -0,0 +1,22 @@ + + + + + res.partner + + + + Partner form + + + 'The company name' + context.get('company_id', context.get('company')) + + + + + + + + + diff --git a/base_view_inheritance_extension/models/__init__.py b/base_view_inheritance_extension/models/__init__.py new file mode 100644 index 00000000000..7e711cb1820 --- /dev/null +++ b/base_view_inheritance_extension/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# © 2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import ir_ui_view diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py new file mode 100644 index 00000000000..ea227a5287b --- /dev/null +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# © 2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from lxml import etree +from openerp import api, models, tools + + +class UnquoteObject(str): + def __getattr__(self, name): + return UnquoteObject('%s.%s' % (self, name)) + + def __repr__(self): + return self + + def __call__(self, *args, **kwargs): + return UnquoteObject( + '%s(%s)' % ( + self, + ','.join( + [ + UnquoteObject( + a if not isinstance(a, basestring) + else "'%s'" % a + ) + for a in args + ] + + [ + '%s=%s' % (UnquoteObject(k), v) + for (k, v) in kwargs.iteritems() + ] + ) + ) + ) + + +class UnquoteEvalObjectContext(tools.misc.UnquoteEvalContext): + def __missing__(self, key): + return UnquoteObject(key) + + +class IrUiView(models.Model): + _inherit = 'ir.ui.view' + + @api.model + def apply_inheritance_specs(self, source, specs_tree, inherit_id): + for specs, handled_by in self._iter_inheritance_specs(specs_tree): + source = handled_by(source, specs, inherit_id) + return source + + @api.model + def _iter_inheritance_specs(self, spec): + if spec.tag == 'data': + for child in spec: + for node, handler in self._iter_inheritance_specs(child): + yield node, handler + return + if spec.get('position') == 'attributes': + for child in spec: + node = etree.Element(spec.tag, **spec.attrib) + node.insert(0, child) + yield node, self._get_inheritance_handler_attributes( + child + ) + return + yield spec, self._get_inheritance_handler(spec) + + @api.model + def _get_inheritance_handler(self, node): + handler = super(IrUiView, self).apply_inheritance_specs + if hasattr( + self, 'inheritance_handler_%s' % node.tag + ): + handler = getattr( + self, + 'inheritance_handler_%s' % node.tag + ) + return handler + + @api.model + def _get_inheritance_handler_attributes(self, node): + handler = super(IrUiView, self).apply_inheritance_specs + if hasattr( + self, 'inheritance_handler_attributes_%s' % node.get('operation') + ): + handler = getattr( + self, + 'inheritance_handler_attributes_%s' % node.get('operation') + ) + return handler + + @api.model + def inheritance_handler_attributes_python_dict( + self, source, specs, inherit_id + ): + """Implement + <$node position="attributes"> + + $keyvalue + + """ + node = self.locate_node(source, specs) + for attribute_node in specs: + python_dict = tools.safe_eval( + node.get(attribute_node.get('name')) or '{}', + UnquoteEvalObjectContext() + ) + python_dict[attribute_node.get('key')] = UnquoteObject( + attribute_node.text + ) + node.attrib[attribute_node.get('name')] = str(python_dict) + return source + + @api.model + def inheritance_handler_xpath(self, source, specs, inherit_id): + if not specs.get('position') == 'move': + return super(IrUiView, self).apply_inheritance_specs( + source, specs, inherit_id + ) + node = self.locate_node(source, specs) + target_node = self.locate_node( + source, etree.Element(specs.tag, expr=specs.get('target')) + ) + target_node.append(node) + return source diff --git a/base_view_inheritance_extension/static/description/icon.png b/base_view_inheritance_extension/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/base_view_inheritance_extension/tests/__init__.py b/base_view_inheritance_extension/tests/__init__.py new file mode 100644 index 00000000000..94db4e53b9d --- /dev/null +++ b/base_view_inheritance_extension/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# © 2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import test_base_view_inheritance_extension diff --git a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py new file mode 100644 index 00000000000..f40045590f6 --- /dev/null +++ b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# © 2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from lxml import etree +from openerp.tests.common import TransactionCase + + +class TestBaseViewInheritanceExtension(TransactionCase): + def test_base_view_inheritance_extension(self): + view_id = self.env.ref('base.view_partner_form').id + fields_view_get = self.env['res.partner'].fields_view_get( + view_id=view_id + ) + view = etree.fromstring(fields_view_get['arch']) + # verify normal attributes work + self.assertEqual(view.xpath('//form')[0].get('string'), 'Partner form') + # verify our extra context key worked + self.assertTrue( + 'default_name' in + view.xpath('//field[@name="parent_id"]')[0].get('context') + ) + self.assertTrue( + "context.get('company_id', context.get('company'))" in + view.xpath('//field[@name="parent_id"]')[0].get('context') + ) + # verify we moved the child_ids field + self.assertEqual( + view.xpath('//field[@name="child_ids"]')[0].getparent(), + view.xpath('//page[@name="my_new_page"]')[0] + ) From ff462688811cf5f122e52267ae540da809efd9ba Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 23 Dec 2016 11:37:09 +0100 Subject: [PATCH 146/770] [base_view_inheritance_extension] Relicense to LGPL. (#4) --- base_view_inheritance_extension/README.rst | 6 +++--- base_view_inheritance_extension/__init__.py | 2 +- base_view_inheritance_extension/__openerp__.py | 4 ++-- base_view_inheritance_extension/models/__init__.py | 2 +- base_view_inheritance_extension/models/ir_ui_view.py | 2 +- base_view_inheritance_extension/tests/__init__.py | 2 +- .../tests/test_base_view_inheritance_extension.py | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst index ddfb8bafe4f..e0186c4bb33 100644 --- a/base_view_inheritance_extension/README.rst +++ b/base_view_inheritance_extension/README.rst @@ -1,6 +1,6 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 +.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 ========================= Extended view inheritance diff --git a/base_view_inheritance_extension/__init__.py b/base_view_inheritance_extension/__init__.py index 7eda98a232c..4431c72c42a 100644 --- a/base_view_inheritance_extension/__init__.py +++ b/base_view_inheritance_extension/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- # © 2016 Therp BV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import models diff --git a/base_view_inheritance_extension/__openerp__.py b/base_view_inheritance_extension/__openerp__.py index bec0cfdfee0..1255e401388 100644 --- a/base_view_inheritance_extension/__openerp__.py +++ b/base_view_inheritance_extension/__openerp__.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- # © 2016 Therp BV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", "version": "8.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", - "license": "AGPL-3", + "license": "LGPL-3", "category": "Hidden/Dependency", "summary": "Adds more operators for view inheritance", "depends": [ diff --git a/base_view_inheritance_extension/models/__init__.py b/base_view_inheritance_extension/models/__init__.py index 7e711cb1820..588d249e27c 100644 --- a/base_view_inheritance_extension/models/__init__.py +++ b/base_view_inheritance_extension/models/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- # © 2016 Therp BV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import ir_ui_view diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index ea227a5287b..75222007994 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # © 2016 Therp BV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from lxml import etree from openerp import api, models, tools diff --git a/base_view_inheritance_extension/tests/__init__.py b/base_view_inheritance_extension/tests/__init__.py index 94db4e53b9d..f76aba46a08 100644 --- a/base_view_inheritance_extension/tests/__init__.py +++ b/base_view_inheritance_extension/tests/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- # © 2016 Therp BV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import test_base_view_inheritance_extension diff --git a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py index f40045590f6..2429b4f6fd4 100644 --- a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py +++ b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # © 2016 Therp BV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from lxml import etree from openerp.tests.common import TransactionCase From bd2fe3359f278f97c645a2fc524097b798c352b1 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Fri, 23 Dec 2016 22:52:19 +0100 Subject: [PATCH 147/770] [MIG] base_view_inheritance_extension: Migration to 9.0 Trivial changes: * Version in README changed * Version in manifest changed OCA Transbot updated translations from Transifex OCA Transbot updated translations from Transifex --- base_view_inheritance_extension/README.rst | 2 +- .../__openerp__.py | 2 +- base_view_inheritance_extension/i18n/de.po | 34 +++++++++++++++++++ base_view_inheritance_extension/i18n/sl.po | 34 +++++++++++++++++++ base_view_inheritance_extension/i18n/tr.po | 34 +++++++++++++++++++ 5 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 base_view_inheritance_extension/i18n/de.po create mode 100644 base_view_inheritance_extension/i18n/sl.po create mode 100644 base_view_inheritance_extension/i18n/tr.po diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst index e0186c4bb33..f5eaa8ba6f9 100644 --- a/base_view_inheritance_extension/README.rst +++ b/base_view_inheritance_extension/README.rst @@ -13,7 +13,7 @@ Usage .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/149/8.0 + :target: https://runbot.odoo-community.org/runbot/149/9.0 Change a python dictionary (context for example) ------------------------------------------------ diff --git a/base_view_inheritance_extension/__openerp__.py b/base_view_inheritance_extension/__openerp__.py index 1255e401388..d4304a54081 100644 --- a/base_view_inheritance_extension/__openerp__.py +++ b/base_view_inheritance_extension/__openerp__.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", - "version": "8.0.1.0.0", + "version": "9.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Hidden/Dependency", diff --git a/base_view_inheritance_extension/i18n/de.po b/base_view_inheritance_extension/i18n/de.po new file mode 100644 index 00000000000..c9e0e1ffa89 --- /dev/null +++ b/base_view_inheritance_extension/i18n/de.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +# Translators: +# Rudolf Schnapka , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-03-04 07:39+0000\n" +"PO-Revision-Date: 2017-03-04 07:39+0000\n" +"Last-Translator: Rudolf Schnapka , 2017\n" +"Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "A new page" +msgstr "Eine neue Seite" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "Partner form" +msgstr "Partner-Formular" + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "" diff --git a/base_view_inheritance_extension/i18n/sl.po b/base_view_inheritance_extension/i18n/sl.po new file mode 100644 index 00000000000..1071aca9dc3 --- /dev/null +++ b/base_view_inheritance_extension/i18n/sl.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +# Translators: +# Matjaž Mozetič , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-12-29 03:39+0000\n" +"PO-Revision-Date: 2016-12-29 03:39+0000\n" +"Last-Translator: Matjaž Mozetič , 2016\n" +"Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: sl\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_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "A new page" +msgstr "Nova stran" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "Partner form" +msgstr "Partnerjev obrazec" + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "" diff --git a/base_view_inheritance_extension/i18n/tr.po b/base_view_inheritance_extension/i18n/tr.po new file mode 100644 index 00000000000..77e748e28e7 --- /dev/null +++ b/base_view_inheritance_extension/i18n/tr.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +# Translators: +# Ahmet Altinisik , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-12-29 03:39+0000\n" +"PO-Revision-Date: 2016-12-29 03:39+0000\n" +"Last-Translator: Ahmet Altinisik , 2016\n" +"Language-Team: Turkish (https://www.transifex.com/oca/teams/23907/tr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: tr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "A new page" +msgstr "Yeni bir sayfa" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "Partner form" +msgstr "İş ortağı formu" + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "ir.ui.view" From ef05095ead779c542ad0b0f4e4822d6276bc2c1f Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Tue, 4 Apr 2017 19:06:26 +0200 Subject: [PATCH 148/770] [8.0][ADD] Add list manipulation operations to base_view_inheritance_extension. (#804) * Add list_add operation. * Add list_remove operation. OCA Transbot updated translations from Transifex --- base_view_inheritance_extension/README.rst | 42 ++++++++-- .../__openerp__.py | 2 +- base_view_inheritance_extension/i18n/ca.po | 34 ++++++++ base_view_inheritance_extension/i18n/de.po | 7 +- base_view_inheritance_extension/i18n/it.po | 34 ++++++++ .../models/ir_ui_view.py | 37 +++++++++ .../test_base_view_inheritance_extension.py | 83 +++++++++++++++++++ 7 files changed, 227 insertions(+), 12 deletions(-) create mode 100644 base_view_inheritance_extension/i18n/ca.po create mode 100644 base_view_inheritance_extension/i18n/it.po diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst index f5eaa8ba6f9..af1675b652d 100644 --- a/base_view_inheritance_extension/README.rst +++ b/base_view_inheritance_extension/README.rst @@ -6,7 +6,8 @@ Extended view inheritance ========================= -This module was written to make it simple to add custom operators for view inheritance. +This module was written to make it simple to add custom operators for view +inheritance. Usage ===== @@ -24,7 +25,8 @@ Change a python dictionary (context for example) $new_value -Note that views are subject to evaluation of xmlids anyways, so if you need to refer to some xmlid, say ``%(xmlid)s``. +Note that views are subject to evaluation of xmlids anyways, so if you need +to refer to some xmlid, say ``%(xmlid)s``. Move an element in the view --------------------------- @@ -33,13 +35,30 @@ Move an element in the view -This can also be used to wrap some element into another, create the target element first, then move the node youwant to wrap there. +This can also be used to wrap some element into another, create the target +element first, then move the node youwant to wrap there. + +Add to values in a list (states for example) +-------------------------------------------- + +.. code-block:: xml + + + $new_value(s) + + +Remove values from a list (states for example) +---------------------------------------------- + +.. code-block:: xml + + + $remove_value(s) + Known issues / Roadmap ====================== -* add ``$value`` -* add ``$index`` * add ``$value`` * support ```` * support an ``eval`` attribute for our new node types @@ -58,14 +77,21 @@ Credits Images ------ -* Odoo Community Association: `Icon `_. +* Odoo Community Association: + `Icon `_. Contributors ------------ * Holger Brunn - -Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list `_ or the `appropriate specialized mailinglist `_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues. +* Ronald Portier + +Do not contact contributors directly about help with questions or problems +concerning this addon, but use the +`community mailing list `_ or the +`appropriate specialized mailinglist `_ +for help, and the bug tracker linked in `Bug Tracker`_ above for +technical issues. Maintainer ---------- diff --git a/base_view_inheritance_extension/__openerp__.py b/base_view_inheritance_extension/__openerp__.py index d4304a54081..a9e251017e1 100644 --- a/base_view_inheritance_extension/__openerp__.py +++ b/base_view_inheritance_extension/__openerp__.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", - "version": "9.0.1.0.0", + "version": "9.0.1.1.0", "author": "Therp BV,Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Hidden/Dependency", diff --git a/base_view_inheritance_extension/i18n/ca.po b/base_view_inheritance_extension/i18n/ca.po new file mode 100644 index 00000000000..1e6d67e2b0d --- /dev/null +++ b/base_view_inheritance_extension/i18n/ca.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +# Translators: +# Marc Tormo i Bochaca , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-04-19 17:59+0000\n" +"PO-Revision-Date: 2017-04-19 17:59+0000\n" +"Last-Translator: Marc Tormo i Bochaca , 2017\n" +"Language-Team: Catalan (https://www.transifex.com/oca/teams/23907/ca/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: ca\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "A new page" +msgstr "Una nova pàgina " + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "Partner form" +msgstr "Empresa de " + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "ir.ui.view" diff --git a/base_view_inheritance_extension/i18n/de.po b/base_view_inheritance_extension/i18n/de.po index c9e0e1ffa89..1e8b83970b2 100644 --- a/base_view_inheritance_extension/i18n/de.po +++ b/base_view_inheritance_extension/i18n/de.po @@ -3,13 +3,14 @@ # * base_view_inheritance_extension # # Translators: +# OCA Transbot , 2017 # Rudolf Schnapka , 2017 msgid "" msgstr "" "Project-Id-Version: Odoo Server 9.0c\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-03-04 07:39+0000\n" -"PO-Revision-Date: 2017-03-04 07:39+0000\n" +"POT-Creation-Date: 2017-04-19 17:59+0000\n" +"PO-Revision-Date: 2017-04-19 17:59+0000\n" "Last-Translator: Rudolf Schnapka , 2017\n" "Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n" "MIME-Version: 1.0\n" @@ -31,4 +32,4 @@ msgstr "Partner-Formular" #. module: base_view_inheritance_extension #: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view msgid "ir.ui.view" -msgstr "" +msgstr "ir.ui.view" diff --git a/base_view_inheritance_extension/i18n/it.po b/base_view_inheritance_extension/i18n/it.po new file mode 100644 index 00000000000..dea907836de --- /dev/null +++ b/base_view_inheritance_extension/i18n/it.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +# Translators: +# Paolo Valier , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-04-19 17:59+0000\n" +"PO-Revision-Date: 2017-04-19 17:59+0000\n" +"Last-Translator: Paolo Valier , 2017\n" +"Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "A new page" +msgstr "Una nuova pagina" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +msgid "Partner form" +msgstr "" + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "" diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index 75222007994..a32baee9d2c 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -122,3 +122,40 @@ def inheritance_handler_xpath(self, source, specs, inherit_id): ) target_node.append(node) return source + + @api.model + def inheritance_handler_attributes_list_add( + self, source, specs, inherit_id + ): + """Implement + <$node position="attributes"> + + $new_value + + """ + node = self.locate_node(source, specs) + for attribute_node in specs: + attribute_name = attribute_node.get('name') + old_value = node.get(attribute_name) or '' + new_value = old_value + ',' + attribute_node.text + node.attrib[attribute_name] = new_value + return source + + @api.model + def inheritance_handler_attributes_list_remove( + self, source, specs, inherit_id + ): + """Implement + <$node position="attributes"> + + $value_to_remove + + """ + node = self.locate_node(source, specs) + for attribute_node in specs: + attribute_name = attribute_node.get('name') + old_values = (node.get(attribute_name) or '').split(',') + remove_values = attribute_node.text.split(',') + new_values = [x for x in old_values if x not in remove_values] + node.attrib[attribute_name] = ','.join(filter(None, new_values)) + return source diff --git a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py index 2429b4f6fd4..af1e2c6ba42 100644 --- a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py +++ b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py @@ -6,6 +6,7 @@ class TestBaseViewInheritanceExtension(TransactionCase): + def test_base_view_inheritance_extension(self): view_id = self.env.ref('base.view_partner_form').id fields_view_get = self.env['res.partner'].fields_view_get( @@ -28,3 +29,85 @@ def test_base_view_inheritance_extension(self): view.xpath('//field[@name="child_ids"]')[0].getparent(), view.xpath('//page[@name="my_new_page"]')[0] ) + + def test_list_add(self): + view_model = self.env['ir.ui.view'] + inherit_id = self.env.ref('base.view_partner_form').id + source = etree.fromstring( + """\ +
+
+ """ + ) + modified_source = \ + view_model.inheritance_handler_attributes_list_add( + source, specs, inherit_id + ) + button_node = modified_source.xpath('//button[@name="test"]')[0] + self.assertEqual( + button_node.attrib['states'], + 'draft,open,valid' + ) + # extend with list of values + specs = etree.fromstring( + """\ + + """ + ) + modified_source = \ + view_model.inheritance_handler_attributes_list_add( + source, specs, inherit_id + ) + button_node = modified_source.xpath('//button[@name="test"]')[0] + self.assertEqual( + button_node.attrib['states'], + 'draft,open,valid,payable,paid' + ) + + def test_list_remove(self): + view_model = self.env['ir.ui.view'] + inherit_id = self.env.ref('base.view_partner_form').id + source = etree.fromstring( + """\ +
+
+ """ + ) + modified_source = \ + view_model.inheritance_handler_attributes_list_remove( + source, specs, inherit_id + ) + button_node = modified_source.xpath('//button[@name="test"]')[0] + self.assertEqual( + button_node.attrib['states'], + 'draft,valid,paid' + ) From b2ef6d860ac8e83a990f0fb66360ad394bc259c1 Mon Sep 17 00:00:00 2001 From: lfreeke Date: Thu, 31 Aug 2017 14:11:29 +0200 Subject: [PATCH 149/770] [MIG] base_view_inheritance_extension to 10.0 --- base_view_inheritance_extension/README.rst | 2 +- .../{__openerp__.py => __manifest__.py} | 2 +- .../demo/ir_ui_view.xml | 21 ++++++++++--------- .../models/ir_ui_view.py | 2 +- .../test_base_view_inheritance_extension.py | 8 +++---- 5 files changed, 18 insertions(+), 17 deletions(-) rename base_view_inheritance_extension/{__openerp__.py => __manifest__.py} (93%) diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst index af1675b652d..d97a5b97dc1 100644 --- a/base_view_inheritance_extension/README.rst +++ b/base_view_inheritance_extension/README.rst @@ -14,7 +14,7 @@ Usage .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/149/9.0 + :target: https://runbot.odoo-community.org/runbot/149/10.0 Change a python dictionary (context for example) ------------------------------------------------ diff --git a/base_view_inheritance_extension/__openerp__.py b/base_view_inheritance_extension/__manifest__.py similarity index 93% rename from base_view_inheritance_extension/__openerp__.py rename to base_view_inheritance_extension/__manifest__.py index a9e251017e1..9088c8430d7 100644 --- a/base_view_inheritance_extension/__openerp__.py +++ b/base_view_inheritance_extension/__manifest__.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", - "version": "9.0.1.1.0", + "version": "10.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Hidden/Dependency", diff --git a/base_view_inheritance_extension/demo/ir_ui_view.xml b/base_view_inheritance_extension/demo/ir_ui_view.xml index 648a19cb558..96c6c73522f 100644 --- a/base_view_inheritance_extension/demo/ir_ui_view.xml +++ b/base_view_inheritance_extension/demo/ir_ui_view.xml @@ -1,9 +1,8 @@ - - - + + res.partner - + Partner form @@ -12,11 +11,13 @@ 'The company name' context.get('company_id', context.get('company')) - - - - +
+ + + +
+ +
-
-
+ diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index a32baee9d2c..3c844ec7769 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -2,7 +2,7 @@ # © 2016 Therp BV # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from lxml import etree -from openerp import api, models, tools +from odoo import api, models, tools class UnquoteObject(str): diff --git a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py index af1e2c6ba42..caeb8a3598f 100644 --- a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py +++ b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py @@ -2,13 +2,13 @@ # © 2016 Therp BV # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from lxml import etree -from openerp.tests.common import TransactionCase +from odoo.tests.common import TransactionCase class TestBaseViewInheritanceExtension(TransactionCase): def test_base_view_inheritance_extension(self): - view_id = self.env.ref('base.view_partner_form').id + view_id = self.env.ref('base.view_partner_simple_form').id fields_view_get = self.env['res.partner'].fields_view_get( view_id=view_id ) @@ -26,8 +26,8 @@ def test_base_view_inheritance_extension(self): ) # verify we moved the child_ids field self.assertEqual( - view.xpath('//field[@name="child_ids"]')[0].getparent(), - view.xpath('//page[@name="my_new_page"]')[0] + view.xpath('//field[@name="mobile"]')[0].getparent(), + view.xpath('//page[@name="phone_book"]')[0] ) def test_list_add(self): From 70e67bb4c6e8f8222a6b29bc9e890b6975eec123 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 11 Oct 2017 04:51:35 +0200 Subject: [PATCH 150/770] [FIX] don't break when an attribute node in an xpath makes the expression not match the node any more. Fixes #885 --- base_view_inheritance_extension/__manifest__.py | 2 +- base_view_inheritance_extension/demo/ir_ui_view.xml | 4 ++++ base_view_inheritance_extension/models/ir_ui_view.py | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/base_view_inheritance_extension/__manifest__.py b/base_view_inheritance_extension/__manifest__.py index 9088c8430d7..79b3cc94c7e 100644 --- a/base_view_inheritance_extension/__manifest__.py +++ b/base_view_inheritance_extension/__manifest__.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", - "version": "10.0.1.0.0", + "version": "10.0.1.0.1", "author": "Therp BV,Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Hidden/Dependency", diff --git a/base_view_inheritance_extension/demo/ir_ui_view.xml b/base_view_inheritance_extension/demo/ir_ui_view.xml index 96c6c73522f..0439602096c 100644 --- a/base_view_inheritance_extension/demo/ir_ui_view.xml +++ b/base_view_inheritance_extension/demo/ir_ui_view.xml @@ -11,6 +11,10 @@ 'The company name' context.get('company_id', context.get('company')) + + + parent_id +
diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index 3c844ec7769..ae5a0069550 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -55,6 +55,9 @@ def _iter_inheritance_specs(self, spec): yield node, handler return if spec.get('position') == 'attributes': + if all(not c.get('operation') for c in spec): + yield spec, self._get_inheritance_handler(spec) + return for child in spec: node = etree.Element(spec.tag, **spec.attrib) node.insert(0, child) From 99a048dbf802e8824d3cc52bdf360e0ee00ddb6a Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 11 Oct 2017 04:40:14 +0200 Subject: [PATCH 151/770] [FIX] base_view_inheritance_extension: 2 things: * warning about dynamic context * import for safe_eval --- .../i18n/base_view_inheritance_extension.pot | 30 ++++++++++++++++ base_view_inheritance_extension/i18n/ca.po | 19 +++++----- base_view_inheritance_extension/i18n/de.po | 27 +++++++------- base_view_inheritance_extension/i18n/es.po | 34 ++++++++++++++++++ base_view_inheritance_extension/i18n/hr.po | 35 +++++++++++++++++++ base_view_inheritance_extension/i18n/it.po | 28 +++++++-------- base_view_inheritance_extension/i18n/sl.po | 22 +++++++----- base_view_inheritance_extension/i18n/tr.po | 19 +++++----- .../models/ir_ui_view.py | 6 ++-- 9 files changed, 165 insertions(+), 55 deletions(-) create mode 100644 base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot create mode 100644 base_view_inheritance_extension/i18n/es.po create mode 100644 base_view_inheritance_extension/i18n/hr.po diff --git a/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot b/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot new file mode 100644 index 00000000000..6a43a709f4c --- /dev/null +++ b/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot @@ -0,0 +1,30 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.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_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Partner form" +msgstr "" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "" + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "" + diff --git a/base_view_inheritance_extension/i18n/ca.po b/base_view_inheritance_extension/i18n/ca.po index 1e6d67e2b0d..fef50e279eb 100644 --- a/base_view_inheritance_extension/i18n/ca.po +++ b/base_view_inheritance_extension/i18n/ca.po @@ -1,7 +1,7 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * base_view_inheritance_extension -# +# # Translators: # Marc Tormo i Bochaca , 2017 msgid "" @@ -12,23 +12,26 @@ msgstr "" "PO-Revision-Date: 2017-04-19 17:59+0000\n" "Last-Translator: Marc Tormo i Bochaca , 2017\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" -"Language: ca\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form -msgid "A new page" -msgstr "Una nova pàgina " - -#. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form msgid "Partner form" msgstr "Empresa de " +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "" + #. module: base_view_inheritance_extension #: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view msgid "ir.ui.view" msgstr "ir.ui.view" + +#~ msgid "A new page" +#~ msgstr "Una nova pàgina " diff --git a/base_view_inheritance_extension/i18n/de.po b/base_view_inheritance_extension/i18n/de.po index 1e8b83970b2..0862b29ddc3 100644 --- a/base_view_inheritance_extension/i18n/de.po +++ b/base_view_inheritance_extension/i18n/de.po @@ -1,33 +1,32 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * base_view_inheritance_extension -# +# # Translators: -# OCA Transbot , 2017 -# Rudolf Schnapka , 2017 +# Niki Waibel , 2017 msgid "" msgstr "" -"Project-Id-Version: Odoo Server 9.0c\n" +"Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-04-19 17:59+0000\n" -"PO-Revision-Date: 2017-04-19 17:59+0000\n" -"Last-Translator: Rudolf Schnapka , 2017\n" +"POT-Creation-Date: 2017-12-01 02:10+0000\n" +"PO-Revision-Date: 2017-12-01 02:10+0000\n" +"Last-Translator: Niki Waibel , 2017\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" -"Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form -msgid "A new page" -msgstr "Eine neue Seite" +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Partner form" +msgstr "" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form -msgid "Partner form" -msgstr "Partner-Formular" +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "" #. module: base_view_inheritance_extension #: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view diff --git a/base_view_inheritance_extension/i18n/es.po b/base_view_inheritance_extension/i18n/es.po new file mode 100644 index 00000000000..87cecfad636 --- /dev/null +++ b/base_view_inheritance_extension/i18n/es.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +# Translators: +# Pedro M. Baeza , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-12-01 02:10+0000\n" +"PO-Revision-Date: 2017-12-01 02:10+0000\n" +"Last-Translator: Pedro M. Baeza , 2017\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" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Partner form" +msgstr "" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "" + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "ir.ui.view" diff --git a/base_view_inheritance_extension/i18n/hr.po b/base_view_inheritance_extension/i18n/hr.po new file mode 100644 index 00000000000..3ded69c2c95 --- /dev/null +++ b/base_view_inheritance_extension/i18n/hr.po @@ -0,0 +1,35 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_view_inheritance_extension +# +# Translators: +# Bole , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-02 18:40+0000\n" +"PO-Revision-Date: 2018-03-02 18:40+0000\n" +"Last-Translator: Bole , 2018\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" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Partner form" +msgstr "Forma partnera" + +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "Brojevi telefona" + +#. module: base_view_inheritance_extension +#: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view +msgid "ir.ui.view" +msgstr "ir.ui.view" diff --git a/base_view_inheritance_extension/i18n/it.po b/base_view_inheritance_extension/i18n/it.po index dea907836de..c2f0849f28f 100644 --- a/base_view_inheritance_extension/i18n/it.po +++ b/base_view_inheritance_extension/i18n/it.po @@ -1,34 +1,34 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * base_view_inheritance_extension -# +# # Translators: -# Paolo Valier , 2017 +# Paolo Valier , 2018 msgid "" msgstr "" -"Project-Id-Version: Odoo Server 9.0c\n" +"Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-04-19 17:59+0000\n" -"PO-Revision-Date: 2017-04-19 17:59+0000\n" -"Last-Translator: Paolo Valier , 2017\n" +"POT-Creation-Date: 2018-01-06 02:25+0000\n" +"PO-Revision-Date: 2018-01-06 02:25+0000\n" +"Last-Translator: Paolo Valier , 2018\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" -"Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form -msgid "A new page" -msgstr "Una nuova pagina" +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Partner form" +msgstr "Form Partner" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form -msgid "Partner form" -msgstr "" +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "Numeri di telefono" #. module: base_view_inheritance_extension #: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view msgid "ir.ui.view" -msgstr "" +msgstr "ir.ui.view" diff --git a/base_view_inheritance_extension/i18n/sl.po b/base_view_inheritance_extension/i18n/sl.po index 1071aca9dc3..864f77e7a87 100644 --- a/base_view_inheritance_extension/i18n/sl.po +++ b/base_view_inheritance_extension/i18n/sl.po @@ -1,7 +1,7 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * base_view_inheritance_extension -# +# # Translators: # Matjaž Mozetič , 2016 msgid "" @@ -12,23 +12,27 @@ msgstr "" "PO-Revision-Date: 2016-12-29 03:39+0000\n" "Last-Translator: Matjaž Mozetič , 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" -"Language: sl\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_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form -msgid "A new page" -msgstr "Nova stran" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" +"%100==4 ? 2 : 3);\n" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form msgid "Partner form" msgstr "Partnerjev obrazec" +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "" + #. module: base_view_inheritance_extension #: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view msgid "ir.ui.view" msgstr "" + +#~ msgid "A new page" +#~ msgstr "Nova stran" diff --git a/base_view_inheritance_extension/i18n/tr.po b/base_view_inheritance_extension/i18n/tr.po index 77e748e28e7..45654394ef0 100644 --- a/base_view_inheritance_extension/i18n/tr.po +++ b/base_view_inheritance_extension/i18n/tr.po @@ -1,7 +1,7 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * base_view_inheritance_extension -# +# # Translators: # Ahmet Altinisik , 2016 msgid "" @@ -12,23 +12,26 @@ msgstr "" "PO-Revision-Date: 2016-12-29 03:39+0000\n" "Last-Translator: Ahmet Altinisik , 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" -"Language: tr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form -msgid "A new page" -msgstr "Yeni bir sayfa" - -#. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_form +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form msgid "Partner form" msgstr "İş ortağı formu" +#. module: base_view_inheritance_extension +#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +msgid "Phone numbers" +msgstr "" + #. module: base_view_inheritance_extension #: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view msgid "ir.ui.view" msgstr "ir.ui.view" + +#~ msgid "A new page" +#~ msgstr "Yeni bir sayfa" diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index ae5a0069550..7f38233715f 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -3,6 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from lxml import etree from odoo import api, models, tools +from odoo.tools.safe_eval import safe_eval class UnquoteObject(str): @@ -103,9 +104,10 @@ def inheritance_handler_attributes_python_dict( """ node = self.locate_node(source, specs) for attribute_node in specs: - python_dict = tools.safe_eval( + python_dict = safe_eval( node.get(attribute_node.get('name')) or '{}', - UnquoteEvalObjectContext() + UnquoteEvalObjectContext(), + nocopy=True ) python_dict[attribute_node.get('key')] = UnquoteObject( attribute_node.text From 3c20fa9114b7b1534287c09d7448b941647d528a Mon Sep 17 00:00:00 2001 From: Sergio Teruel Date: Fri, 26 Oct 2018 13:54:14 +0200 Subject: [PATCH 152/770] [MIG] base_view_inheritance_extension: Migration to 11.0 --- base_view_inheritance_extension/README.rst | 84 ++-- base_view_inheritance_extension/__init__.py | 2 - .../__manifest__.py | 6 +- .../i18n/base_view_inheritance_extension.pot | 2 +- .../models/__init__.py | 2 - .../models/ir_ui_view.py | 11 +- .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 2 + .../readme/ROADMAP.rst | 3 + .../readme/USAGE.rst | 36 ++ .../static/description/index.html | 461 ++++++++++++++++++ .../tests/__init__.py | 2 - .../test_base_view_inheritance_extension.py | 3 +- 13 files changed, 565 insertions(+), 52 deletions(-) create mode 100644 base_view_inheritance_extension/readme/CONTRIBUTORS.rst create mode 100644 base_view_inheritance_extension/readme/DESCRIPTION.rst create mode 100644 base_view_inheritance_extension/readme/ROADMAP.rst create mode 100644 base_view_inheritance_extension/readme/USAGE.rst create mode 100644 base_view_inheritance_extension/static/description/index.html diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst index d97a5b97dc1..8844edc2e7b 100644 --- a/base_view_inheritance_extension/README.rst +++ b/base_view_inheritance_extension/README.rst @@ -1,23 +1,43 @@ -.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg - :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html - :alt: License: LGPL-3 - ========================= Extended view inheritance ========================= +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/11.0/base_view_inheritance_extension + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-11-0/server-tools-11-0-base_view_inheritance_extension + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + This module was written to make it simple to add custom operators for view inheritance. +**Table of contents** + +.. contents:: + :local: + Usage ===== -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/149/10.0 +**Change a python dictionary (context for example)** -Change a python dictionary (context for example) ------------------------------------------------- .. code-block:: xml @@ -28,8 +48,7 @@ Change a python dictionary (context for example) Note that views are subject to evaluation of xmlids anyways, so if you need to refer to some xmlid, say ``%(xmlid)s``. -Move an element in the view ---------------------------- +**Move an element in the view** .. code-block:: xml @@ -38,8 +57,7 @@ Move an element in the view This can also be used to wrap some element into another, create the target element first, then move the node youwant to wrap there. -Add to values in a list (states for example) --------------------------------------------- +**Add to values in a list (states for example)** .. code-block:: xml @@ -47,8 +65,7 @@ Add to values in a list (states for example) $new_value(s) -Remove values from a list (states for example) ----------------------------------------------- +**Remove values from a list (states for example)** .. code-block:: xml @@ -66,44 +83,41 @@ Known issues / Roadmap Bug Tracker =========== -Bugs are tracked on `GitHub Issues -`_. In case of trouble, please -check there if your issue has already been reported. If you spotted it first, -help us smashing it by providing a detailed and welcomed feedback. +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= -Images ------- +Authors +~~~~~~~ -* Odoo Community Association: - `Icon `_. +* Therp BV Contributors ------------- +~~~~~~~~~~~~ * Holger Brunn * Ronald Portier +* Sergio Teruel -Do not contact contributors directly about help with questions or problems -concerning this addon, but use the -`community mailing list `_ or the -`appropriate specialized mailinglist `_ -for help, and the bug tracker linked in `Bug Tracker`_ above for -technical issues. +Maintainers +~~~~~~~~~~~ -Maintainer ----------- +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - 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. -To contribute to this module, please visit https://odoo-community.org. +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_view_inheritance_extension/__init__.py b/base_view_inheritance_extension/__init__.py index 4431c72c42a..92325983cfb 100644 --- a/base_view_inheritance_extension/__init__.py +++ b/base_view_inheritance_extension/__init__.py @@ -1,4 +1,2 @@ -# -*- coding: utf-8 -*- -# © 2016 Therp BV # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import models diff --git a/base_view_inheritance_extension/__manifest__.py b/base_view_inheritance_extension/__manifest__.py index 79b3cc94c7e..27a24017e3a 100644 --- a/base_view_inheritance_extension/__manifest__.py +++ b/base_view_inheritance_extension/__manifest__.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- -# © 2016 Therp BV +# Copyright 2016 Therp BV +# Copyright 2018 Tecnativa - Sergio Teruel # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", - "version": "10.0.1.0.1", + "version": "11.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Hidden/Dependency", diff --git a/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot b/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot index 6a43a709f4c..c1b5a480d9b 100644 --- a/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot +++ b/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 10.0\n" +"Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: <>\n" "Language-Team: \n" diff --git a/base_view_inheritance_extension/models/__init__.py b/base_view_inheritance_extension/models/__init__.py index 588d249e27c..352589cd2d5 100644 --- a/base_view_inheritance_extension/models/__init__.py +++ b/base_view_inheritance_extension/models/__init__.py @@ -1,4 +1,2 @@ -# -*- coding: utf-8 -*- -# © 2016 Therp BV # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import ir_ui_view diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index 7f38233715f..c95631625b4 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- -# © 2016 Therp BV +# Copyright 2016 Therp BV +# Copyright 2018 Tecnativa - Sergio Teruel # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from lxml import etree from odoo import api, models, tools @@ -20,14 +20,14 @@ def __call__(self, *args, **kwargs): ','.join( [ UnquoteObject( - a if not isinstance(a, basestring) + a if not isinstance(a, str) else "'%s'" % a ) for a in args ] + [ '%s=%s' % (UnquoteObject(k), v) - for (k, v) in kwargs.iteritems() + for (k, v) in kwargs.items() ] ) ) @@ -162,5 +162,6 @@ def inheritance_handler_attributes_list_remove( old_values = (node.get(attribute_name) or '').split(',') remove_values = attribute_node.text.split(',') new_values = [x for x in old_values if x not in remove_values] - node.attrib[attribute_name] = ','.join(filter(None, new_values)) + node.attrib[attribute_name] = ','.join( + [_f for _f in new_values if _f]) return source diff --git a/base_view_inheritance_extension/readme/CONTRIBUTORS.rst b/base_view_inheritance_extension/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..41d2a472c0c --- /dev/null +++ b/base_view_inheritance_extension/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Holger Brunn +* Ronald Portier +* Sergio Teruel diff --git a/base_view_inheritance_extension/readme/DESCRIPTION.rst b/base_view_inheritance_extension/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..0536b6f26db --- /dev/null +++ b/base_view_inheritance_extension/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module was written to make it simple to add custom operators for view +inheritance. diff --git a/base_view_inheritance_extension/readme/ROADMAP.rst b/base_view_inheritance_extension/readme/ROADMAP.rst new file mode 100644 index 00000000000..ade040dce2e --- /dev/null +++ b/base_view_inheritance_extension/readme/ROADMAP.rst @@ -0,0 +1,3 @@ +* add ``$value`` +* support ```` +* support an ``eval`` attribute for our new node types diff --git a/base_view_inheritance_extension/readme/USAGE.rst b/base_view_inheritance_extension/readme/USAGE.rst new file mode 100644 index 00000000000..65199fd1ed8 --- /dev/null +++ b/base_view_inheritance_extension/readme/USAGE.rst @@ -0,0 +1,36 @@ +**Change a python dictionary (context for example)** + + +.. code-block:: xml + + + $new_value + + +Note that views are subject to evaluation of xmlids anyways, so if you need +to refer to some xmlid, say ``%(xmlid)s``. + +**Move an element in the view** + +.. code-block:: xml + + + +This can also be used to wrap some element into another, create the target +element first, then move the node youwant to wrap there. + +**Add to values in a list (states for example)** + +.. code-block:: xml + + + $new_value(s) + + +**Remove values from a list (states for example)** + +.. code-block:: xml + + + $remove_value(s) + diff --git a/base_view_inheritance_extension/static/description/index.html b/base_view_inheritance_extension/static/description/index.html new file mode 100644 index 00000000000..3d2599b7698 --- /dev/null +++ b/base_view_inheritance_extension/static/description/index.html @@ -0,0 +1,461 @@ + + + + + + +Extended view inheritance + + + +
+

Extended view inheritance

+ + +

Beta License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runbot

+

This module was written to make it simple to add custom operators for view +inheritance.

+

Table of contents

+ +
+

Usage

+

Change a python dictionary (context for example)

+
+<attribute name="$attribute" operation="python_dict" key="$key">
+    $new_value
+</attribute>
+
+

Note that views are subject to evaluation of xmlids anyways, so if you need +to refer to some xmlid, say %(xmlid)s.

+

Move an element in the view

+
+<xpath expr="$xpath" position="move" target="$targetxpath" />
+
+

This can also be used to wrap some element into another, create the target +element first, then move the node youwant to wrap there.

+

Add to values in a list (states for example)

+
+<attribute name="$attribute" operation="list_add">
+    $new_value(s)
+</attribute>
+
+

Remove values from a list (states for example)

+
+<attribute name="$attribute" operation="list_remove">
+    $remove_value(s)
+</attribute>
+
+
+
+

Known issues / Roadmap

+
    +
  • add <attribute operation="json_dict" key="$key">$value</attribute>
  • +
  • support <xpath expr="$xpath" position="move" target="xpath" target_position="position" />
  • +
  • support an eval attribute for our new node types
  • +
+
+
+

Bug Tracker

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
+
+
+

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-tools project on GitHub.

+

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

+
+
+
+ + diff --git a/base_view_inheritance_extension/tests/__init__.py b/base_view_inheritance_extension/tests/__init__.py index f76aba46a08..e19c813a8a4 100644 --- a/base_view_inheritance_extension/tests/__init__.py +++ b/base_view_inheritance_extension/tests/__init__.py @@ -1,4 +1,2 @@ -# -*- coding: utf-8 -*- -# © 2016 Therp BV # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import test_base_view_inheritance_extension diff --git a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py index caeb8a3598f..212a20b5ed9 100644 --- a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py +++ b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# © 2016 Therp BV +# Copyright 2016 Therp BV # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from lxml import etree from odoo.tests.common import TransactionCase From b40abe94e8807f8556ae4601abf2bc5fa728e25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Thu, 14 Mar 2019 18:10:16 -0300 Subject: [PATCH 153/770] [MIG] base_view_inheritance_extension: Migration to 12.0 --- base_view_inheritance_extension/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base_view_inheritance_extension/__manifest__.py b/base_view_inheritance_extension/__manifest__.py index 27a24017e3a..baa593cd7fd 100644 --- a/base_view_inheritance_extension/__manifest__.py +++ b/base_view_inheritance_extension/__manifest__.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", - "version": "11.0.1.0.0", + "version": "12.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Hidden/Dependency", From 293ee72393bced9c0c2a8acaedfb6bc394807b47 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 3 Mar 2020 15:21:37 +0100 Subject: [PATCH 154/770] Remove the "move" feature which is now native Use https in URL to licence --- base_view_inheritance_extension/README.rst | 25 ++++++++----------- base_view_inheritance_extension/__init__.py | 1 - .../__manifest__.py | 2 +- .../demo/ir_ui_view.xml | 2 -- .../i18n/base_view_inheritance_extension.pot | 8 +++--- .../models/__init__.py | 1 - .../models/ir_ui_view.py | 15 +---------- .../readme/USAGE.rst | 15 +++++------ .../static/description/index.html | 16 +++++------- .../tests/__init__.py | 1 - .../test_base_view_inheritance_extension.py | 7 +----- 11 files changed, 30 insertions(+), 63 deletions(-) diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst index 8844edc2e7b..b17b13b6288 100644 --- a/base_view_inheritance_extension/README.rst +++ b/base_view_inheritance_extension/README.rst @@ -14,13 +14,13 @@ Extended view inheritance :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/11.0/base_view_inheritance_extension + :target: https://github.com/OCA/server-tools/tree/12.0/base_view_inheritance_extension :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-11-0/server-tools-11-0-base_view_inheritance_extension + :target: https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-base_view_inheritance_extension :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/149/11.0 + :target: https://runbot.odoo-community.org/runbot/149/12.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -48,15 +48,6 @@ Usage Note that views are subject to evaluation of xmlids anyways, so if you need to refer to some xmlid, say ``%(xmlid)s``. -**Move an element in the view** - -.. code-block:: xml - - - -This can also be used to wrap some element into another, create the target -element first, then move the node youwant to wrap there. - **Add to values in a list (states for example)** .. code-block:: xml @@ -73,6 +64,12 @@ element first, then move the node youwant to wrap there. $remove_value(s) +**Move an element in the view** + +This feature is now native, cf the `official Odoo documentation `_. + + + Known issues / Roadmap ====================== @@ -86,7 +83,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -118,6 +115,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_view_inheritance_extension/__init__.py b/base_view_inheritance_extension/__init__.py index 92325983cfb..0650744f6bc 100644 --- a/base_view_inheritance_extension/__init__.py +++ b/base_view_inheritance_extension/__init__.py @@ -1,2 +1 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import models diff --git a/base_view_inheritance_extension/__manifest__.py b/base_view_inheritance_extension/__manifest__.py index baa593cd7fd..e178e57259c 100644 --- a/base_view_inheritance_extension/__manifest__.py +++ b/base_view_inheritance_extension/__manifest__.py @@ -1,6 +1,6 @@ # Copyright 2016 Therp BV # Copyright 2018 Tecnativa - Sergio Teruel -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", "version": "12.0.1.0.0", diff --git a/base_view_inheritance_extension/demo/ir_ui_view.xml b/base_view_inheritance_extension/demo/ir_ui_view.xml index 0439602096c..fb357c955d4 100644 --- a/base_view_inheritance_extension/demo/ir_ui_view.xml +++ b/base_view_inheritance_extension/demo/ir_ui_view.xml @@ -20,8 +20,6 @@
- - diff --git a/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot b/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot index c1b5a480d9b..709ef727170 100644 --- a/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot +++ b/base_view_inheritance_extension/i18n/base_view_inheritance_extension.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: <>\n" "Language-Team: \n" @@ -14,17 +14,17 @@ msgstr "" "Plural-Forms: \n" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +#: model_terms:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form msgid "Partner form" msgstr "" #. module: base_view_inheritance_extension -#: model:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form +#: model_terms:ir.ui.view,arch_db:base_view_inheritance_extension.view_partner_simple_form msgid "Phone numbers" msgstr "" #. module: base_view_inheritance_extension #: model:ir.model,name:base_view_inheritance_extension.model_ir_ui_view -msgid "ir.ui.view" +msgid "View" msgstr "" diff --git a/base_view_inheritance_extension/models/__init__.py b/base_view_inheritance_extension/models/__init__.py index 352589cd2d5..81f52e3bfa6 100644 --- a/base_view_inheritance_extension/models/__init__.py +++ b/base_view_inheritance_extension/models/__init__.py @@ -1,2 +1 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import ir_ui_view diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index c95631625b4..d7386d70658 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -1,6 +1,6 @@ # Copyright 2016 Therp BV # Copyright 2018 Tecnativa - Sergio Teruel -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). from lxml import etree from odoo import api, models, tools from odoo.tools.safe_eval import safe_eval @@ -115,19 +115,6 @@ def inheritance_handler_attributes_python_dict( node.attrib[attribute_node.get('name')] = str(python_dict) return source - @api.model - def inheritance_handler_xpath(self, source, specs, inherit_id): - if not specs.get('position') == 'move': - return super(IrUiView, self).apply_inheritance_specs( - source, specs, inherit_id - ) - node = self.locate_node(source, specs) - target_node = self.locate_node( - source, etree.Element(specs.tag, expr=specs.get('target')) - ) - target_node.append(node) - return source - @api.model def inheritance_handler_attributes_list_add( self, source, specs, inherit_id diff --git a/base_view_inheritance_extension/readme/USAGE.rst b/base_view_inheritance_extension/readme/USAGE.rst index 65199fd1ed8..2af06adaa3b 100644 --- a/base_view_inheritance_extension/readme/USAGE.rst +++ b/base_view_inheritance_extension/readme/USAGE.rst @@ -10,15 +10,6 @@ Note that views are subject to evaluation of xmlids anyways, so if you need to refer to some xmlid, say ``%(xmlid)s``. -**Move an element in the view** - -.. code-block:: xml - - - -This can also be used to wrap some element into another, create the target -element first, then move the node youwant to wrap there. - **Add to values in a list (states for example)** .. code-block:: xml @@ -34,3 +25,9 @@ element first, then move the node youwant to wrap there. $remove_value(s) + +**Move an element in the view** + +This feature is now native, cf the `official Odoo documentation `_. + + diff --git a/base_view_inheritance_extension/static/description/index.html b/base_view_inheritance_extension/static/description/index.html index 3d2599b7698..e52ac987a93 100644 --- a/base_view_inheritance_extension/static/description/index.html +++ b/base_view_inheritance_extension/static/description/index.html @@ -3,7 +3,7 @@ - + Extended view inheritance -
-

Extended view inheritance

+
+ + +Odoo Community Association + +
+

Extended view inheritance

-

Mature License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

+

Mature License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

This module was written to make it simple to add custom operators for view inheritance.

Table of contents

@@ -387,7 +392,7 @@

Extended view inheritance

-

Usage

+

Usage

Change a python dictionary (context for example)

 <field position="attributes">
@@ -416,13 +421,13 @@ 

Usage

-

Known issues / Roadmap

+

Known issues / Roadmap

  • Support an eval attribute for our new node types.
-

Bug Tracker

+

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 @@ -430,15 +435,15 @@

Bug Tracker

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Therp BV
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -469,5 +474,6 @@

Maintainers

+
From 955672fd6abc07edaf4bc3b21aa95a287ce53e2e Mon Sep 17 00:00:00 2001 From: Ruchir Shukla Date: Fri, 12 Dec 2025 17:58:03 +0530 Subject: [PATCH 188/770] [MIG] base_view_inheritance_extension: Migration to 19.0 --- base_view_inheritance_extension/README.rst | 10 +++---- .../__manifest__.py | 2 +- .../models/ir_ui_view.py | 6 ++--- .../static/description/index.html | 6 ++--- .../test_base_view_inheritance_extension.py | 27 +++++++++++++++++-- 5 files changed, 37 insertions(+), 14 deletions(-) diff --git a/base_view_inheritance_extension/README.rst b/base_view_inheritance_extension/README.rst index d8906b5d2b0..95c8e8e9659 100644 --- a/base_view_inheritance_extension/README.rst +++ b/base_view_inheritance_extension/README.rst @@ -21,13 +21,13 @@ Extended view inheritance :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github - :target: https://github.com/OCA/server-tools/tree/18.0/base_view_inheritance_extension + :target: https://github.com/OCA/server-tools/tree/19.0/base_view_inheritance_extension :alt: OCA/server-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-base_view_inheritance_extension + :target: https://translation.odoo-community.org/projects/server-tools-19-0/server-tools-19-0-base_view_inheritance_extension :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-tools&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=19.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -87,7 +87,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -129,6 +129,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/server-tools `_ project on GitHub. +This module is part of the `OCA/server-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_view_inheritance_extension/__manifest__.py b/base_view_inheritance_extension/__manifest__.py index 7851b86825b..d27da32ee46 100644 --- a/base_view_inheritance_extension/__manifest__.py +++ b/base_view_inheritance_extension/__manifest__.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). { "name": "Extended view inheritance", - "version": "18.0.1.0.2", + "version": "19.0.1.0.0", "development_status": "Mature", "author": "Therp BV,Odoo Community Association (OCA)", "license": "LGPL-3", diff --git a/base_view_inheritance_extension/models/ir_ui_view.py b/base_view_inheritance_extension/models/ir_ui_view.py index b845791adc8..f5aa515ad43 100644 --- a/base_view_inheritance_extension/models/ir_ui_view.py +++ b/base_view_inheritance_extension/models/ir_ui_view.py @@ -9,7 +9,7 @@ from lxml import etree from odoo import api, models -from odoo.osv import expression +from odoo.fields import Domain def ast_dict_update(source, update): @@ -163,9 +163,9 @@ def _inheritance_handler_attributes_domain_add(self, source, specs): self._var2str_domain_text(attribute_node.text.strip()) ) if join_operator == "OR": - new_value = str(expression.OR([old_domain, new_domain])) + new_value = str(Domain.OR([old_domain, new_domain])) else: - new_value = str(expression.AND([old_domain, new_domain])) + new_value = str(Domain.AND([old_domain, new_domain])) new_value = self._str2var_domain_text(new_value) old_value = "".join(old_value.splitlines()) else: diff --git a/base_view_inheritance_extension/static/description/index.html b/base_view_inheritance_extension/static/description/index.html index 4f59755ca4b..1f046f10423 100644 --- a/base_view_inheritance_extension/static/description/index.html +++ b/base_view_inheritance_extension/static/description/index.html @@ -374,7 +374,7 @@

Extended view inheritance

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:cb2fb0b7f832559d24b4af1f66ac9d92258ed4cb2a2f32b5b1afaa3437f7b9eb !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Mature License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

+

Mature License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

This module was written to make it simple to add custom operators for view inheritance.

Table of contents

@@ -431,7 +431,7 @@

Bug Tracker

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

+feedback.

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

@@ -469,7 +469,7 @@

Maintainers

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

-

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

+

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

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

diff --git a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py index eeaf22c2a73..c4b76132f3e 100644 --- a/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py +++ b/base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py @@ -13,10 +13,33 @@ class TestBaseViewInheritanceExtension(TransactionCase): def setUpClass(cls): super().setUpClass() cls.maxDiff = None + cls.view = cls.env["ir.ui.view"].create( + { + "name": "Test Partner Simple Form Override", + "type": "form", + "model": "res.partner", + "inherit_id": cls.env.ref("base.view_partner_simple_form").id, + "arch": """ + + + Partner form + + + + + { + "default_email": "info@odoo-community.org", + "default_company_id": allowed_company_ids[0] + } + + + + """, + } + ) def test_base_view_inheritance_extension(self): - view_id = self.env.ref("base.view_partner_simple_form").id - arch, _ = self.env["res.partner"]._get_view(view_id=view_id) + arch, _ = self.env["res.partner"]._get_view(view_id=self.view.id) # Verify normal attributes work self.assertEqual(arch.xpath("//form")[0].get("string"), "Partner form") # Verify our extra context key worked From 4d9fdb5c88e1a155aeaf8de175afb1397558f88b Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Fri, 6 Nov 2020 21:39:26 +0100 Subject: [PATCH 189/770] [MOVE] openupgrade_records : move module from OCA/OpenUpgrade (branch 13.0) to OCA/server-tools Based on commit 746b7acbd90d62f9ffe6ee17472a1a3533e36597 (Fri Nov 6 17:18:47 2020 +0100) Co-authored-by: Stefan Rijnhart --- upgrade_analysis/apriori.py | 98 ++++++++ upgrade_analysis/compare.py | 438 ++++++++++++++++++++++++++++++++++++ 2 files changed, 536 insertions(+) create mode 100644 upgrade_analysis/apriori.py create mode 100644 upgrade_analysis/compare.py diff --git a/upgrade_analysis/apriori.py b/upgrade_analysis/apriori.py new file mode 100644 index 00000000000..973300fa0fe --- /dev/null +++ b/upgrade_analysis/apriori.py @@ -0,0 +1,98 @@ +""" Encode any known changes to the database here +to help the matching process +""" + +renamed_modules = { + # Odoo + 'crm_reveal': 'crm_iap_lead', + 'document': 'attachment_indexation', + 'payment_ogone': 'payment_ingenico', + # OCA/hr + # TODO: Transform possible data + 'hr_skill': 'hr_skills' +} + +merged_modules = { + # Odoo + 'account_cancel': 'account', + 'account_voucher': 'account', + 'crm_phone_validation': 'crm', + 'decimal_precision': 'base', + 'delivery_hs_code': 'delivery', + 'hw_scale': 'hw_drivers', + 'hw_scanner': 'hw_drivers', + 'hw_screen': 'hw_drivers', + 'l10n_fr_certification': 'account', + 'l10n_fr_sale_closing': 'l10n_fr', + 'mrp_bom_cost': 'mrp_account', + 'mrp_byproduct': 'mrp', + 'payment_stripe_sca': 'payment_stripe', + 'stock_zebra': 'stock', + 'survey_crm': 'survey', + 'test_pylint': 'test_lint', + 'web_settings_dashboard': 'base_setup', + 'website_crm_phone_validation': 'website_crm', + 'website_sale_link_tracker': 'website_sale', + 'website_survey': 'survey', + # OCA/account-financial-tools + 'account_move_chatter': 'account', + # OCA/account-reconcile + 'account_set_reconcilable': 'account', + # OCA/l10n-spain + 'l10n_es_aeat_sii': 'l10n_es_aeat_sii_oca', + # OCA/server-backend + 'base_suspend_security': 'base', + # OCA/social + 'mass_mailing_unique': 'mass_mailing', + # OCA/timesheet + 'sale_timesheet_existing_project': 'sale_timesheet', + # OCA/web + 'web_favicon': 'base', + 'web_widget_color': 'web', + 'web_widget_many2many_tags_multi_selection': 'web', + # OCA/website + 'website_canonical_url': 'website', + 'website_logo': 'website', +} + +# only used here for openupgrade_records analysis: +renamed_models = { + # Odoo + 'account.register.payments': 'account.payment.register', + 'crm.reveal.industry': 'crm.iap.lead.industry', + 'crm.reveal.role': 'crm.iap.lead.role', + 'crm.reveal.seniority': 'crm.iap.lead.seniority', + 'mail.blacklist.mixin': 'mail.thread.blacklist', + 'mail.mail.statistics': 'mailing.trace', + 'mail.statistics.report': 'mailing.trace.report', + 'mail.mass_mailing': 'mailing.mailing', + 'mail.mass_mailing.contact': 'mailing.contact', + 'mail.mass_mailing.list': 'mailing.list', + 'mail.mass_mailing.list_contact_rel': 'mailing.contact.subscription', + 'mail.mass_mailing.stage': 'utm.stage', + 'mail.mass_mailing.tag': 'utm.tag', + 'mail.mass_mailing.test': 'mailing.mailing.test', + 'mass.mailing.list.merge': 'mailing.list.merge', + 'mass.mailing.schedule.date': 'mailing.mailing.schedule.date', + 'mrp.subproduct': 'mrp.bom.byproduct', + 'sms.send_sms': 'sms.composer', + 'stock.fixed.putaway.strat': 'stock.putaway.rule', + 'survey.mail.compose.message': 'survey.invite', + 'website.redirect': 'website.rewrite', + # OCA/... +} + +# only used here for openupgrade_records analysis: +merged_models = { + # Odoo + 'account.invoice': 'account.move', + 'account.invoice.line': 'account.move.line', + 'account.invoice.tax': 'account.move.line', + 'account.voucher': 'account.move', + 'account.voucher.line': 'account.move.line', + 'lunch.order.line': 'lunch.order', + 'mail.mass_mailing.campaign': 'utm.campaign', + 'slide.category': 'slide.slide', + 'survey.page': 'survey.question', + # OCA/... +} diff --git a/upgrade_analysis/compare.py b/upgrade_analysis/compare.py new file mode 100644 index 00000000000..52ac7bbe891 --- /dev/null +++ b/upgrade_analysis/compare.py @@ -0,0 +1,438 @@ +# coding: utf-8 +# Copyright 2011-2015 Therp BV +# Copyright 2015-2016 Opener B.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +##################################################################### +# library providing a function to analyse two progressive database +# layouts from the OpenUpgrade server. +##################################################################### + +import collections +import copy + +from odoo.addons.openupgrade_records.lib import apriori + + +def module_map(module): + return apriori.renamed_modules.get( + module, apriori.merged_modules.get(module, module)) + + +def model_rename_map(model): + return apriori.renamed_models.get(model, model) + + +def model_map(model): + return apriori.renamed_models.get( + model, apriori.merged_models.get(model, model)) + + +def inv_model_map(model): + inv_model_map_dict = {v: k for k, v in apriori.renamed_models.items()} + return inv_model_map_dict.get(model, model) + + +IGNORE_FIELDS = [ + 'create_date', + 'create_uid', + 'id', + 'write_date', + 'write_uid', + ] + + +def compare_records(dict_old, dict_new, fields): + """ + Check equivalence of two OpenUpgrade field representations + with respect to the keys in the 'fields' arguments. + Take apriori knowledge into account for mapped modules or + model names. + Return True of False. + """ + for field in fields: + if field == 'module': + if module_map(dict_old['module']) != dict_new['module']: + return False + elif field == 'model': + if model_rename_map(dict_old['model']) != dict_new['model']: + return False + elif field == 'other_prefix': + if dict_old['module'] != dict_old['prefix'] or \ + dict_new['module'] != dict_new['prefix']: + return False + if dict_old['model'] == 'ir.ui.view': + # basically, to avoid the assets_backend case + return False + elif dict_old[field] != dict_new[field]: + return False + return True + + +def search(item, item_list, fields): + """ + Find a match of a dictionary in a list of similar dictionaries + with respect to the keys in the 'fields' arguments. + Return the item if found or None. + """ + for other in item_list: + if not compare_records(item, other, fields): + continue + return other + # search for renamed fields + if 'field' in fields: + for other in item_list: + if not item['field'] or item['field'] is not None or \ + item['isproperty']: + continue + if compare_records( + dict(item, field=other['field']), other, fields): + return other + return None + + +def fieldprint(old, new, field, text, reprs): + fieldrepr = "%s (%s)" % (old['field'], old['type']) + fullrepr = '%-12s / %-24s / %-30s' % ( + old['module'], old['model'], fieldrepr) + if not text: + text = "%s is now '%s' ('%s')" % (field, new[field], old[field]) + if field == 'relation': + text += ' [nothing to do]' + reprs[module_map(old['module'])].append("%s: %s" % (fullrepr, text)) + if field == 'module': + text = "previously in module %s" % old[field] + fullrepr = '%-12s / %-24s / %-30s' % ( + new['module'], old['model'], fieldrepr) + reprs[module_map(new['module'])].append("%s: %s" % (fullrepr, text)) + + +def report_generic(new, old, attrs, reprs): + for attr in attrs: + if attr == 'required': + if old[attr] != new['required'] and new['required']: + text = "now required" + if new['req_default']: + text += ', req_default: %s' % new['req_default'] + fieldprint(old, new, '', text, reprs) + elif attr == 'stored': + if old[attr] != new[attr]: + if new['stored']: + text = "is now stored" + else: + text = "not stored anymore" + fieldprint(old, new, '', text, reprs) + elif attr == 'isfunction': + if old[attr] != new[attr]: + if new['isfunction']: + text = "now a function" + else: + text = "not a function anymore" + fieldprint(old, new, '', text, reprs) + elif attr == 'isproperty': + if old[attr] != new[attr]: + if new[attr]: + text = "now a property" + else: + text = "not a property anymore" + fieldprint(old, new, '', text, reprs) + elif attr == 'isrelated': + if old[attr] != new[attr]: + if new[attr]: + text = "now related" + else: + text = "not related anymore" + fieldprint(old, new, '', text, reprs) + elif old[attr] != new[attr]: + fieldprint(old, new, attr, '', reprs) + + +def compare_sets(old_records, new_records): + """ + Compare a set of OpenUpgrade field representations. + Try to match the equivalent fields in both sets. + Return a textual representation of changes in a dictionary with + module names as keys. Special case is the 'general' key + which contains overall remarks and matching statistics. + """ + reprs = collections.defaultdict(list) + + def clean_records(records): + result = [] + for record in records: + if record['field'] not in IGNORE_FIELDS: + result.append(record) + return result + + old_records = clean_records(old_records) + new_records = clean_records(new_records) + + origlen = len(old_records) + new_models = set([column['model'] for column in new_records]) + old_models = set([column['model'] for column in old_records]) + + matched_direct = 0 + matched_other_module = 0 + matched_other_type = 0 + in_obsolete_models = 0 + + obsolete_models = [] + for model in old_models: + if model not in new_models: + if model_map(model) not in new_models: + obsolete_models.append(model) + + non_obsolete_old_records = [] + for column in copy.copy(old_records): + if column['model'] in obsolete_models: + in_obsolete_models += 1 + else: + non_obsolete_old_records.append(column) + + def match(match_fields, report_fields, warn=False): + count = 0 + for column in copy.copy(non_obsolete_old_records): + found = search(column, new_records, match_fields) + if found: + if warn: + pass + # print "Tentatively" + report_generic(found, column, report_fields, reprs) + old_records.remove(column) + non_obsolete_old_records.remove(column) + new_records.remove(found) + count += 1 + return count + + matched_direct = match( + ['module', 'mode', 'model', 'field'], + ['relation', 'type', 'selection_keys', 'inherits', 'stored', + 'isfunction', 'isrelated', 'required', 'table']) + + # other module, same type and operation + matched_other_module = match( + ['mode', 'model', 'field', 'type'], + ['module', 'relation', 'selection_keys', 'inherits', 'stored', + 'isfunction', 'isrelated', 'required', 'table']) + + # other module, same operation, other type + matched_other_type = match( + ['mode', 'model', 'field'], + ['relation', 'type', 'selection_keys', 'inherits', 'stored', + 'isfunction', 'isrelated', 'required', 'table']) + + printkeys = [ + 'relation', 'required', 'selection_keys', + 'req_default', 'inherits', 'mode', 'attachment', + ] + for column in old_records: + # we do not care about removed non stored function fields + if not column['stored'] and ( + column['isfunction'] or column['isrelated']): + continue + if column['mode'] == 'create': + column['mode'] = '' + extra_message = ", ".join( + [k + ': ' + str(column[k]) if k != str(column[k]) else k + for k in printkeys if column[k]] + ) + if extra_message: + extra_message = " " + extra_message + fieldprint( + column, '', '', "DEL" + extra_message, reprs) + + printkeys.extend([ + 'hasdefault', + ]) + for column in new_records: + # we do not care about newly added non stored function fields + if not column['stored'] and ( + column['isfunction'] or column['isrelated']): + continue + if column['mode'] == 'create': + column['mode'] = '' + printkeys_plus = printkeys.copy() + if column['isfunction'] or column['isrelated']: + printkeys_plus.extend(['isfunction', 'isrelated', 'stored']) + extra_message = ", ".join( + [k + ': ' + str(column[k]) if k != str(column[k]) else k + for k in printkeys_plus if column[k]] + ) + if extra_message: + extra_message = " " + extra_message + fieldprint( + column, '', '', "NEW" + extra_message, reprs) + + for line in [ + "# %d fields matched," % (origlen - len(old_records)), + "# Direct match: %d" % matched_direct, + "# Found in other module: %d" % matched_other_module, + "# Found with different type: %d" % matched_other_type, + "# In obsolete models: %d" % in_obsolete_models, + "# Not matched: %d" % len(old_records), + "# New columns: %d" % len(new_records) + ]: + reprs['general'].append(line) + return reprs + + +def compare_xml_sets(old_records, new_records): + reprs = collections.defaultdict(list) + + def match(match_fields, match_type='direct'): + matched_records = [] + for column in copy.copy(old_records): + found = search(column, new_records, match_fields) + if found: + old_records.remove(column) + new_records.remove(found) + if match_type != 'direct': + column['old'] = True + found['new'] = True + column[match_type] = found['module'] + found[match_type] = column['module'] + found['domain'] = column['domain'] != found['domain'] and \ + column['domain'] != '[]' and found['domain'] is False + column['domain'] = False + column['noupdate_switched'] = False + found['noupdate_switched'] = \ + column['noupdate'] != found['noupdate'] + if match_type != 'direct': + matched_records.append(column) + matched_records.append(found) + elif (match_type == 'direct' and found['domain']) or \ + found['noupdate_switched']: + matched_records.append(found) + return matched_records + + # direct match + modified_records = match(['module', 'model', 'name']) + + # other module, same full xmlid + moved_records = match(['model', 'name'], 'moved') + + # other module, same suffix, other prefix + renamed_records = match(['model', 'suffix', 'other_prefix'], 'renamed') + + for record in old_records: + record['old'] = True + record['domain'] = False + record['noupdate_switched'] = False + for record in new_records: + record['new'] = True + record['domain'] = False + record['noupdate_switched'] = False + + sorted_records = sorted( + old_records + new_records + moved_records + renamed_records + + modified_records, + key=lambda k: (k['model'], 'old' in k, k['name']) + ) + for entry in sorted_records: + content = '' + if 'old' in entry: + content = 'DEL %(model)s: %(name)s' % entry + if 'moved' in entry: + content += ' [potentially moved to %(moved)s module]' % entry + elif 'renamed' in entry: + content += ' [renamed to %(renamed)s module]' % entry + elif 'new' in entry: + content = 'NEW %(model)s: %(name)s' % entry + if 'moved' in entry: + content += ' [potentially moved from %(moved)s module]' % entry + elif 'renamed' in entry: + content += ' [renamed from %(renamed)s module]' % entry + if 'old' not in entry and 'new' not in entry: + content = '%(model)s: %(name)s' % entry + if entry['domain']: + content += ' (deleted domain)' + if entry['noupdate']: + content += ' (noupdate)' + if entry['noupdate_switched']: + content += ' (noupdate switched)' + reprs[module_map(entry['module'])].append(content) + return reprs + + +def compare_model_sets(old_records, new_records): + """ + Compare a set of OpenUpgrade model representations. + """ + reprs = collections.defaultdict(list) + + new_models = {column['model']: column['module'] for column in new_records} + old_models = {column['model']: column['module'] for column in old_records} + + obsolete_models = [] + for column in copy.copy(old_records): + model = column['model'] + if model in old_models: + if model not in new_models: + if model_map(model) not in new_models: + obsolete_models.append(model) + text = 'obsolete model %s' % model + if column['model_type']: + text += " [%s]" % column['model_type'] + reprs[module_map(column['module'])].append(text) + reprs['general'].append('obsolete model %s [module %s]' % ( + model, module_map(column['module']))) + else: + moved_module = '' + if module_map(column['module']) != new_models[model_map( + model)]: + moved_module = ' in module %s' % new_models[model_map( + model)] + text = 'obsolete model %s (renamed to %s%s)' % ( + model, model_map(model), moved_module) + if column['model_type']: + text += " [%s]" % column['model_type'] + reprs[module_map(column['module'])].append(text) + reprs['general'].append( + 'obsolete model %s (renamed to %s) [module %s]' % ( + model, model_map(model), + module_map(column['module']))) + else: + if module_map(column['module']) != new_models[model]: + text = 'model %s (moved to %s)' % ( + model, new_models[model]) + if column['model_type']: + text += " [%s]" % column['model_type'] + reprs[module_map(column['module'])].append(text) + text = 'model %s (moved from %s)' % ( + model, old_models[model]) + if column['model_type']: + text += " [%s]" % column['model_type'] + + for column in copy.copy(new_records): + model = column['model'] + if model in new_models: + if model not in old_models: + if inv_model_map(model) not in old_models: + text = 'new model %s' % model + if column['model_type']: + text += " [%s]" % column['model_type'] + reprs[column['module']].append(text) + reprs['general'].append('new model %s [module %s]' % ( + model, column['module'])) + else: + moved_module = '' + if column['module'] != module_map(old_models[inv_model_map( + model)]): + moved_module = ' in module %s' % old_models[ + inv_model_map(model)] + text = 'new model %s (renamed from %s%s)' % ( + model, inv_model_map(model), moved_module) + if column['model_type']: + text += " [%s]" % column['model_type'] + reprs[column['module']].append(text) + reprs['general'].append( + 'new model %s (renamed from %s) [module %s]' % ( + model, inv_model_map(model), column['module'])) + else: + if column['module'] != module_map(old_models[model]): + text = 'model %s (moved from %s)' % ( + model, old_models[model]) + if column['model_type']: + text += " [%s]" % column['model_type'] + reprs[column['module']].append(text) + return reprs From c354a44845bbce84ac215e6b890e29530fd96d33 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Fri, 6 Nov 2020 22:24:04 +0100 Subject: [PATCH 190/770] [REF] rename module openupgrade_records into upgrade_analysis --- upgrade_analysis/README.rst | 59 ++++++ upgrade_analysis/__init__.py | 2 + upgrade_analysis/__manifest__.py | 23 +++ upgrade_analysis/blacklist.py | 7 + upgrade_analysis/models/__init__.py | 5 + upgrade_analysis/models/analysis_wizard.py | 182 ++++++++++++++++++ upgrade_analysis/models/comparison_config.py | 88 +++++++++ .../models/generate_records_wizard.py | 119 ++++++++++++ upgrade_analysis/models/install_all_wizard.py | 51 +++++ upgrade_analysis/models/openupgrade_record.py | 113 +++++++++++ upgrade_analysis/security/ir.model.access.csv | 4 + upgrade_analysis/views/analysis_wizard.xml | 36 ++++ upgrade_analysis/views/comparison_config.xml | 77 ++++++++ .../views/generate_records_wizard.xml | 47 +++++ upgrade_analysis/views/install_all_wizard.xml | 48 +++++ upgrade_analysis/views/openupgrade_record.xml | 96 +++++++++ 16 files changed, 957 insertions(+) create mode 100644 upgrade_analysis/README.rst create mode 100644 upgrade_analysis/__init__.py create mode 100644 upgrade_analysis/__manifest__.py create mode 100644 upgrade_analysis/blacklist.py create mode 100644 upgrade_analysis/models/__init__.py create mode 100644 upgrade_analysis/models/analysis_wizard.py create mode 100644 upgrade_analysis/models/comparison_config.py create mode 100644 upgrade_analysis/models/generate_records_wizard.py create mode 100644 upgrade_analysis/models/install_all_wizard.py create mode 100644 upgrade_analysis/models/openupgrade_record.py create mode 100644 upgrade_analysis/security/ir.model.access.csv create mode 100644 upgrade_analysis/views/analysis_wizard.xml create mode 100644 upgrade_analysis/views/comparison_config.xml create mode 100644 upgrade_analysis/views/generate_records_wizard.xml create mode 100644 upgrade_analysis/views/install_all_wizard.xml create mode 100644 upgrade_analysis/views/openupgrade_record.xml diff --git a/upgrade_analysis/README.rst b/upgrade_analysis/README.rst new file mode 100644 index 00000000000..f3d1c48de1d --- /dev/null +++ b/upgrade_analysis/README.rst @@ -0,0 +1,59 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +=============================== +OpenUpgrade Database Comparison +=============================== + +This module provides the tool to generate the database analysis files that indicate how the Odoo data model and module data have changed between two versions of Odoo. Database analysis files for the core modules are included in the OpenUpgrade distribution so as a migration script developer you will not usually need to use this tool yourself. If you do need to run your analysis of a custom set of modules, please refer to the documentation here: https://doc.therp.nl/openupgrade/analysis.html + +Installation +============ + +This module has a python dependency on openerp-client-lib. You need to make this module available in your Python environment, for instance by installing it with the pip tool. + +Known issues / Roadmap +====================== + +* scripts/compare_noupdate_xml_records.py should be integrated in the analysis process (#590) +* Log removed modules in the module that owned them (#468) +* Detect renamed many2many tables (#213) + +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 smash it by providing detailed and welcomed feedback. + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Stefan Rijnhart +* Holger Brunn +* Pedro M. Baeza +* Ferdinand Gassauer +* Florent Xicluna +* Miquel Raïch + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/upgrade_analysis/__init__.py b/upgrade_analysis/__init__.py new file mode 100644 index 00000000000..c102a8ca668 --- /dev/null +++ b/upgrade_analysis/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import blacklist diff --git a/upgrade_analysis/__manifest__.py b/upgrade_analysis/__manifest__.py new file mode 100644 index 00000000000..79ebfc939b6 --- /dev/null +++ b/upgrade_analysis/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2011-2015 Therp BV +# Copyright 2016 Opener B.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "OpenUpgrade Records", + "version": "14.0.1.0.0", + "category": "Migration", + "author": "Therp BV, Opener B.V., Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-tools", + "data": [ + "views/openupgrade_record.xml", + "views/comparison_config.xml", + "views/analysis_wizard.xml", + "views/generate_records_wizard.xml", + "views/install_all_wizard.xml", + "security/ir.model.access.csv", + ], + "installable": True, + "external_dependencies": { + "python": ["odoorpc", "openupgradelib"], + }, + "license": "AGPL-3", +} diff --git a/upgrade_analysis/blacklist.py b/upgrade_analysis/blacklist.py new file mode 100644 index 00000000000..814396ad692 --- /dev/null +++ b/upgrade_analysis/blacklist.py @@ -0,0 +1,7 @@ +BLACKLIST_MODULES = [ + # the hw_* modules are not affected by a migration as they don't + # contain any ORM functionality, but they do start up threads that + # delay the process and spit out annoying log messages continously. + "hw_escpos", + "hw_proxy", +] diff --git a/upgrade_analysis/models/__init__.py b/upgrade_analysis/models/__init__.py new file mode 100644 index 00000000000..6e445842447 --- /dev/null +++ b/upgrade_analysis/models/__init__.py @@ -0,0 +1,5 @@ +from . import openupgrade_record +from . import comparison_config +from . import analysis_wizard +from . import generate_records_wizard +from . import install_all_wizard diff --git a/upgrade_analysis/models/analysis_wizard.py b/upgrade_analysis/models/analysis_wizard.py new file mode 100644 index 00000000000..61a5e208203 --- /dev/null +++ b/upgrade_analysis/models/analysis_wizard.py @@ -0,0 +1,182 @@ +# Copyright 2011-2015 Therp BV +# Copyright 2016 Opener B.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# flake8: noqa: C901 + +import os + +from odoo import fields, models +from odoo.modules import get_module_path + +from ..lib import compare + + +class AnalysisWizard(models.TransientModel): + _name = "openupgrade.analysis.wizard" + _description = "OpenUpgrade Analysis Wizard" + + server_config = fields.Many2one( + "openupgrade.comparison.config", "Configuration", required=True + ) + state = fields.Selection( + [("init", "Init"), ("ready", "Ready")], readonly=True, default="init" + ) + log = fields.Text() + write_files = fields.Boolean( + help="Write analysis files to the module directories", default=True + ) + + def get_communication(self): + """ + Retrieve both sets of database representations, + perform the comparison and register the resulting + change set + """ + + def write_file(module, version, content, filename="openupgrade_analysis.txt"): + module_path = get_module_path(module) + if not module_path: + return "ERROR: could not find module path:\n" + full_path = os.path.join(module_path, "migrations", version) + if not os.path.exists(full_path): + try: + os.makedirs(full_path) + except os.error: + return "ERROR: could not create migrations directory:\n" + logfile = os.path.join(full_path, filename) + try: + f = open(logfile, "w") + except Exception: + return "ERROR: could not open file %s for writing:\n" % logfile + f.write(content) + f.close() + return None + + self.ensure_one() + connection = self.server_config.get_connection() + remote_record_obj = connection.env["openupgrade.record"] + local_record_obj = self.env["openupgrade.record"] + + # Retrieve field representations and compare + remote_records = remote_record_obj.field_dump() + local_records = local_record_obj.field_dump() + res = compare.compare_sets(remote_records, local_records) + + # Retrieve xml id representations and compare + flds = [ + "module", + "model", + "name", + "noupdate", + "prefix", + "suffix", + "domain", + ] + local_xml_records = [ + {field: record[field] for field in flds} + for record in local_record_obj.search([("type", "=", "xmlid")]) + ] + remote_xml_record_ids = remote_record_obj.search([("type", "=", "xmlid")]) + remote_xml_records = [ + {field: record[field] for field in flds} + for record in remote_record_obj.read(remote_xml_record_ids, flds) + ] + res_xml = compare.compare_xml_sets(remote_xml_records, local_xml_records) + + # Retrieve model representations and compare + flds = [ + "module", + "model", + "name", + "model_original_module", + "model_type", + ] + local_model_records = [ + {field: record[field] for field in flds} + for record in local_record_obj.search([("type", "=", "model")]) + ] + remote_model_record_ids = remote_record_obj.search([("type", "=", "model")]) + remote_model_records = [ + {field: record[field] for field in flds} + for record in remote_record_obj.read(remote_model_record_ids, flds) + ] + res_model = compare.compare_model_sets( + remote_model_records, local_model_records + ) + + affected_modules = sorted( + { + record["module"] + for record in remote_records + + local_records + + remote_xml_records + + local_xml_records + + remote_model_records + + local_model_records + } + ) + + # reorder and output the result + keys = ["general"] + affected_modules + modules = { + module["name"]: module + for module in self.env["ir.module.module"].search( + [("state", "=", "installed")] + ) + } + general = "" + for key in keys: + contents = "---Models in module '%s'---\n" % key + if key in res_model: + contents += "\n".join([str(line) for line in res_model[key]]) + if res_model[key]: + contents += "\n" + contents += "---Fields in module '%s'---\n" % key + if key in res: + contents += "\n".join([str(line) for line in sorted(res[key])]) + if res[key]: + contents += "\n" + contents += "---XML records in module '%s'---\n" % key + if key in res_xml: + contents += "\n".join([str(line) for line in res_xml[key]]) + if res_xml[key]: + contents += "\n" + if key not in res and key not in res_xml and key not in res_model: + contents += "---nothing has changed in this module--\n" + if key == "general": + general += contents + continue + if compare.module_map(key) not in modules: + general += ( + "ERROR: module not in list of installed modules:\n" + contents + ) + continue + if key not in modules: + # no need to log in general the merged/renamed modules + continue + if self.write_files: + error = write_file(key, modules[key].installed_version, contents) + if error: + general += error + general += contents + else: + general += contents + + # Store the general log in as many places as possible ;-) + if self.write_files and "base" in modules: + write_file( + "base", + modules["base"].installed_version, + general, + "openupgrade_general_log.txt", + ) + self.server_config.write({"last_log": general}) + self.write({"state": "ready", "log": general}) + + return { + "name": self._description, + "view_mode": "form", + "res_model": self._name, + "type": "ir.actions.act_window", + "res_id": self.id, + } diff --git a/upgrade_analysis/models/comparison_config.py b/upgrade_analysis/models/comparison_config.py new file mode 100644 index 00000000000..0e5274bdf8a --- /dev/null +++ b/upgrade_analysis/models/comparison_config.py @@ -0,0 +1,88 @@ +# Copyright 2011-2015 Therp BV +# Copyright 2016 Opener B.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import fields, models +from odoo.exceptions import UserError +from odoo.tools.translate import _ + +from ..lib import apriori + + +class OpenupgradeComparisonConfig(models.Model): + _name = "openupgrade.comparison.config" + _description = "OpenUpgrade Comparison Configuration" + + name = fields.Char() + server = fields.Char(required=True) + port = fields.Integer(required=True, default=8069) + protocol = fields.Selection( + [("http://", "XML-RPC")], + # ('https://', 'XML-RPC Secure')], not supported by libopenerp + required=True, + default="http://", + ) + database = fields.Char(required=True) + username = fields.Char(required=True) + password = fields.Char(required=True) + last_log = fields.Text() + + def get_connection(self): + self.ensure_one() + import odoorpc + + remote = odoorpc.ODOO(self.server, port=self.port) + remote.login(self.database, self.username, self.password) + return remote + + def test_connection(self): + self.ensure_one() + try: + connection = self.get_connection() + user_model = connection.env["res.users"] + ids = user_model.search([("login", "=", "admin")]) + user_info = user_model.read([ids[0]], ["name"])[0] + except Exception as e: + raise UserError(_("Connection failed.\n\nDETAIL: %s") % e) + raise UserError(_("%s is connected.") % user_info["name"]) + + def analyze(self): + """ Run the analysis wizard """ + self.ensure_one() + wizard = self.env["openupgrade.analysis.wizard"].create( + {"server_config": self.id} + ) + return { + "name": wizard._description, + "view_mode": "form", + "res_model": wizard._name, + "type": "ir.actions.act_window", + "target": "new", + "res_id": wizard.id, + "nodestroy": True, + } + + def install_modules(self): + """ Install same modules as in source DB """ + self.ensure_one() + connection = self.get_connection() + remote_module_obj = connection.env["ir.module.module"] + remote_module_ids = remote_module_obj.search([("state", "=", "installed")]) + + modules = [] + for module_id in remote_module_ids: + mod = remote_module_obj.read([module_id], ["name"])[0] + mod_name = mod["name"] + mod_name = apriori.renamed_modules.get(mod_name, mod_name) + modules.append(mod_name) + _logger = logging.getLogger(__name__) + _logger.debug("remote modules %s", modules) + local_modules = self.env["ir.module.module"].search( + [("name", "in", modules), ("state", "=", "uninstalled")] + ) + _logger.debug("local modules %s", ",".join(local_modules.mapped("name"))) + if local_modules: + local_modules.write({"state": "to install"}) + return {} diff --git a/upgrade_analysis/models/generate_records_wizard.py b/upgrade_analysis/models/generate_records_wizard.py new file mode 100644 index 00000000000..b09f6839e6e --- /dev/null +++ b/upgrade_analysis/models/generate_records_wizard.py @@ -0,0 +1,119 @@ +# Copyright 2011-2015 Therp BV +# Copyright 2016 Opener B.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade_tools + +from odoo import _, fields, models +from odoo.exceptions import UserError +from odoo.modules.registry import Registry + + +class GenerateWizard(models.TransientModel): + _name = "openupgrade.generate.records.wizard" + _description = "OpenUpgrade Generate Records Wizard" + _rec_name = "state" + + state = fields.Selection([("init", "init"), ("ready", "ready")], default="init") + + def quirk_standard_calendar_attendances(self): + """Introduced in Odoo 13. The reinstallation causes a one2many value + in [(0, 0, {})] format to be loaded on top of the first load, causing a + violation of database constraint.""" + for cal in ("resource_calendar_std_35h", "resource_calendar_std_38h"): + record = self.env.ref("resource.%s" % cal, False) + if record: + record.attendance_ids.unlink() + + def generate(self): + """Main wizard step. Make sure that all modules are up-to-date, + then reinitialize all installed modules. + Equivalent of running the server with '-d --init all' + + The goal of this is to fill the records table. + + TODO: update module list and versions, then update all modules?""" + # Truncate the records table + if openupgrade_tools.table_exists( + self.env.cr, "openupgrade_attribute" + ) and openupgrade_tools.table_exists(self.env.cr, "openupgrade_record"): + self.env.cr.execute("TRUNCATE openupgrade_attribute, openupgrade_record;") + + # Run any quirks + self.quirk_standard_calendar_attendances() + + # Need to get all modules in state 'installed' + modules = self.env["ir.module.module"].search( + [("state", "in", ["to install", "to upgrade"])] + ) + if modules: + self.env.cr.commit() # pylint: disable=invalid-commit + Registry.new(self.env.cr.dbname, update_module=True) + # Did we succeed above? + modules = self.env["ir.module.module"].search( + [("state", "in", ["to install", "to upgrade"])] + ) + if modules: + raise UserError( + _("Cannot seem to install or upgrade modules %s") + % (", ".join([module.name for module in modules])) + ) + # Now reinitialize all installed modules + self.env["ir.module.module"].search([("state", "=", "installed")]).write( + {"state": "to install"} + ) + self.env.cr.commit() # pylint: disable=invalid-commit + Registry.new(self.env.cr.dbname, update_module=True) + + # Set domain property + self.env.cr.execute( + """ UPDATE openupgrade_record our + SET domain = iaw.domain + FROM ir_model_data imd + JOIN ir_act_window iaw ON imd.res_id = iaw.id + WHERE our.type = 'xmlid' + AND imd.model = 'ir.actions.act_window' + AND our.model = imd.model + AND our.name = imd.module || '.' || imd.name + """ + ) + self.env.cache.invalidate( + [ + (self.env["openupgrade.record"]._fields["domain"], None), + ] + ) + + # Set noupdate property from ir_model_data + self.env.cr.execute( + """ UPDATE openupgrade_record our + SET noupdate = imd.noupdate + FROM ir_model_data imd + WHERE our.type = 'xmlid' + AND our.model = imd.model + AND our.name = imd.module || '.' || imd.name + """ + ) + self.env.cache.invalidate( + [ + (self.env["openupgrade.record"]._fields["noupdate"], None), + ] + ) + + # Log model records + self.env.cr.execute( + """INSERT INTO openupgrade_record + (module, name, model, type) + SELECT imd2.module, imd2.module || '.' || imd.name AS name, + im.model, 'model' AS type + FROM ( + SELECT min(id) as id, name, res_id + FROM ir_model_data + WHERE name LIKE 'model_%' AND model = 'ir.model' + GROUP BY name, res_id + ) imd + JOIN ir_model_data imd2 ON imd2.id = imd.id + JOIN ir_model im ON imd.res_id = im.id + ORDER BY imd.name, imd.id""", + ) + + return self.write({"state": "ready"}) diff --git a/upgrade_analysis/models/install_all_wizard.py b/upgrade_analysis/models/install_all_wizard.py new file mode 100644 index 00000000000..c1085418e6d --- /dev/null +++ b/upgrade_analysis/models/install_all_wizard.py @@ -0,0 +1,51 @@ +# Copyright 2011-2015 Therp BV +# Copyright 2016 Opener B.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.modules.registry import Registry +from odoo.osv.expression import AND + +from ..blacklist import BLACKLIST_MODULES + + +class InstallAll(models.TransientModel): + _name = "openupgrade.install.all.wizard" + _description = "OpenUpgrade Install All Wizard" + + state = fields.Selection( + [("init", "init"), ("ready", "ready")], readonly=True, default="init" + ) + to_install = fields.Integer("Number of modules to install", readonly=True) + + @api.model + def default_get(self, fields): + """Update module list and retrieve the number + of installable modules""" + res = super(InstallAll, self).default_get(fields) + update, add = self.env["ir.module.module"].update_list() + modules = self.env["ir.module.module"].search( + [("state", "not in", ["uninstallable", "unknown"])] + ) + res["to_install"] = len(modules) + return res + + def install_all(self, extra_domain=None): + """Main wizard step. Set all installable modules to install + and actually install them. Exclude testing modules.""" + domain = [ + "&", + "&", + ("state", "not in", ["uninstallable", "unknown"]), + ("category_id.name", "!=", "Tests"), + ("name", "not in", BLACKLIST_MODULES), + ] + if extra_domain: + domain = AND([domain, extra_domain]) + modules = self.env["ir.module.module"].search(domain) + if modules: + modules.write({"state": "to install"}) + self.env.cr.commit() # pylint: disable=invalid-commit + Registry.new(self.env.cr.dbname, update_module=True) + self.write({"state": "ready"}) + return True diff --git a/upgrade_analysis/models/openupgrade_record.py b/upgrade_analysis/models/openupgrade_record.py new file mode 100644 index 00000000000..80b5a8a3a36 --- /dev/null +++ b/upgrade_analysis/models/openupgrade_record.py @@ -0,0 +1,113 @@ +# Copyright 2011-2015 Therp BV +# Copyright 2016 Opener B.V. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class Attribute(models.Model): + _name = "openupgrade.attribute" + _description = "OpenUpgrade Attribute" + + name = fields.Char(readonly=True) + value = fields.Char(readonly=True) + record_id = fields.Many2one( + "openupgrade.record", + ondelete="CASCADE", + readonly=True, + ) + + +class Record(models.Model): + _name = "openupgrade.record" + _description = "OpenUpgrade Record" + + name = fields.Char(readonly=True) + module = fields.Char(readonly=True) + model = fields.Char(readonly=True) + field = fields.Char(readonly=True) + mode = fields.Selection( + [("create", "Create"), ("modify", "Modify")], + help="Set to Create if a field is newly created " + "in this module. If this module modifies an attribute of an " + "existing field, set to Modify.", + readonly=True, + ) + type = fields.Selection( # Uh oh, reserved keyword + [("field", "Field"), ("xmlid", "XML ID"), ("model", "Model")], + readonly=True, + ) + attribute_ids = fields.One2many("openupgrade.attribute", "record_id", readonly=True) + noupdate = fields.Boolean(readonly=True) + domain = fields.Char(readonly=True) + prefix = fields.Char(compute="_compute_prefix_and_suffix") + suffix = fields.Char(compute="_compute_prefix_and_suffix") + model_original_module = fields.Char(compute="_compute_model_original_module") + model_type = fields.Char(compute="_compute_model_type") + + @api.depends("name") + def _compute_prefix_and_suffix(self): + for rec in self: + rec.prefix, rec.suffix = rec.name.split(".", 1) + + @api.depends("model", "type") + def _compute_model_original_module(self): + for rec in self: + if rec.type == "model": + rec.model_original_module = self.env[rec.model]._original_module + else: + rec.model_original_module = "" + + @api.depends("model", "type") + def _compute_model_type(self): + for rec in self: + if rec.type == "model": + model = self.env[rec.model] + if model._auto and model._transient: + rec.model_type = "transient" + elif model._auto: + rec.model_type = "" + elif not model._auto and model._abstract: + rec.model_type = "abstract" + else: + rec.model_type = "sql_view" + else: + rec.model_type = "" + + @api.model + def field_dump(self): + keys = [ + "attachment", + "module", + "mode", + "model", + "field", + "type", + "isfunction", + "isproperty", + "isrelated", + "relation", + "required", + "stored", + "selection_keys", + "req_default", + "hasdefault", + "table", + "inherits", + ] + + template = {x: False for x in keys} + data = [] + for record in self.search([("type", "=", "field")]): + repre = template.copy() + repre.update( + { + "module": record.module, + "model": record.model, + "field": record.field, + "mode": record.mode, + } + ) + repre.update({x.name: x.value for x in record.attribute_ids}) + data.append(repre) + return data diff --git a/upgrade_analysis/security/ir.model.access.csv b/upgrade_analysis/security/ir.model.access.csv new file mode 100644 index 00000000000..2ab5e67c18b --- /dev/null +++ b/upgrade_analysis/security/ir.model.access.csv @@ -0,0 +1,4 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_openupgrade_record","openupgrade.record all","model_openupgrade_record",,1,0,0,0 +"access_openupgrade_attribute","openupgrade.attribute all","model_openupgrade_attribute",,1,0,0,0 +"access_openupgrade_comparison_config","openupgrade.comparison.config","model_openupgrade_comparison_config",base.group_system,1,1,1,1 diff --git a/upgrade_analysis/views/analysis_wizard.xml b/upgrade_analysis/views/analysis_wizard.xml new file mode 100644 index 00000000000..efaa5294f17 --- /dev/null +++ b/upgrade_analysis/views/analysis_wizard.xml @@ -0,0 +1,36 @@ + + + + + view.openupgrade.analysis_wizard.form + openupgrade.analysis.wizard + +
+ + + + + + +
+
+
+
+
+ +
diff --git a/upgrade_analysis/views/comparison_config.xml b/upgrade_analysis/views/comparison_config.xml new file mode 100644 index 00000000000..db0ae777052 --- /dev/null +++ b/upgrade_analysis/views/comparison_config.xml @@ -0,0 +1,77 @@ + + + + + view.openupgrade.comparison_config.tree + openupgrade.comparison.config + + + + + + + + + + + + + view.openupgrade.comparison_config.form + openupgrade.comparison.config + +
+ + + + + + + + + +
+ + + + + + + + + + +