Skip to content

[20.0][MIG] openupgrade_framework - #6046

Open
hbrunn wants to merge 112 commits into
OCA:20.0from
hbrunn:20.0-mig-openupgrade_framework
Open

hbrunn wants to merge 112 commits into
OCA:20.0from
hbrunn:20.0-mig-openupgrade_framework

Conversation

@hbrunn

@hbrunn hbrunn commented Sep 29, 2026

Copy link
Copy Markdown
Member

legalsylvain and others added 30 commits September 29, 2026 05:34
- Apply new copier template. (from v1.14.2 to v1.18 : replace setup by pyproject.toml, replace black by ruff, ...)
- Disable .github/workflows/test.yml because the openupgrade is not initialized yet
- add new empty modules160-170.rst file
-This commit remove unecessary patch
-Check if test exist then execute it in the test.yml
-Remove the test loader from patch, detail at
OCA#4327 (comment)
@OCA-git-bot OCA-git-bot added mod:openupgrade_scripts Module openupgrade_scripts mod:openupgrade_framework Module openupgrade_framework series:20.0 labels Sep 29, 2026
@hbrunn hbrunn mentioned this pull request Sep 29, 2026
1 of 6 tasks
@hbrunn hbrunn added this to the 20.0 milestone Sep 29, 2026
@hbrunn

hbrunn commented Sep 29, 2026

Copy link
Copy Markdown
Member Author

pre-commit failure fixed in OCA/pylint-odoo#590

@FerDeNetlab FerDeNetlab left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on a 19.0 db with crm, sale_management, purchase, stock, account + l10n_mx, project, hr, mrp and point_of_sale (131 modules after the upgrade). These are the framework issues we hit, one per comment so you can take what makes sense.

Drafted with AI assistance (Claude Opus 5.5), reviewed by me; each suggestion was checked against the current code of this PR and our test migration.

# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openupgradelib import openupgrade

from odoo import models

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the patch below.

Suggested change
from odoo import models
from odoo import api, models

)


IrModel._drop_table = _drop_table

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v20 added IrModel._unlink_related_attachments, an ondelete(at_uninstall=False) that deletes all ir.attachment rows of the model, including attachment=True binary fields, and marks their files for GC. registry.uninstalling_modules is still empty when _process_end runs, so it fires for every obsolete model while _drop_table keeps the table.

Suggested change
IrModel._drop_table = _drop_table
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

Comment on lines +17 to +24
new_auto_install_modules = self.browse([])
for module in self.env["ir.module.module"].search(
[
("auto_install", "=", True),
("state", "=", "uninstalled"),
("name", "not like", ("l10n_%")),
]
):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

button_install skips auto_install modules whose country_ids don't match any company, this loop doesn't, so e.g. account_peppol gets installed on databases without a Peppol country.

Suggested change
new_auto_install_modules = self.browse([])
for module in self.env["ir.module.module"].search(
[
("auto_install", "=", True),
("state", "=", "uninstalled"),
("name", "not like", ("l10n_%")),
]
):
new_auto_install_modules = self.browse([])
company_countries = self.env["res.company"].sudo().search([]).country_id
for module in self.env["ir.module.module"].search(
[
("auto_install", "=", True),
("state", "=", "uninstalled"),
("name", "not like", ("l10n_%")),
]
):
# same rule as button_install
if module.country_ids and not module.country_ids & company_countries:
continue

Comment on lines +19 to +30
try:
return IrUiView._check_xml._original_method(self)
except ValidationError 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,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here:

  • _validate_module_views and create() call _check_xml on several views, so self.xml_id in the handler raises Expected singleton and aborts the upgrade instead of logging.
  • As _raise_view_error doesn't raise anymore, validation goes on and fails later with AttributeError/KeyError/NameError/ValueError (_check_field_paths, _validate_domain_identifiers).
Suggested change
try:
return IrUiView._check_xml._original_method(self)
except ValidationError 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,
)
for view in self:
try:
IrUiView._check_xml._original_method(view)
# with _raise_view_error not raising, validation can fail later on
except (
ValidationError,
ValueError,
AttributeError,
KeyError,
NameError,
) 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",
view.xml_id,
view.model,
e,
)

# Copyright Odoo Community Association (OCA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from uuid import uuid4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed with the change below.

Suggested change
from uuid import uuid4

Comment on lines +22 to +31
savepoint = str(uuid4)
try:
self.env.cr.execute( # pylint: disable=sql-injection
f'SAVEPOINT "{savepoint}"'
)
return BaseModel.unlink._original_method(self)
except Exception as e:
self.env.cr.execute( # pylint: disable=sql-injection
f'ROLLBACK TO SAVEPOINT "{savepoint}"'
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str(uuid4) is the repr of the function, so every call uses the same savepoint name, and the savepoint is never released after a successful unlink. cr.savepoint() also resets the ORM caches on rollback.

Suggested change
savepoint = str(uuid4)
try:
self.env.cr.execute( # pylint: disable=sql-injection
f'SAVEPOINT "{savepoint}"'
)
return BaseModel.unlink._original_method(self)
except Exception as e:
self.env.cr.execute( # pylint: disable=sql-injection
f'ROLLBACK TO SAVEPOINT "{savepoint}"'
)
try:
with self.env.cr.savepoint():
return BaseModel.unlink._original_method(self)
except Exception as e:

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:openupgrade_framework Module openupgrade_framework mod:openupgrade_scripts Module openupgrade_scripts series:20.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.