diff --git a/openupgrade_framework/README.rst b/openupgrade_framework/README.rst new file mode 100644 index 000000000000..e26c73a2ca96 --- /dev/null +++ b/openupgrade_framework/README.rst @@ -0,0 +1,214 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +===================== +Openupgrade Framework +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:5798eba54677737dfb52eec94d10de67ba298ebfdda05d8173dc4a802c8be38e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2FOpenUpgrade-lightgray.png?logo=github + :target: https://github.com/OCA/OpenUpgrade/tree/20.0/openupgrade_framework + :alt: OCA/OpenUpgrade +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/OpenUpgrade-20-0/OpenUpgrade-20-0-openupgrade_framework + :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/OpenUpgrade&target_branch=20.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is a technical module that contains a number of +monkeypatches to improve the behaviour of Odoo when migrating your +database using the OpenUpgrade migration scripts: + +- Prevent dropping columns or tables in the database when fields or + models are obsoleted in the Odoo data model of the target release. + After the migration, you can review and delete unused database tables + and columns using database_cleanup. See + https://odoo-community.org/shop/product/database-cleanup-918 +- When data records are deleted during the migration (such as views or + other system records), this is done in a secure mode. If the deletion + fails because of some unforeseen dependency, the deletion will be + cancelled and a message is logged, after which the migration + continues. +- Prevent a number of log messages that do not apply when using + OpenUpgrade. +- Suppress log messages about failed view validation, which are to be + expected during a migration. +- Run migration scripts for modules that are installed as new + dependencies of upgraded modules (when there are such scripts for + those particular modules) +- Production databases generated with demo data, will be transformed to + non-demo ones. If you want to avoid that, you have to pass through the + environment variable OPENUPGRADE_USE_DEMO, the value "yes". + +For detailed documentation see: + +- https://github.com/OCA/OpenUpgrade/ +- https://oca.github.io/OpenUpgrade + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +This module does not need to be installed on a database. It simply needs +to be available via your ``addons-path``. + +Configuration +============= + +- call your odoo instance with the option + ``--load=base,web,openupgrade_framework`` + +or + +- add the key to your configuration file: + +.. code:: shell + + [options] + server_wide_modules = web,openupgrade_framework + +When you load the module in either way of these ways, and you have the +openupgrade_scripts module in your addons path available, the +--upgrade-path option of Odoo will be set automatically to the location +of the OpenUpgrade migration scripts. + +Development +=========== + +The odoo_patch folder contains python files in a tree that mimicks the +folder tree of the Odoo project. It contains a number of monkey patches +to improve the migration of an Odoo database between two major versions. + +So far, we are able to make everything work without overwriting large +blocks of code, but if larger patches need to be added, please use the +method described below: + +To see the patches added, you can use ``meld`` tools: + +``meld PATH_TO_ODOO_FOLDER/odoo/ PATH_TO_OPENUPGRADE_FRAMEWORK_MODULE/odoo_patch`` + +To make more easy the diff analysis : + +- Make sure the python files has the same path as the original one. +- Keep the same indentation as the original file. (using ``if True:`` if + required) +- Add the following two lines at the beginning of your file, to avoid + flake8 / pylint errors + +.. code:: python + + # flake8: noqa + # pylint: skip-file + +- When you want to change the code. add the following tags: + +For an addition: + +.. code:: python + + # + some code... + # + +For a change: + +.. code:: python + + # + some code... + # + +For a removal: + +.. code:: python + + # + # Comment the code, instead of removing it. + # + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Therp BV +* Opener B.V. +* GRAP +* Hunki Enterprises BV + +Contributors +------------ + +- Stefan Rijnhart +- Sylvain LE GAL + +Other credits +------------- + +Many developers have contributed to the OpenUpgrade framework in its +previous incarnation. Their original contributions may no longer needed, +or they are no longer recognizable in their current form but OpenUpgrade +would not have existed at this point without them. + +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 +.. |maintainer-StefanRijnhart| image:: https://github.com/StefanRijnhart.png?size=40px + :target: https://github.com/StefanRijnhart + :alt: StefanRijnhart +.. |maintainer-hbrunn| image:: https://github.com/hbrunn.png?size=40px + :target: https://github.com/hbrunn + :alt: hbrunn + +Current `maintainers `__: + +|maintainer-legalsylvain| |maintainer-StefanRijnhart| |maintainer-hbrunn| + +This module is part of the `OCA/OpenUpgrade `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/openupgrade_framework/__init__.py b/openupgrade_framework/__init__.py new file mode 100644 index 000000000000..97a704fd9e63 --- /dev/null +++ b/openupgrade_framework/__init__.py @@ -0,0 +1,33 @@ +import logging +import os + +from odoo.modules import get_module_path +from odoo.tools import config + +from . import odoo_patch + +if not config.get("upgrade_path"): + path = get_module_path("openupgrade_scripts", display_warning=False) + if path and os.path.isdir(os.path.join(path, "scripts")): + logging.getLogger(__name__).info( + "Setting upgrade_path to the scripts directory inside the module " + "location of openupgrade_scripts" + ) + config["upgrade_path"] = os.path.join(path, "scripts") + + +def openupgrade_test(cls): + """ + Set attributes on a test class necessary for the test framework + Use as decorator on test classes in openupgrade_scripts/scripts/*/tests/test_*.py + """ + tags = getattr(cls, "test_tags", None) or set() + if "openupgrade" not in tags: + tags.add("openupgrade") + if not any(t.endswith("_install") for t in tags): + tags.add("at_install") + cls.test_tags = tags + cls.test_module = cls.__module__.split(".")[2] + cls.test_class = cls.__name__ + cls.test_sequence = 0 + return cls diff --git a/openupgrade_framework/__manifest__.py b/openupgrade_framework/__manifest__.py new file mode 100644 index 000000000000..09f4bf7a264a --- /dev/null +++ b/openupgrade_framework/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Openupgrade Framework", + "summary": "Module to add in the server_wide_modules " + "option to make upgrades between two major revisions.", + "author": "Odoo Community Association (OCA), Therp BV, Opener B.V., GRAP, " + "Hunki Enterprises BV", + "maintainers": ["legalsylvain", "StefanRijnhart", "hbrunn"], + "website": "https://github.com/OCA/OpenUpgrade", + "category": "Migration", + "version": "20.0.1.0.0", + "license": "AGPL-3", + "depends": ["base"], + "images": ["static/description/banner.jpg"], + "external_dependencies": {"python": ["openupgradelib"]}, + "installable": True, +} diff --git a/openupgrade_framework/odoo_patch/__init__.py b/openupgrade_framework/odoo_patch/__init__.py new file mode 100644 index 000000000000..3c691cd11703 --- /dev/null +++ b/openupgrade_framework/odoo_patch/__init__.py @@ -0,0 +1 @@ +from . import odoo diff --git a/openupgrade_framework/odoo_patch/odoo/__init__.py b/openupgrade_framework/odoo_patch/odoo/__init__.py new file mode 100644 index 000000000000..18ff659459d2 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/__init__.py @@ -0,0 +1 @@ +from . import addons, modules, orm diff --git a/openupgrade_framework/odoo_patch/odoo/addons/__init__.py b/openupgrade_framework/odoo_patch/odoo/addons/__init__.py new file mode 100644 index 000000000000..0e44449338cf --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/__init__.py @@ -0,0 +1 @@ +from . import base diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/__init__.py b/openupgrade_framework/odoo_patch/odoo/addons/base/__init__.py new file mode 100644 index 000000000000..0650744f6bc6 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/__init__.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/__init__.py new file mode 100644 index 000000000000..5ec7e0740f90 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/__init__.py @@ -0,0 +1,3 @@ +from . import ir_model +from . import ir_module +from . import ir_ui_view diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py new file mode 100644 index 000000000000..94cda8c31595 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py @@ -0,0 +1,75 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openupgradelib import openupgrade + +from odoo import api, models + +from odoo.addons.base.models.ir_model import ( + IrModel, + IrModelFields, + IrModelRelation, +) + + +def _drop_table(self): + """Never drop tables""" + for model in self: + if self.env.get(model.model) is not None: + openupgrade.message( + self.env.cr, + self.env.context.get("module", "Unknown"), + False, + False, + "Not dropping the table or view of model %s", + model.model, + ) + + +IrModel._drop_table = _drop_table + + +@api.ondelete(at_uninstall=False) +def _unlink_related_attachments(self): + """Never delete attachments (incl. binary field data) of removed models""" + for model in self: + openupgrade.message( + self.env.cr, + self.env.context.get("module", "Unknown"), + False, + False, + "Not deleting the attachments of model %s", + model.model, + ) + + +IrModel._unlink_related_attachments = _unlink_related_attachments + + +def _drop_column(self): + """Never drop columns""" + for field in self: + if field.name in models.MAGIC_COLUMNS: + continue + openupgrade.message( + self.env.cr, + self.env.context.get("module", "Unknown"), + False, + False, + "Not dropping the column of field %s of model %s", + field.name, + field.model, + ) + continue + + +IrModelFields._drop_column = _drop_column + + +def _module_data_uninstall(self): + """Don't delete many2many relation tables. Only unlink the + ir.model.relation record itself. + """ + self.unlink() + + +IrModelRelation._module_data_uninstall = _module_data_uninstall diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_module.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_module.py new file mode 100644 index 000000000000..88a858de34b9 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_module.py @@ -0,0 +1,63 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api +from odoo.fields import Domain + +from odoo.addons.base.models.ir_module import IrModuleModule + + +@api.model +def update_list(self): + """ + Mark auto_install modules as to install if all their dependencies are some kind of + installed. + Ignore localization modules that are set to auto_install + """ + result = IrModuleModule.update_list._original_method(self) + new_auto_install_modules = self.browse([]) + for module in self.env["ir.module.module"].search( + Domain( + [ + ("auto_install", "=", True), + ("state", "=", "uninstalled"), + ("name", "not like", ("l10n_%")), + ] + ) + & Domain.OR( + [ + Domain("country_ids", "=", False), + Domain( + "country_ids", + "in", + self.env["res.company"].search([]).country_id.ids, + ), + ] + ) + ): + if all( + state in ("to upgrade", "to install", "installed") + for state in module.dependencies_id.mapped("state") + ): + new_auto_install_modules |= module + if new_auto_install_modules: + new_auto_install_modules.button_install() + return result + + +def check_external_dependencies(self, module_name, newstate="to install"): + try: + IrModuleModule.check_external_dependencies._original_method( + self, module_name, newstate=newstate + ) + except AttributeError: # pylint: disable=except-pass + # this happens when a module is installed that doesn't exist in the new version + pass + + +update_list._original_method = IrModuleModule.update_list +IrModuleModule.update_list = update_list +check_external_dependencies._original_method = ( + IrModuleModule.check_external_dependencies +) +IrModuleModule.check_external_dependencies = check_external_dependencies diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_ui_view.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_ui_view.py new file mode 100644 index 000000000000..f4c5ee43f87b --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_ui_view.py @@ -0,0 +1,67 @@ +# Copyright 2024 Viindoo Technology Joint Stock Company (Viindoo) +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + +from odoo.modules.module import get_resource_from_path +from odoo.tools import mute_logger + +from odoo.addons.base.models.ir_ui_view import IrUiView + +_logger = logging.getLogger(__name__) + + +def _check_xml(self): + """Don't do any view validation, as it will mostly fail during upgrades""" + + +def _raise_view_error( + self, message, node=None, *, from_exception=None, from_traceback=None +): + """Don't raise or log exceptions in view validation unless explicitly + requested + """ + raise_exception = self.env.context.get("raise_view_error") + to_mute = "odoo.addons.base.models.ir_ui_view" if raise_exception else "not_muted" + with mute_logger(to_mute): + try: + return IrUiView._raise_view_error._original_method( + self, + message, + node=node, + from_exception=from_exception, + from_traceback=from_traceback, + ) + except ValueError as e: + _logger.warning( + "Can't render custom view %s for model %s. " + "Assuming you are migrating between major versions of Odoo. " + "Please review the view contents manually after the migration.\n" + "Error: %s", + self.xml_id, + self.model, + e, + ) + + +def _inverse_arch(self): + """ + Remove install_filename from context if it's from openupgrade_scripts. + Without this, arch_fs will point to openupgrade_scripts' file which most likely + won't exist when the migrated database is deployed, which breaks resetting views + """ + if "install_filename" in self.env.context: + path_info = get_resource_from_path(self.env.context["install_filename"]) + if path_info and path_info[0] == "openupgrade_scripts": + self = self.with_context( # pylint: disable=context-overridden + {k: v for k, v in self.env.context.items() if k != "install_filename"} + ) + return _inverse_arch._original_method(self) + + +_check_xml._original_method = IrUiView._check_xml +IrUiView._check_xml = _check_xml +_raise_view_error._original_method = IrUiView._raise_view_error +IrUiView._raise_view_error = _raise_view_error +_inverse_arch._original_method = IrUiView._inverse_arch +IrUiView._inverse_arch = _inverse_arch diff --git a/openupgrade_framework/odoo_patch/odoo/modules/__init__.py b/openupgrade_framework/odoo_patch/odoo/modules/__init__.py new file mode 100644 index 000000000000..e060fc2ed96b --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/modules/__init__.py @@ -0,0 +1 @@ +from . import module_graph, loading, migration diff --git a/openupgrade_framework/odoo_patch/odoo/modules/loading.py b/openupgrade_framework/odoo_patch/odoo/modules/loading.py new file mode 100644 index 000000000000..3a5424f476ce --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/modules/loading.py @@ -0,0 +1,18 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import odoo.modules.loading + + +def load_module_graph(env, graph, *args, **kwargs): + """ + Force run pre-migration scripts for modules being installed + """ + env.registry._force_upgrade_scripts.update({package.name for package in graph}) + return odoo.modules.loading.load_module_graph._original_method( + env, graph, *args, **kwargs + ) + + +load_module_graph._original_method = odoo.modules.loading.load_module_graph +odoo.modules.loading.load_module_graph = load_module_graph diff --git a/openupgrade_framework/odoo_patch/odoo/modules/migration.py b/openupgrade_framework/odoo_patch/odoo/modules/migration.py new file mode 100644 index 000000000000..fdc6f09eda1b --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/modules/migration.py @@ -0,0 +1,16 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.modules.migration import MigrationManager + + +def _get_files(self): + """Turns out Odoo SA sometimes add migration scripts that interfere with + OpenUpgrade. Those we filter out here""" + MigrationManager._get_files._original_method(self) + to_exclude = [("analytic", "1.2")] + for addon, version in to_exclude: + self.migrations.get(addon, {}).get("module", {}).pop(version, None) + + +_get_files._original_method = MigrationManager._get_files +MigrationManager._get_files = _get_files diff --git a/openupgrade_framework/odoo_patch/odoo/modules/module_graph.py b/openupgrade_framework/odoo_patch/odoo/modules/module_graph.py new file mode 100644 index 000000000000..8c04d26931ae --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/modules/module_graph.py @@ -0,0 +1,26 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import os + +import odoo +from odoo.modules.module_graph import ModuleGraph + + +def _update_from_database(self, *args, **kwargs) -> None: + """Prevent reloading of demo data from the new version on major upgrade""" + ModuleGraph._update_from_database._original_method(self, *args, **kwargs) + + if os.environ.get("OPENUPGRADE_USE_DEMO", "") == "yes": + return + if ( + "base" in self._modules + and self._modules["base"].demo + and self._modules["base"].installed_version < odoo.release.major_version + ): + self._cr.execute("UPDATE ir_module_module SET demo = false") + for module in self._modules.values(): + module.demo = False + + +_update_from_database._original_method = ModuleGraph._update_from_database +ModuleGraph._update_from_database = _update_from_database diff --git a/openupgrade_framework/odoo_patch/odoo/orm/__init__.py b/openupgrade_framework/odoo_patch/odoo/orm/__init__.py new file mode 100644 index 000000000000..0650744f6bc6 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/orm/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/openupgrade_framework/odoo_patch/odoo/orm/models.py b/openupgrade_framework/odoo_patch/odoo/orm/models.py new file mode 100644 index 000000000000..57a9cece3d60 --- /dev/null +++ b/openupgrade_framework/odoo_patch/odoo/orm/models.py @@ -0,0 +1,35 @@ +# Copyright Odoo Community Association (OCA) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + +from odoo.models import BaseModel + +from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG + +_logger = logging.getLogger(__name__) + + +def unlink(self): + """Don't break on unlink of obsolete records + when called from ir.model::_process_end() + + This only adapts the base unlink method. If overrides of this method + on individual models give problems, add patches for those as well. + """ + if not self.env.context.get(MODULE_UNINSTALL_FLAG): + return BaseModel.unlink._original_method(self) + try: + with self.env.cr.savepoint(): + return BaseModel.unlink._original_method(self) + except Exception as e: + _logger.warning( + "Could not delete obsolete record with ids %s of model %s", + self.ids, + self._name, + exc_info=e, + ) + return False + + +unlink._original_method = BaseModel.unlink +BaseModel.unlink = unlink diff --git a/openupgrade_framework/pyproject.toml b/openupgrade_framework/pyproject.toml new file mode 100644 index 000000000000..4231d0cccb3d --- /dev/null +++ b/openupgrade_framework/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/openupgrade_framework/readme/CONFIGURE.md b/openupgrade_framework/readme/CONFIGURE.md new file mode 100644 index 000000000000..13ef1e5b0946 --- /dev/null +++ b/openupgrade_framework/readme/CONFIGURE.md @@ -0,0 +1,16 @@ +- call your odoo instance with the option + `--load=base,web,openupgrade_framework` + +or + +- add the key to your configuration file: + +``` shell +[options] +server_wide_modules = web,openupgrade_framework +``` + +When you load the module in either way of these ways, and you have the +openupgrade_scripts module in your addons path available, the +--upgrade-path option of Odoo will be set automatically to the location +of the OpenUpgrade migration scripts. diff --git a/openupgrade_framework/readme/CONTRIBUTORS.md b/openupgrade_framework/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..a108409b034c --- /dev/null +++ b/openupgrade_framework/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Stefan Rijnhart \<\> +- Sylvain LE GAL \<\> diff --git a/openupgrade_framework/readme/CREDITS.md b/openupgrade_framework/readme/CREDITS.md new file mode 100644 index 000000000000..fa515612942e --- /dev/null +++ b/openupgrade_framework/readme/CREDITS.md @@ -0,0 +1,4 @@ +Many developers have contributed to the OpenUpgrade framework in its +previous incarnation. Their original contributions may no longer needed, +or they are no longer recognizable in their current form but OpenUpgrade +would not have existed at this point without them. diff --git a/openupgrade_framework/readme/DESCRIPTION.md b/openupgrade_framework/readme/DESCRIPTION.md new file mode 100644 index 000000000000..71cb68ac0b99 --- /dev/null +++ b/openupgrade_framework/readme/DESCRIPTION.md @@ -0,0 +1,29 @@ +This module is a technical module that contains a number of +monkeypatches to improve the behaviour of Odoo when migrating your +database using the OpenUpgrade migration scripts: + +- Prevent dropping columns or tables in the database when fields or + models are obsoleted in the Odoo data model of the target release. + After the migration, you can review and delete unused database tables + and columns using database_cleanup. See + +- When data records are deleted during the migration (such as views or + other system records), this is done in a secure mode. If the deletion + fails because of some unforeseen dependency, the deletion will be + cancelled and a message is logged, after which the migration + continues. +- Prevent a number of log messages that do not apply when using + OpenUpgrade. +- Suppress log messages about failed view validation, which are to be + expected during a migration. +- Run migration scripts for modules that are installed as new + dependencies of upgraded modules (when there are such scripts for + those particular modules) +- Production databases generated with demo data, will be transformed to + non-demo ones. If you want to avoid that, you have to pass through the + environment variable OPENUPGRADE_USE_DEMO, the value "yes". + +For detailed documentation see: + +- +- diff --git a/openupgrade_framework/readme/DEVELOP.md b/openupgrade_framework/readme/DEVELOP.md new file mode 100644 index 000000000000..9d4c548862be --- /dev/null +++ b/openupgrade_framework/readme/DEVELOP.md @@ -0,0 +1,50 @@ +The odoo_patch folder contains python files in a tree that mimicks the +folder tree of the Odoo project. It contains a number of monkey patches +to improve the migration of an Odoo database between two major versions. + +So far, we are able to make everything work without overwriting large +blocks of code, but if larger patches need to be added, please use the +method described below: + +To see the patches added, you can use `meld` tools: + +`meld PATH_TO_ODOO_FOLDER/odoo/ PATH_TO_OPENUPGRADE_FRAMEWORK_MODULE/odoo_patch` + +To make more easy the diff analysis : + +- Make sure the python files has the same path as the original one. +- Keep the same indentation as the original file. (using `if True:` if + required) +- Add the following two lines at the beginning of your file, to avoid + flake8 / pylint errors + +``` python +# flake8: noqa +# pylint: skip-file +``` + +- When you want to change the code. add the following tags: + +For an addition: + +``` python +# +some code... +# +``` + +For a change: + +``` python +# +some code... +# +``` + +For a removal: + +``` python +# +# Comment the code, instead of removing it. +# +``` diff --git a/openupgrade_framework/readme/INSTALL.md b/openupgrade_framework/readme/INSTALL.md new file mode 100644 index 000000000000..471a1d162ce1 --- /dev/null +++ b/openupgrade_framework/readme/INSTALL.md @@ -0,0 +1,2 @@ +This module does not need to be installed on a database. It simply needs +to be available via your `addons-path`. diff --git a/openupgrade_framework/static/description/banner.png b/openupgrade_framework/static/description/banner.png new file mode 100644 index 000000000000..9dd67ec04cf4 Binary files /dev/null and b/openupgrade_framework/static/description/banner.png differ diff --git a/openupgrade_framework/static/description/icon.png b/openupgrade_framework/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/openupgrade_framework/static/description/icon.png differ diff --git a/openupgrade_framework/static/description/index.html b/openupgrade_framework/static/description/index.html new file mode 100644 index 000000000000..543f2d15a681 --- /dev/null +++ b/openupgrade_framework/static/description/index.html @@ -0,0 +1,543 @@ + + + + + +Openupgrade Framework + + + +
+ + + +Odoo Community Association + +
+

Openupgrade Framework

+ +

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

+

This module is a technical module that contains a number of +monkeypatches to improve the behaviour of Odoo when migrating your +database using the OpenUpgrade migration scripts:

+
    +
  • Prevent dropping columns or tables in the database when fields or +models are obsoleted in the Odoo data model of the target release. +After the migration, you can review and delete unused database tables +and columns using database_cleanup. See +https://odoo-community.org/shop/product/database-cleanup-918
  • +
  • When data records are deleted during the migration (such as views or +other system records), this is done in a secure mode. If the deletion +fails because of some unforeseen dependency, the deletion will be +cancelled and a message is logged, after which the migration +continues.
  • +
  • Prevent a number of log messages that do not apply when using +OpenUpgrade.
  • +
  • Suppress log messages about failed view validation, which are to be +expected during a migration.
  • +
  • Run migration scripts for modules that are installed as new +dependencies of upgraded modules (when there are such scripts for +those particular modules)
  • +
  • Production databases generated with demo data, will be transformed to +non-demo ones. If you want to avoid that, you have to pass through the +environment variable OPENUPGRADE_USE_DEMO, the value “yes”.
  • +
+

For detailed documentation see:

+ +

Table of contents

+ +
+

Installation

+

This module does not need to be installed on a database. It simply needs +to be available via your addons-path.

+
+
+

Configuration

+
    +
  • call your odoo instance with the option +--load=base,web,openupgrade_framework
  • +
+

or

+
    +
  • add the key to your configuration file:
  • +
+
+[options]
+server_wide_modules = web,openupgrade_framework
+
+

When you load the module in either way of these ways, and you have the +openupgrade_scripts module in your addons path available, the +–upgrade-path option of Odoo will be set automatically to the location +of the OpenUpgrade migration scripts.

+
+
+

Development

+

The odoo_patch folder contains python files in a tree that mimicks the +folder tree of the Odoo project. It contains a number of monkey patches +to improve the migration of an Odoo database between two major versions.

+

So far, we are able to make everything work without overwriting large +blocks of code, but if larger patches need to be added, please use the +method described below:

+

To see the patches added, you can use meld tools:

+

meld PATH_TO_ODOO_FOLDER/odoo/ PATH_TO_OPENUPGRADE_FRAMEWORK_MODULE/odoo_patch

+

To make more easy the diff analysis :

+
    +
  • Make sure the python files has the same path as the original one.
  • +
  • Keep the same indentation as the original file. (using if True: if +required)
  • +
  • Add the following two lines at the beginning of your file, to avoid +flake8 / pylint errors
  • +
+
+# flake8: noqa
+# pylint: skip-file
+
+
    +
  • When you want to change the code. add the following tags:
  • +
+

For an addition:

+
+# <OpenUpgrade:ADD>
+some code...
+# </OpenUpgrade>
+
+

For a change:

+
+# <OpenUpgrade:CHANGE>
+some code...
+# </OpenUpgrade>
+
+

For a removal:

+
+# <OpenUpgrade:REMOVE>
+# Comment the code, instead of removing it.
+# </OpenUpgrade>
+
+
+
+

Bug Tracker

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
  • Opener B.V.
  • +
  • GRAP
  • +
  • Hunki Enterprises BV
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

Many developers have contributed to the OpenUpgrade framework in its +previous incarnation. Their original contributions may no longer needed, +or they are no longer recognizable in their current form but OpenUpgrade +would not have existed at this point without them.

+
+
+

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 maintainers:

+

legalsylvain StefanRijnhart hbrunn

+

This module is part of the OCA/OpenUpgrade project on GitHub.

+

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

+
+
+
+
+ + diff --git a/openupgrade_framework/tests/__init__.py b/openupgrade_framework/tests/__init__.py new file mode 100644 index 000000000000..f0425e721034 --- /dev/null +++ b/openupgrade_framework/tests/__init__.py @@ -0,0 +1 @@ +from . import test_openupgrade_framework diff --git a/openupgrade_framework/tests/test_openupgrade_framework.py b/openupgrade_framework/tests/test_openupgrade_framework.py new file mode 100644 index 000000000000..8b80da771d27 --- /dev/null +++ b/openupgrade_framework/tests/test_openupgrade_framework.py @@ -0,0 +1,94 @@ +from odoo import fields +from odoo.tests.common import TransactionCase +from odoo.tools.misc import mute_logger + +from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG + + +class TestOpenupgradeFramework(TransactionCase): + def test_01_delete_undeletable_record(self): + """ + Test that Odoo doesn't crash when deleting records that can't be deleted + during module upgrade + """ + with ( + mute_logger("odoo.sql_db"), + self.assertLogs( + "odoo.addons.openupgrade_framework.odoo_patch.odoo.orm.models" + ), + ): + self.env.ref("base.partner_admin").with_context( + **{MODULE_UNINSTALL_FLAG: True} + ).unlink() + + def test_02_invalid_view(self): + """ + Test that we patch away fatal view errors and log the problem + """ + base_view = self.env["ir.ui.view"].create( + { + "model": "res.partner", + "arch": '
', + } + ) + inherited_view1 = self.env["ir.ui.view"].create( + { + "model": "res.partner", + "inherit_id": base_view.id, + "arch": '', + } + ) + inherited_view2 = inherited_view1.copy() + with self.assertLogs( + "odoo.addons.openupgrade_framework.odoo_patch.odoo.addons.base.models.ir_ui_view", + ): + (inherited_view1 + inherited_view2)._get_combined_archs() + + def test_03_update_list(self): + """ + Test that updating the module list marks uninstalled auto_install modules as + to install if it's not a localization or country specific with a country not + in use + """ + IrModuleModule = self.env["ir.module.module"] + base = IrModuleModule.create( + { + "name": "openupgrade_framework_test_base", + "state": "installed", + } + ) + auto_install = IrModuleModule.create( + { + "name": "openupgrade_framework_test_auto_install", + "state": "uninstalled", + "auto_install": "true", + "dependencies_id": [ + fields.Command.create({"name": base.name}), + ], + } + ) + auto_install_localization = IrModuleModule.create( + { + "name": "l10n_openupgrade_framework_localization", + "state": "uninstalled", + "auto_install": "true", + "dependencies_id": [ + fields.Command.create({"name": base.name}), + ], + } + ) + auto_install_country_specific = IrModuleModule.create( + { + "name": "l10n_openupgrade_framework_auto_install_country_specific", + "state": "uninstalled", + "auto_install": "true", + "dependencies_id": [ + fields.Command.create({"name": base.name}), + ], + } + ) + with mute_logger("odoo.modules.module"): + self.env["ir.module.module"].update_list() + self.assertEqual(auto_install.state, "to install") + self.assertEqual(auto_install_localization.state, "uninstalled") + self.assertEqual(auto_install_country_specific.state, "uninstalled")