diff --git a/mail_thread_last_update/README.rst b/mail_thread_last_update/README.rst new file mode 100644 index 0000000000..ac58ce8c3e --- /dev/null +++ b/mail_thread_last_update/README.rst @@ -0,0 +1,97 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +======================= +Mail Thread Last Update +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:1d44e96513bb20b72974dbb2b2d77c74dbc11329e00d20b702fcee4af31d540a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/15.0/mail_thread_last_update + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-15-0/social-15-0-mail_thread_last_update + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=15.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a last chatter update datetime (``last_chatter_update``) +to models with chatter support. +The models to update and the message types to use are controlled by system +parameters. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +#. Navigate to *Settings > Technical > Parameters > System Parameters*. +#. Set ``mail_thread_last_update.target_models`` to a comma-separated list of model names. +#. If ``mail_thread_last_update.target_models`` is empty, any chatter-enabled model will update ``last_chatter_update``. +#. Set ``mail_thread_last_update.message_types`` to a comma-separated list of message types. +#. If ``mail_thread_last_update.message_types`` is empty, any message type will update ``last_chatter_update``. +#. Example values: + ``helpdesk.ticket,res.partner`` / ``comment,email,note`` + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Quartile + +Contributors +~~~~~~~~~~~~ + +* `Quartile `__: + + * Toshikimi Shigenobu + * Yoshi Tashiro + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_thread_last_update/__init__.py b/mail_thread_last_update/__init__.py new file mode 100644 index 0000000000..f2d5f49b8a --- /dev/null +++ b/mail_thread_last_update/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/mail_thread_last_update/__manifest__.py b/mail_thread_last_update/__manifest__.py new file mode 100644 index 0000000000..1ec60ea5dc --- /dev/null +++ b/mail_thread_last_update/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Mail Thread Last Update", + "version": "15.0.1.0.0", + "category": "Mail", + "author": "Quartile, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/social", + "depends": ["mail"], + "data": [ + "data/ir_config_parameter.xml", + ], + "installable": True, +} diff --git a/mail_thread_last_update/data/ir_config_parameter.xml b/mail_thread_last_update/data/ir_config_parameter.xml new file mode 100644 index 0000000000..ba1b00bba8 --- /dev/null +++ b/mail_thread_last_update/data/ir_config_parameter.xml @@ -0,0 +1,11 @@ + + + + mail_thread_last_update.target_models + + + + mail_thread_last_update.message_types + + + diff --git a/mail_thread_last_update/models/__init__.py b/mail_thread_last_update/models/__init__.py new file mode 100644 index 0000000000..62b0b1e665 --- /dev/null +++ b/mail_thread_last_update/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import mail_message +from . import mail_thread diff --git a/mail_thread_last_update/models/mail_message.py b/mail_thread_last_update/models/mail_message.py new file mode 100644 index 0000000000..60cf5344ad --- /dev/null +++ b/mail_thread_last_update/models/mail_message.py @@ -0,0 +1,38 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class MailMessage(models.Model): + _inherit = "mail.message" + + @api.model_create_multi + def create(self, vals_list): + messages = super().create(vals_list) + messages._update_last_chatter_update() + return messages + + def _update_last_chatter_update(self): + params = self.env["ir.config_parameter"].sudo() + models_value = (params.get_param("mail_thread_last_update.target_models") or "").strip() + types_value = (params.get_param("mail_thread_last_update.message_types") or "").strip() + + target_models = {item.strip() for item in models_value.split(",") if item.strip()} + message_types = {item.strip() for item in types_value.split(",") if item.strip()} + + for message in self: + is_target = ( + message.model + and message.res_id + and (not target_models or message.model in target_models) + and (not message_types or message.message_type in message_types) + ) + if not is_target: + continue + + record = self.env[message.model].browse(message.res_id) + if record.exists() and hasattr(record, "last_chatter_update"): + record.sudo().write({ + "last_chatter_update": message.date or fields.Datetime.now(), + }) diff --git a/mail_thread_last_update/models/mail_thread.py b/mail_thread_last_update/models/mail_thread.py new file mode 100644 index 0000000000..b1b0229719 --- /dev/null +++ b/mail_thread_last_update/models/mail_thread.py @@ -0,0 +1,13 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + last_chatter_update = fields.Datetime( + string="Last Chatter Update", + help="Datetime when a message was posted on the record.", + ) diff --git a/mail_thread_last_update/readme/CONFIGURE.rst b/mail_thread_last_update/readme/CONFIGURE.rst new file mode 100644 index 0000000000..d9e5933eca --- /dev/null +++ b/mail_thread_last_update/readme/CONFIGURE.rst @@ -0,0 +1,7 @@ +#. Navigate to *Settings > Technical > Parameters > System Parameters*. +#. Set ``mail_thread_last_update.target_models`` to a comma-separated list of model names. +#. If ``mail_thread_last_update.target_models`` is empty, any chatter-enabled model will update ``last_chatter_update``. +#. Set ``mail_thread_last_update.message_types`` to a comma-separated list of message types. +#. If ``mail_thread_last_update.message_types`` is empty, any message type will update ``last_chatter_update``. +#. Example values: + ``helpdesk.ticket,res.partner`` / ``comment,email,note`` diff --git a/mail_thread_last_update/readme/CONTRIBUTORS.rst b/mail_thread_last_update/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..52192eefec --- /dev/null +++ b/mail_thread_last_update/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Quartile `__: + + * Toshikimi Shigenobu + * Yoshi Tashiro diff --git a/mail_thread_last_update/readme/DESCRIPTION.rst b/mail_thread_last_update/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..1fbe410e26 --- /dev/null +++ b/mail_thread_last_update/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module adds a last chatter update datetime (``last_chatter_update``) +to models with chatter support. +The models to update and the message types to use are controlled by system +parameters. diff --git a/mail_thread_last_update/static/description/index.html b/mail_thread_last_update/static/description/index.html new file mode 100644 index 0000000000..bd89eae719 --- /dev/null +++ b/mail_thread_last_update/static/description/index.html @@ -0,0 +1,444 @@ + + + + + +README.rst + + + +
+ + +Odoo Community Association +
+

Mail Thread Last Update

+ +

Beta License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

+

This module adds a last chatter update datetime (last_chatter_update) +to models with chatter support. +The models to update and the message types to use are controlled by system +parameters.

+

Table of contents

+ +
+

Configuration

+
    +
  1. Navigate to Settings > Technical > Parameters > System Parameters.
  2. +
  3. Set mail_thread_last_update.target_models to a comma-separated list of model names.
  4. +
  5. If mail_thread_last_update.target_models is empty, any chatter-enabled model will update last_chatter_update.
  6. +
  7. Set mail_thread_last_update.message_types to a comma-separated list of message types.
  8. +
  9. If mail_thread_last_update.message_types is empty, any message type will update last_chatter_update.
  10. +
  11. Example values: +helpdesk.ticket,res.partner / comment,email,note
  12. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Quartile
  • +
+
+
+

Contributors

+
    +
  • Quartile:
      +
    • Toshikimi Shigenobu
    • +
    • Yoshi Tashiro
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/social project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/mail_thread_last_update/tests/__init__.py b/mail_thread_last_update/tests/__init__.py new file mode 100644 index 0000000000..73cbfbe2e0 --- /dev/null +++ b/mail_thread_last_update/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2025 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_mail_thread_last_update diff --git a/mail_thread_last_update/tests/test_mail_thread_last_update.py b/mail_thread_last_update/tests/test_mail_thread_last_update.py new file mode 100644 index 0000000000..554d3df23c --- /dev/null +++ b/mail_thread_last_update/tests/test_mail_thread_last_update.py @@ -0,0 +1,51 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import common + + +class TestMailThreadLastUpdate(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.params = cls.env["ir.config_parameter"].sudo() + cls.message_model = cls.env["mail.message"] + cls.partner = cls.env["res.partner"].create({"name": "Test Partner"}) + + def _create_message(self, message_type="comment"): + return self.message_model.create( + { + "message_type": message_type, + "model": "res.partner", + "res_id": self.partner.id, + "body": "Test message", + } + ) + + def test_update_on_target_model(self): + self.params.set_param("mail_thread_last_update.target_models", "res.partner") + self.params.set_param("mail_thread_last_update.message_types", "") + self.partner.write({"last_chatter_update": False}) + message = self._create_message(message_type="comment") + self.assertEqual(self.partner.last_chatter_update, message.date) + + def test_skip_on_non_target_model(self): + self.params.set_param("mail_thread_last_update.target_models", "res.company") + self.params.set_param("mail_thread_last_update.message_types", "") + self.partner.write({"last_chatter_update": False}) + self._create_message(message_type="comment") + self.assertFalse(self.partner.last_chatter_update) + + def test_update_on_target_message_type(self): + self.params.set_param("mail_thread_last_update.target_models", "res.partner") + self.params.set_param("mail_thread_last_update.message_types", "comment") + self.partner.write({"last_chatter_update": False}) + message = self._create_message(message_type="comment") + self.assertEqual(self.partner.last_chatter_update, message.date) + + def test_skip_on_non_target_message_type(self): + self.params.set_param("mail_thread_last_update.target_models", "res.partner") + self.params.set_param("mail_thread_last_update.message_types", "comment") + self.partner.write({"last_chatter_update": False}) + self._create_message(message_type="email") + self.assertFalse(self.partner.last_chatter_update) diff --git a/setup/mail_thread_last_update/odoo/addons/mail_thread_last_update b/setup/mail_thread_last_update/odoo/addons/mail_thread_last_update new file mode 120000 index 0000000000..cbee6bba1d --- /dev/null +++ b/setup/mail_thread_last_update/odoo/addons/mail_thread_last_update @@ -0,0 +1 @@ +../../../../mail_thread_last_update \ No newline at end of file diff --git a/setup/mail_thread_last_update/setup.py b/setup/mail_thread_last_update/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/mail_thread_last_update/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)