diff --git a/odoo_project_migration/models/__init__.py b/odoo_project_migration/models/__init__.py index c3e8b904..def3f96b 100644 --- a/odoo_project_migration/models/__init__.py +++ b/odoo_project_migration/models/__init__.py @@ -1,3 +1,4 @@ from . import odoo_module_branch_migration +from . import odoo_project_module from . import odoo_project_module_migration from . import odoo_project diff --git a/odoo_project_migration/models/odoo_project_module.py b/odoo_project_migration/models/odoo_project_module.py new file mode 100644 index 00000000..22afcd16 --- /dev/null +++ b/odoo_project_migration/models/odoo_project_module.py @@ -0,0 +1,12 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models + + +class OdooProjectModule(models.Model): + _inherit = "odoo.project.module" + + def open_next_module_branches(self): + self.ensure_one() + return self.module_branch_id.open_next_module_branches() diff --git a/odoo_project_migration/models/odoo_project_module_migration.py b/odoo_project_migration/models/odoo_project_module_migration.py index 5cd79a72..47885d25 100644 --- a/odoo_project_migration/models/odoo_project_module_migration.py +++ b/odoo_project_migration/models/odoo_project_module_migration.py @@ -92,9 +92,10 @@ class OdooProjectModuleMigration(models.Model): selection=[ ("fully_ported", "Fully Ported"), ("migrate", "To migrate"), - ("port_commits", "Commits to port"), - ("review_migration", "Migration to review"), - ("moved_to_standard", "Moved to standard"), + ("port_commits", "Ported (missing commits?)"), + ("review_migration", "To review"), + ("replaced", "Replaced"), + ("moved_to_standard", "Moved to standard?"), ("moved_to_oca", "Moved to OCA"), ("moved_to_generic", "Moved to generic repo"), # New states to qualify modules without migration data @@ -121,13 +122,24 @@ def _compute_project_module_id(self): ) rec.project_module_id = project_module - @api.depends("source_module_branch_id", "migration_path_id") + @api.depends( + "source_module_branch_id", + "migration_path_id", + "module_migration_id.replaced_by_module_id", + "module_migration_id.renamed_to_module_id", + ) def _compute_target_module_branch_id(self): module_branch_model = self.env["odoo.module.branch"] for rec in self: + # Look for the right module technical name + module = ( + rec.module_migration_id.replaced_by_module_id + or rec.module_migration_id.renamed_to_module_id + or rec.source_module_branch_id.module_id + ) rec.target_module_branch_id = module_branch_model._find( rec.migration_path_id.target_branch_id, - rec.source_module_branch_id.module_id, + module, rec.odoo_project_id.repository_id, domain=[("installable", "=", True)], ) @@ -159,27 +171,15 @@ def _compute_migration_script_ids(self): ], order="sequence", ) - new_release_versions = version_model.search( - [ - ( - "module_id", - "=", - rec.module_id.id, - ), - ( - "branch_sequence", - ">", - rec.source_module_branch_id.branch_id.sequence, - ), - ( - "branch_sequence", - "<=", - rec.target_module_branch_id.branch_id.sequence, - ), - ("has_migration_script", "=", True), - ], - order="branch_sequence, sequence", + # Collect versions with migration scripts accross next modules + # taking into account module renaming/replacement + target_branch = rec.migration_path_id.target_branch_id + next_modules = rec.source_module_branch_id._get_next_module_branches( + target_branch ) + new_release_versions = next_modules.version_ids.filtered( + "has_migration_script" + ).sorted(key=lambda v: (v.branch_sequence, v.sequence)) rec.migration_script_ids = current_release_versions | new_release_versions @api.depends("module_migration_id.state") diff --git a/odoo_project_migration/views/odoo_module_branch_migration.xml b/odoo_project_migration/views/odoo_module_branch_migration.xml index ca4b1ab1..0eecb3b5 100644 --- a/odoo_project_migration/views/odoo_module_branch_migration.xml +++ b/odoo_project_migration/views/odoo_module_branch_migration.xml @@ -11,9 +11,9 @@ ref="odoo_repository_migration.odoo_module_branch_migration_view_form" /> - + - + diff --git a/odoo_project_migration/wizards/export_migration_report.py b/odoo_project_migration/wizards/export_migration_report.py index bf241da7..a66223a7 100644 --- a/odoo_project_migration/wizards/export_migration_report.py +++ b/odoo_project_migration/wizards/export_migration_report.py @@ -134,16 +134,26 @@ def _prepare_csv_module_line(self, module): def _get_csv_module_info(self, module): migration = module.module_migration_id - info = "" + info = [] + # Module renamed or replaced by another one + if migration.renamed_to_module_id: + info.append(f"Renamed to {migration.target_module_branch_id.module_name}") + elif migration.replaced_by_module_id: + info.append( + f"Replaced by {migration.target_module_branch_id.module_name} " + f"(in {migration.target_module_branch_id.repository_id.display_name})" + ) # Migration to review or commits/PRs to port if migration.state == "review_migration": - info = migration.pr_url or "" + if migration.pr_url: + info.append(f"PR to review: {migration.pr_url}") elif migration.process == "port_commits": nb_prs = len(migration.results) - info = f"{nb_prs} PR(s) to check/port" - info = "\n".join( - [info] + [f"- {pr['url']}" for pr in migration.results.values()] + msg = f"{nb_prs} PR(s) to check/port" + msg = "\n".join( + [msg] + [f"- {pr['url']}" for pr in migration.results.values()] ) + info.append(msg) # Migration scripts if module.migration_script_ids: nb_scripts = len(module.migration_script_ids) @@ -152,8 +162,8 @@ def _get_csv_module_info(self, module): [info_mig] + [f"- {sc.migration_script_url}" for sc in module.migration_script_ids] ) - info += info_mig - return info + info.append(info_mig) + return "\n\n".join(info) def _get_csv_module_warning(self, module): warning = "" diff --git a/odoo_repository/lib/scanner.py b/odoo_repository/lib/scanner.py index cbd033f7..b75d4737 100644 --- a/odoo_repository/lib/scanner.py +++ b/odoo_repository/lib/scanner.py @@ -507,6 +507,10 @@ def _scan_migration_path( module_names = self._get_module_paths(repo, addons_path, source_branch) res = [] for module in module_names: + if isinstance(module, tuple): + module, target_module = module + else: + target_module = module if self._is_module_blacklisted(module): _logger.info( "%s: '%s' is blacklisted (no migration scan)", @@ -532,7 +536,7 @@ def _scan_migration_path( repo.commit(repo_source_commit).tree, module ) module_target_tree = self._get_subtree( - repo.commit(repo_target_commit).tree, module + repo.commit(repo_target_commit).tree, target_module ) module_source_commit = self._get_last_commit_of_git_tree( repo_source_commit, module_source_tree @@ -556,6 +560,7 @@ def _scan_migration_path( repo, addons_path, module, + target_module, module_branch_id, source_branch, target_remote, @@ -575,6 +580,7 @@ def _scan_module( repo: git.Repo, addons_path: str, module: str, + target_module: str, module_branch_id: int, source_branch: str, target_remote: str, @@ -596,6 +602,7 @@ def _scan_module( "target_commit": target_last_scanned_commit, } module_path = str(pathlib.Path(addons_path).joinpath(module)) + target_module_path = str(pathlib.Path(addons_path).joinpath(target_module)) # If files updated in the module since the last scan are not relevant # (e.g. all new commits are updating PO files), we skip the scan. source_scan_relevant = self._is_scan_module_relevant( @@ -606,7 +613,7 @@ def _scan_module( ) target_scan_relevant = self._is_scan_module_relevant( repo, - module_path, + target_module_path, target_last_mig_scanned_commit, target_commit, ) @@ -623,12 +630,16 @@ def _scan_module( _logger.info( "%s: relevant changes detected in '%s' (%s -> %s)", self.full_name, - module, + module if source_scan_relevant else target_module, source_branch, target_branch, ) oca_port_data = self._run_oca_port( - module_path, source_branch, target_remote, target_branch + module_path, + target_module_path, + source_branch, + target_remote, + target_branch, ) data["report"] = oca_port_data self._push_scanned_data(module_branch_id, data) @@ -681,7 +692,14 @@ def _check_relevant_commits(self, repo, module_path, commits): return True return False - def _run_oca_port(self, module_path, source_branch, target_remote, target_branch): + def _run_oca_port( + self, + module_path, + target_module_path, + source_branch, + target_remote, + target_branch, + ): _logger.info( "%s: collect migration data for '%s' (%s -> %s)", self.full_name, @@ -694,6 +712,7 @@ def _run_oca_port(self, module_path, source_branch, target_remote, target_branch "source": f"origin/{source_branch}", "target": f"{target_remote}/{target_branch}", "addon_path": module_path, + "target_addon_path": target_module_path, "upstream_org": self.org, "repo_path": self.path, "repo_name": self.name, diff --git a/odoo_repository/security/ir.model.access.csv b/odoo_repository/security/ir.model.access.csv index 4fda44e8..db75b492 100644 --- a/odoo_repository/security/ir.model.access.csv +++ b/odoo_repository/security/ir.model.access.csv @@ -22,5 +22,6 @@ access_odoo_repository_manager,odoo_repository_manager,model_odoo_repository,gro access_odoo_repository_branch_user,odoo_repository_branch_user,model_odoo_repository_branch,group_odoo_repository_user,1,0,0,0 access_odoo_repository_branch_manager,odoo_repository_branch_manager,model_odoo_repository_branch,group_odoo_repository_manager,1,1,1,1 access_odoo_module_branch_user,odoo_module_branch_user,model_odoo_module_branch,group_odoo_repository_user,1,0,0,0 +access_odoo_module_branch_manager,odoo_module_branch_manager,model_odoo_module_branch,group_odoo_repository_manager,1,1,0,0 access_odoo_module_branch_version_user,odoo_module_branch_version_user,model_odoo_module_branch_version,group_odoo_repository_user,1,0,0,0 access_odoo_module_branch_version_manager,odoo_module_branch_version_manager,model_odoo_module_branch_version,group_odoo_repository_manager,1,1,1,1 diff --git a/odoo_repository/views/odoo_module_branch.xml b/odoo_repository/views/odoo_module_branch.xml index 55952d14..13355c5d 100644 --- a/odoo_repository/views/odoo_module_branch.xml +++ b/odoo_repository/views/odoo_module_branch.xml @@ -21,23 +21,27 @@ - - - - - - - - - - - + + + + + + + + + + + - - - + + + - - - - - + + + + + - - - - + + + + - + @@ -75,7 +79,7 @@ - + @@ -88,7 +92,7 @@ - + @@ -101,7 +105,7 @@ - + diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 36dfce56..a33a66c3 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -1,24 +1,188 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import api, fields, models +from odoo import _, api, fields, models class OdooModuleBranch(models.Model): _inherit = "odoo.module.branch" + next_odoo_version_id = fields.Many2one( + comodel_name="odoo.branch", + compute="_compute_next_odoo_version_id", + ) + next_odoo_version_state = fields.Selection( + selection=[ + ("same", "is named the same"), + ("renamed", "has been renamed to"), + ("replaced", "has been replaced by"), + ], + inverse="_inverse_next_odoo_version_fields", + default="same", + required=True, + index=True, + ) + next_odoo_version_module_id = fields.Many2one( + comodel_name="odoo.module", + ondelete="restrict", + inverse="_inverse_next_odoo_version_fields", + string="Next Odoo Version Module", + index=True, + ) + next_odoo_version_module_branch_id = fields.Many2one( + comodel_name="odoo.module.branch", + compute="_compute_next_odoo_version_module_branch_id", + string="Next Odoo Version Module Branch", + ) migration_ids = fields.One2many( comodel_name="odoo.module.branch.migration", inverse_name="module_branch_id", string="Migrations", ) - migration_scan = fields.Boolean( compute="_compute_migration_scan", store=True, help="Technical field telling if this module is elligible for a migration scan.", ) + @api.depends("branch_id") + def _compute_next_odoo_version_id(self): + for rec in self: + next_odoo_version = False + if rec.branch_id: + next_odoo_version = self.env["odoo.branch"].search( + [ + ("odoo_version", "=", True), + ("sequence", ">", rec.branch_id.sequence), + ], + limit=1, + ) + rec.next_odoo_version_id = next_odoo_version + + @api.depends("next_odoo_version_id", "next_odoo_version_module_id") + def _compute_next_odoo_version_module_branch_id(self): + for rec in self: + rec.next_odoo_version_module_branch_id = False + # Stop there if no next version + if not rec.next_odoo_version_id: + continue + # Look for the next available version for this module name + rec.next_odoo_version_module_branch_id = self.search( + [ + ("branch_id", "=", rec.next_odoo_version_id.id), + ("module_id", "=", rec.module_id.id), + ], + limit=1, + ) + # Stop there if no renaming/relacement + if not rec.next_odoo_version_module_id: + continue + rec.next_odoo_version_module_branch_id = self.search( + [ + ("branch_id", "=", rec.next_odoo_version_id.id), + ("module_id", "=", rec.next_odoo_version_module_id.id), + ], + limit=1, + ) + + def _inverse_next_odoo_version_fields(self): + # Reset selected module if the module name doesn't change with next version + if self.next_odoo_version_state == "same": + self.next_odoo_version_module_id = False + # If module is renamed or replaced in next Odoo version, we reset the + # last target scan commits on all impacted migration paths. + # E.g. + # if a module on 17.0 is set as renamed starting from 18.0, + # all migration paths of this module targetting versions >= 18.0 + # should re-trigger a migration scan. + else: + migrations = ( + self.env["odoo.module.branch.migration"] + .search( + [ + ("module_id", "=", self.module_id.id), + ( + "target_branch_id.sequence", + ">=", + self.next_odoo_version_id.sequence, + ), + ] + ) + .sudo() + ) + migrations.last_target_scanned_commit = False + migrations._compute_renamed_to_module_id() + migrations._compute_replaced_by_module_id() + migrations._compute_state() + + def _replaced_by_module_in_target_version(self, target_branch): + """Return the module replacing current one in last module versions. + + Look for current + next modules as the migration scan could do a jump + 14.0 -> 18.0, while a module has been replaced starting from 18.0 (and + therefore flagged as replaced in 17.0 module data). + + We give the priority to last modules while checking them. + """ + self.ensure_one() + modules = self._get_next_versions(target_branch) + for module in modules.sorted( + key=lambda mod: mod.branch_id.sequence, reverse=True + ): + if module.next_odoo_version_state == "replaced": + return module.next_odoo_version_module_id + return self.env["odoo.module"] + + def _renamed_to_module_in_target_version(self, target_branch): + """Return the new module technical name in last module versions. + + Look for current + next modules as the migration scan could do a jump + 14.0 -> 18.0, while a module has been renamed starting from 18.0 (and + therefore flagged as renamed in 17.0 module data). + + We give the priority to last modules while checking them. + """ + self.ensure_one() + modules = self._get_next_versions(target_branch) + for module in modules.sorted( + key=lambda mod: mod.branch_id.sequence, reverse=True + ): + if module.next_odoo_version_state == "renamed": + return module.next_odoo_version_module_id + return self.env["odoo.module"] + + def _get_next_versions(self, target_branch): + self.ensure_one() + return self.env["odoo.module.branch"].search( + [ + ("module_id", "=", self.module_id.id), + ("branch_id.sequence", ">=", self.branch_id.sequence), + ("branch_id.sequence", "<", target_branch.sequence), + ] + ) + + def _get_next_module_branches(self, target_branch=None): + """Return all modules in the right version order starting from current one. + + This is taking into account module renamed or replaced in intermediate versions. + """ + if not self: + return self.browse() + self.ensure_one() + if target_branch: + assert self.branch_id.sequence < target_branch.sequence + next_module_branch = self.next_odoo_version_module_branch_id + next_module_branch_ids = [] + while next_module_branch: + if ( + target_branch + and next_module_branch.branch_id.sequence > target_branch.sequence + ): + break + next_module_branch_ids.append(next_module_branch.id) + next_module_branch = next_module_branch.next_odoo_version_module_branch_id + return self.browse(next_module_branch_ids) + @api.depends( "removed", "pr_url", @@ -92,3 +256,11 @@ def _update_migration_target_module_id(self): ) # Recompute 'target_module_id' field migrations._compute_target_module_branch_id() + + def open_next_module_branches(self): + self.ensure_one() + xml_id = "odoo_repository.odoo_module_branch_action" + action = self.env["ir.actions.actions"]._for_xml_id(xml_id) + action["name"] = _("Next versions") + action["domain"] = [("id", "in", self._get_next_module_branches().ids)] + return action diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index aea3d234..720f4706 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -85,13 +85,28 @@ class OdooModuleBranchMigration(models.Model): "same scope so it deserves a check during a migration." ), ) + renamed_to_module_id = fields.Many2one( + comodel_name="odoo.module", + compute="_compute_renamed_to_module_id", + string="Renamed to", + store=True, + index=True, + ) + replaced_by_module_id = fields.Many2one( + comodel_name="odoo.module", + compute="_compute_replaced_by_module_id", + string="Replaced by", + store=True, + index=True, + ) state = fields.Selection( selection=[ ("fully_ported", "Fully Ported"), ("migrate", "To migrate"), - ("port_commits", "Commits to port"), - ("review_migration", "Migration to review"), - ("moved_to_standard", "Moved to standard"), + ("port_commits", "Ported (missing commits?)"), + ("review_migration", "To review"), + ("replaced", "Replaced"), + ("moved_to_standard", "Moved to standard?"), ("moved_to_oca", "Moved to OCA"), ("moved_to_generic", "Moved to generic repo"), ], @@ -136,13 +151,24 @@ def _compute_display_name(self): f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" ) - @api.depends("module_branch_id", "migration_path_id") + @api.depends( + "module_branch_id", + "migration_path_id", + "replaced_by_module_id", + "renamed_to_module_id", + ) def _compute_target_module_branch_id(self): module_branch_model = self.env["odoo.module.branch"] for rec in self: + # Look for the right module technical name + module = ( + rec.replaced_by_module_id + or rec.renamed_to_module_id + or rec.module_branch_id.module_id + ) rec.target_module_branch_id = module_branch_model._find( rec.migration_path_id.target_branch_id, - rec.module_branch_id.module_id, + module, rec.module_branch_id.repository_id, domain=[("installable", "=", True)], ) @@ -180,10 +206,45 @@ def _compute_moved_to_generic(self): ) @api.depends( - "process", "pr_url", "moved_to_standard", "moved_to_oca", "moved_to_generic" + "module_branch_id.next_odoo_version_state", + "module_branch_id.next_odoo_version_module_id", + "target_branch_id", + ) + def _compute_renamed_to_module_id(self): + for rec in self: + rec.renamed_to_module_id = ( + rec.module_branch_id._renamed_to_module_in_target_version( + rec.target_branch_id + ) + ) + + @api.depends( + "module_branch_id.next_odoo_version_state", + "module_branch_id.next_odoo_version_module_id", + "target_branch_id", + ) + def _compute_replaced_by_module_id(self): + for rec in self: + rec.replaced_by_module_id = ( + rec.module_branch_id._replaced_by_module_in_target_version( + rec.target_branch_id + ) + ) + + @api.depends( + "replaced_by_module_id", + "process", + "pr_url", + "moved_to_standard", + "moved_to_oca", + "moved_to_generic", ) def _compute_state(self): for rec in self: + if rec.replaced_by_module_id: + # Module replaced by another one + rec.state = "replaced" + continue if rec.moved_to_standard: # Module moved to a standard repository (likely from OCA to # odoo/odoo, like 'l10n_eu_oss', 'knowledge', ...). @@ -214,6 +275,8 @@ def _compute_results_text(self): rec.results_text = pprint.pformat(rec.results) @api.depends( + "module_branch_id.last_scanned_commit", + "replaced_by_module_id", "repository_id.collect_migration_data", "last_source_scanned_commit", "last_target_scanned_commit", @@ -233,6 +296,9 @@ def _compute_migration_scan(self): # No migration scan for modules moved to Odoo/OCA/generic repo if rec.state and rec.state.startswith("moved_to"): continue + # No migration scan for modules replaced by another module + if rec.replaced_by_module_id: + continue if ( rec.last_source_scanned_commit != rec.module_branch_id.last_scanned_commit diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index b2879e2a..96d2ed75 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -111,12 +111,33 @@ def _migration_create_jobs_scan_module(self, migration_path, modules_to_scan): return jobs def _scan_migration_module(self, migration_path_id, module_branch_id): - """Scan migration path for `module_branch_id`.""" + """Scan migration path for `module_branch_id`. + + The migration scan can only occur if: + - target module doesn't exist (and can be migrated) + - source and target modules share the same commits histories (able to + collect migration data) + + Also, a target module could have been renamed while sharing the commits history. + + But a module that has been replaced (different name, different commits + history, but providing the same feature) in next versions cannot be scanned. + Such module will get a migration status "Replaced". + """ module = self.env["odoo.module.branch"].browse(module_branch_id).exists() module.ensure_one() migration_path = ( self.env["odoo.migration.path"].browse(migration_path_id).exists() ) + # Skip migration scan if module is replaced in next versions + if module._replaced_by_module_in_target_version( + migration_path.target_branch_id + ): + return ( + f"{module.name} is now replaced by " + f"{module.next_odoo_version_module_id.name}, no need to collect " + "migration data." + ) # Check if module has already been migrated on target version but in a # different repository. If so, tune the scanner parameters to perform # the scan from current repo to new one. @@ -135,11 +156,15 @@ def _scan_migration_module(self, migration_path_id, module_branch_id): params = self._prepare_migration_scanner_parameters( migration_path, target_repository ) + module_names = [module.module_id.name] + if target_module: + if module.module_id != target_module.module_id: + module_names = [(module.module_id.name, target_module.module_id.name)] # Run the migration scan try: scanner = MigrationScannerOdooEnv(**params) return scanner.scan( - addons_path=module.addons_path, module_names=[module.module_id.name] + addons_path=module.addons_path, module_names=module_names ) except Exception as exc: raise RetryableJobError("Scanner error") from exc diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index 6efabeab..09bf8eb8 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -288,3 +288,103 @@ def test_migration_scan_target_module_moved_to_generic(self): self.assertTrue(mig.moved_to_generic) self.assertEqual(mig.state, "moved_to_generic") self.assertFalse(mig.migration_scan) + + def test_renamed_to_module_in_target_version(self): + self.odoo_repository.collect_migration_data = True + # Next version is 16.0 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + # Create the target module + new_module = self.module.copy({"name": "new_module"}) + target_module_branch = self._create_odoo_module_branch( + new_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch.id, + last_scanned_commit="sha", + ) + # Generate migration data records + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": next_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertEqual(self.module_branch.next_odoo_version_id, next_branch) + # Module has been renamed starting from 16.0 + self.module_branch.next_odoo_version_state = "renamed" + self.module_branch.next_odoo_version_module_id = new_module + renamed_to_module = self.module_branch._renamed_to_module_in_target_version( + self.module_branch.next_odoo_version_id + ) + self.assertEqual(renamed_to_module, new_module) + # We target 17.0 to check if intermediate data in 16.0 is found + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + renamed_to_module = self.module_branch._renamed_to_module_in_target_version( + target_branch + ) + self.assertEqual(renamed_to_module, new_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertEqual(mig.renamed_to_module_id, new_module) + self.assertFalse(mig.replaced_by_module_id) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "migrate") + self.assertTrue(mig.migration_scan) + + def test_replaced_by_module_in_target_version(self): + self.odoo_repository.collect_migration_data = True + # Next version is 16.0 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + # Create the target module + new_module = self.module.copy({"name": "new_module"}) + target_module_branch = self._create_odoo_module_branch( + new_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch.id, + last_scanned_commit="sha", + ) + # Generate migration data records + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": next_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertEqual(self.module_branch.next_odoo_version_id, next_branch) + # New module is replacing current one starting from 16.0 + self.module_branch.next_odoo_version_state = "replaced" + self.module_branch.next_odoo_version_module_id = new_module + replaced_by_module = self.module_branch._replaced_by_module_in_target_version( + self.module_branch.next_odoo_version_id + ) + self.assertEqual(replaced_by_module, new_module) + # We target 17.0 to check if intermediate data in 16.0 is found + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + replaced_by_module = self.module_branch._replaced_by_module_in_target_version( + target_branch + ) + self.assertEqual(replaced_by_module, new_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertEqual(mig.replaced_by_module_id, new_module) + self.assertFalse(mig.renamed_to_module_id) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "replaced") + self.assertFalse(mig.migration_scan) diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index a0cd9738..914d7dc9 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -8,8 +8,45 @@ odoo.module.branch +
+ +
+
+ Starting from version , + this module + + + + +
diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index 17eb9973..868b338d 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -13,13 +13,23 @@

- - - - - - - + + + + + + + + + + +