diff --git a/customer_mail_reply_stage/models/__init__.py b/customer_mail_reply_stage/models/__init__.py deleted file mode 100644 index e08f62b74d..0000000000 --- a/customer_mail_reply_stage/models/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import ir_model -from . import mail_message diff --git a/customer_mail_reply_stage/models/ir_model.py b/customer_mail_reply_stage/models/ir_model.py deleted file mode 100644 index b8dd6f8e6c..0000000000 --- a/customer_mail_reply_stage/models/ir_model.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2025 Quartile Limited -# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). - -from odoo import fields, models - - -class IrModel(models.Model): - _inherit = "ir.model" - - use_reply_stage = fields.Boolean( - help="If enabled, the reply stage feature will be activated for this model." - ) diff --git a/customer_mail_reply_stage/models/mail_message.py b/customer_mail_reply_stage/models/mail_message.py deleted file mode 100644 index 2ead51fddf..0000000000 --- a/customer_mail_reply_stage/models/mail_message.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2025 Quartile Limited -# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). - -from odoo import api, models - - -class MailMessage(models.Model): - _inherit = "mail.message" - - @api.model_create_multi - def create(self, values_list): - messages = super().create(values_list) - for message in messages: - res_model = ( - self.env["ir.model"] - .sudo() - .search([("model", "=", message.model)], limit=1) - ) - if not res_model.use_reply_stage: - continue - if not (message.subtype_id and not message.subtype_id.internal): - continue - resource = self.env[message.model].browse(message.res_id) - if ( - getattr(resource, resource._remain_state_field()) - == resource._remain_state_value() - ): - continue - user = message.author_id.user_ids[:1] - if user and user.has_group("base.group_user"): - continue - parent = resource[resource._parent_field()] - reply_stage = getattr(parent, resource._reply_stage_field()) - if reply_stage: - resource.sudo().write({"stage_id": reply_stage.id}) - continue - return messages - - def _resource_model(self): - """Override this method in child models to specify the model name.""" - raise NotImplementedError("Subclasses must implement `_resource_model`.") - - def _remain_state_field(self): - """Override this method in child models to specify the remain state.""" - raise NotImplementedError("Subclasses must implement `_remain_state_field`.") - - def _remain_state_value(self): - """Override this method in child models to specify the remain state value.""" - raise NotImplementedError("Subclasses must implement `_remain_state_value`.") - - def _reply_stage_field(self): - """Override this method in child models to specify the reply stage field name.""" - raise NotImplementedError("Subclasses must implement `_reply_stage_field`.") - - def _parent_field(self): - """Override this method in child models to specify the parent field name.""" - raise NotImplementedError("Subclasses must implement `_parent_field`.") diff --git a/customer_mail_reply_stage/views/ir_model_views.xml b/customer_mail_reply_stage/views/ir_model_views.xml deleted file mode 100644 index 4bfcebae12..0000000000 --- a/customer_mail_reply_stage/views/ir_model_views.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - ir.model.form.inherit - ir.model - - - - - - - - diff --git a/customer_mail_reply_stage/README.rst b/mail_reply_stage/README.rst similarity index 100% rename from customer_mail_reply_stage/README.rst rename to mail_reply_stage/README.rst diff --git a/customer_mail_reply_stage/__init__.py b/mail_reply_stage/__init__.py similarity index 100% rename from customer_mail_reply_stage/__init__.py rename to mail_reply_stage/__init__.py diff --git a/customer_mail_reply_stage/__manifest__.py b/mail_reply_stage/__manifest__.py similarity index 70% rename from customer_mail_reply_stage/__manifest__.py rename to mail_reply_stage/__manifest__.py index 24f2e3cdbe..b5e95cedf6 100644 --- a/customer_mail_reply_stage/__manifest__.py +++ b/mail_reply_stage/__manifest__.py @@ -1,13 +1,16 @@ # Copyright 2025 Quartile Limited # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { - "name": "Customer Mail Reply Stage", + "name": "Mail Reply Stage", "category": "Mail", "version": "15.0.1.0.0", "author": "Quartile, Odoo Community Association (OCA)", "website": "https://github.com/OCA/social", "license": "AGPL-3", "depends": ["mail"], - "data": ["views/ir_model_views.xml"], + "data": [ + "security/ir.model.access.csv", + "views/mail_reply_config_views.xml", + ], "installable": True, } diff --git a/mail_reply_stage/models/__init__.py b/mail_reply_stage/models/__init__.py new file mode 100644 index 0000000000..ad64cea8ed --- /dev/null +++ b/mail_reply_stage/models/__init__.py @@ -0,0 +1,2 @@ +from . import mail_message +from . import mail_reply_config diff --git a/mail_reply_stage/models/mail_message.py b/mail_reply_stage/models/mail_message.py new file mode 100644 index 0000000000..88874f13b3 --- /dev/null +++ b/mail_reply_stage/models/mail_message.py @@ -0,0 +1,50 @@ +# Copyright 2025 Quartile Limited +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from odoo import api, models + + +class MailMessage(models.Model): + _inherit = "mail.message" + + @api.model_create_multi + def create(self, values_list): + messages = super().create(values_list) + for message in messages: + # user = message.author_id.user_ids[:1] + # if user and user.has_group("base.group_user"): + # continue + if message.subtype_id and message.subtype_id.internal: + continue + res_model = self.env["ir.model"].sudo().search([("model", "=", message.model)], limit=1) + if not res_model: + continue + resource = self.env[message.model].browse(message.res_id) + config_records = self.env["mail.reply.config"].search([ + ('model_id', '=', res_model.id) + ]) + matched_config = None + for config in config_records: + parent_field_value = getattr(resource, config.parent_field_id.name, None) + if parent_field_value and getattr(parent_field_value, 'name', None) == config.parent_field_value: + matched_config = config + break + if not matched_config: + matched_config = self.env["mail.reply.config"].search([ + ('model_id', '=', res_model.id), ('parent_field_id', '=', False) + ], limit=1) + if not matched_config: + continue + current_stage = getattr(resource, matched_config.reply_stage_field_id.name, None) + if current_stage == matched_config.remain_stage: + continue + reply_stage_rec = self.env[matched_config.reply_stage_field_id.relation].search([ + ('name', '=', matched_config.reply_stage) + ]) + if config.parent_stage_field_id: + allowed_stages = getattr(parent_field_value, config.parent_stage_field_id.name, self.env[matched_config.parent_stage_field_id.relation]) + reply_stage_rec = reply_stage_rec.filtered(lambda stage: stage in allowed_stages) + if reply_stage_rec: + resource.sudo().write({matched_config.reply_stage_field_id.name: reply_stage_rec.id}) + return messages + \ No newline at end of file diff --git a/mail_reply_stage/models/mail_reply_config.py b/mail_reply_stage/models/mail_reply_config.py new file mode 100644 index 0000000000..e7c4603200 --- /dev/null +++ b/mail_reply_stage/models/mail_reply_config.py @@ -0,0 +1,39 @@ +# Copyright 2025 Quartile Limited +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from odoo import api, fields, models + + +class MailReplyStage(models.Model): + _name = "mail.reply.config" + + + model_id = fields.Many2one('ir.model', string='Model', required=True, ondelete="cascade") + parent_field_id = fields.Many2one('ir.model.fields', string='Parent Field', domain="[('model_id', '=', model_id), ('ttype', '=', 'many2one')]", required=True,ondelete="cascade") + parent_model_name = fields.Char( + related="parent_field_id.relation", + store=True, + help="Automatically stores the model name of the related parent entity." + ) + parent_stage_field_id = fields.Many2one( + 'ir.model.fields', + string='Parent Stage Field', + domain="[('model_id.model', '=', parent_model_name), ('ttype', '=', 'many2many')]", + ondelete="cascade", + help="A Many2Many field within the parent model that defines valid stages for this configuration." + ) + parent_field_value = fields.Char( + help="The specific value of the parent field that this configuration applies to. For example, a project name." + ) + reply_stage_field_id = fields.Many2one('ir.model.fields', string='Field', domain="[('model_id', '=', model_id), ('ttype', '=', 'many2one')]", required=True,ondelete="cascade") + reply_stage = fields.Char(required=True, help="This stage of record will be changed when a non-internal user replies to the record.") + remain_stage = fields.Char(string='No Reply Stage', required=True, help="Record in this stage will not update to the mail reply stage when a non-internal user replies to the record.") + + @api.onchange('model_id') + def _onchange_model_id(self): + self.reply_stage_field_id = False + + @api.onchange('reply_stage_field_id') + def _onchange_reply_stage_field_id(self): + self.reply_stage = False + self.remain_stage = False diff --git a/customer_mail_reply_stage/readme/CONFIGURE.rst b/mail_reply_stage/readme/CONFIGURE.rst similarity index 100% rename from customer_mail_reply_stage/readme/CONFIGURE.rst rename to mail_reply_stage/readme/CONFIGURE.rst diff --git a/customer_mail_reply_stage/readme/DESCRIPTION.rst b/mail_reply_stage/readme/DESCRIPTION.rst similarity index 100% rename from customer_mail_reply_stage/readme/DESCRIPTION.rst rename to mail_reply_stage/readme/DESCRIPTION.rst diff --git a/customer_mail_reply_stage/readme/USAGE.rst b/mail_reply_stage/readme/USAGE.rst similarity index 100% rename from customer_mail_reply_stage/readme/USAGE.rst rename to mail_reply_stage/readme/USAGE.rst diff --git a/mail_reply_stage/security/ir.model.access.csv b/mail_reply_stage/security/ir.model.access.csv new file mode 100644 index 0000000000..7eb425b993 --- /dev/null +++ b/mail_reply_stage/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_mail_reply_config_all,mail.reply.config.all,model_mail_reply_config,,1,0,0,0 +access_mail_reply_config_admin,mail.reply.config.admin,model_mail_reply_config,base.group_system,1,1,1,1 diff --git a/customer_mail_reply_stage/static/description/index.html b/mail_reply_stage/static/description/index.html similarity index 100% rename from customer_mail_reply_stage/static/description/index.html rename to mail_reply_stage/static/description/index.html diff --git a/mail_reply_stage/views/mail_reply_config_views.xml b/mail_reply_stage/views/mail_reply_config_views.xml new file mode 100644 index 0000000000..71c54133ca --- /dev/null +++ b/mail_reply_stage/views/mail_reply_config_views.xml @@ -0,0 +1,31 @@ + + + + mail.reply.config.tree + mail.reply.config + + + + + + + + + + + + + + + Mail Reply Configurations + mail.reply.config + tree + + + +