diff --git a/addons/cetmix_tower_git/models/cx_tower_git_project_rel.py b/addons/cetmix_tower_git/models/cx_tower_git_project_rel.py new file mode 100644 index 0000000..d9fba6b --- /dev/null +++ b/addons/cetmix_tower_git/models/cx_tower_git_project_rel.py @@ -0,0 +1,177 @@ +# Copyright (C) 2024 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class CxTowerGitProjectRel(models.Model): + """ + Relation between git projects and other model records. + """ + + _name = "cx.tower.git.project.rel" + _inherit = [ + "cx.tower.reference.mixin", + "cx.tower.yaml.mixin", + ] + _table = "cx_tower_git_project_rel" + _description = "Cetmix Tower Git Project relation to Files and Servers" + _log_access = False + + name = fields.Char(related="git_project_id.name", readonly=True) + git_project_id = fields.Many2one( + comodel_name="cx.tower.git.project", + index=True, + required=True, + ondelete="cascade", + ) + server_id = fields.Many2one( + comodel_name="cx.tower.server", + index=True, + required=True, + ondelete="cascade", + ) + file_id = fields.Many2one( + comodel_name="cx.tower.file", + domain="[('server_id', '=', server_id)," + "('source', '=', 'tower')," + "('file_type', '=', 'text')]", + required=True, + ondelete="cascade", + ) + project_format = fields.Selection( + selection=lambda self: self.env[ + "cx.tower.git.project" + ]._selection_project_format(), + default=lambda self: self.env["cx.tower.git.project"]._default_project_format(), + required=True, + string="Format", + ) + auto_sync = fields.Boolean(related="file_id.auto_sync", readonly=False) + + _sql_constraints = [ + ( + "project_server_file_format_uniq", + "unique(git_project_id, file_id, project_format)", + "File is already related to the same project and format", + ), + ] + + @api.constrains("server_id", "file_id") + def _check_server_file_relation(self): + """ + Check if server and file are related. + """ + for record in self: + if ( + record.file_id.server_id + and record.server_id != record.file_id.server_id + ): + raise ValidationError( + _( + "File '%(file)s' doesn't belong to server '%(server)s'", + file=record.file_id.name, + server=record.server_id.name, + ) + ) + + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) + + # Export project to file + res._save_to_file() + return res + + def write(self, vals): + res = super().write(vals) + # Export project to file + self._save_to_file() + return res + + def action_open_project(self): + """ + Open project record in current window + """ + self.ensure_one() + return { + "type": "ir.actions.act_window", + "name": self.name, + "res_model": "cx.tower.git.project", + "res_id": self.git_project_id.id, # pylint: disable=no-member + "view_mode": "form", + "view_type": "form", + "target": "current", + } + + def action_open_server(self): + """ + Open server record in current window + """ + self.ensure_one() + return { + "type": "ir.actions.act_window", + "name": self.server_id.name, + "res_model": "cx.tower.server", + "res_id": self.server_id.id, # pylint: disable=no-member + "view_mode": "form", + "view_type": "form", + "target": "current", + } + + # ---------------------------------------------------- + # Save project to linked file based on selected format + # ---------------------------------------------------- + def _save_to_file(self): + """Save project to linked file using format-specific function.""" + + # Get required function based on project format + # Following the pattern: _generate_code_ where format + # is one of the values in _selection_project_format + # Function gets a single record as an argument. + + # Save resolved functions to dict for faster access + code_generator_functions = {} + + for record in self: + # Disconnect file from file template if it is connected + if record.file_id.template_id: + record.file_id.action_unlink_from_template() + + code_generator_function = code_generator_functions.get( + record.project_format + ) + if not code_generator_function: + code_generator_function = getattr( + self.git_project_id, f"_generate_code_{record.project_format}", None + ) + if not code_generator_function: + raise ValidationError( + _( + "Code generator function for '%(project_format)s'" + " format not found.", + project_format=record.project_format, + ) + ) + code_generator_functions[ + record.project_format + ] = code_generator_function + + # Generate code for current record + code = code_generator_function(record) + if record.file_id.code != code: + record.file_id.write({"code": code}) + + # ------------------------------ + # YAML mixin methods + # ------------------------------ + def _get_fields_for_yaml(self): + res = super()._get_fields_for_yaml() + res += [ + "file_id", + "git_project_id", + "project_format", + "auto_sync", + ] + return res