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
+
+
+
+