diff --git a/project_merge/README.rst b/project_merge/README.rst index fcc89425ae..3aecd744a8 100644 --- a/project_merge/README.rst +++ b/project_merge/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ================== Project Task Merge ================== @@ -17,7 +13,7 @@ Project Task Merge .. |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 +.. |badge2| image:: https://img.shields.io/badge/licence-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%2Fproject-lightgray.png?logo=github @@ -36,6 +32,9 @@ This module adds a wizard to merge project tasks. A wizard that can be called from tree view of project task. +Messages, activities and attachments from the merged tasks are also +moved to the destination task. + **Table of contents** .. contents:: @@ -48,6 +47,12 @@ To use this module, you need to: 1. Merge Project Task +Known issues / Roadmap +====================== + +Task dependencies (Blocked by / Blocking, ``depend_on_ids`` / +``dependent_ids``) are not transferred to the destination task on merge. + Bug Tracker =========== @@ -70,6 +75,7 @@ Contributors ------------ - `Onestein `__ +- Cyril VINH-TUNG cyril@invitu.com Maintainers ----------- diff --git a/project_merge/readme/CONTRIBUTORS.md b/project_merge/readme/CONTRIBUTORS.md index 3a4386c7d7..3b270dc0f4 100644 --- a/project_merge/readme/CONTRIBUTORS.md +++ b/project_merge/readme/CONTRIBUTORS.md @@ -1 +1,2 @@ - [Onestein](http://www.onestein.eu) +- Cyril VINH-TUNG diff --git a/project_merge/readme/DESCRIPTION.md b/project_merge/readme/DESCRIPTION.md index a21dc24091..3bdba52155 100644 --- a/project_merge/readme/DESCRIPTION.md +++ b/project_merge/readme/DESCRIPTION.md @@ -1,3 +1,6 @@ This module adds a wizard to merge project tasks. A wizard that can be called from tree view of project task. + +Messages, activities and attachments from the merged tasks are also moved +to the destination task. diff --git a/project_merge/readme/ROADMAP.md b/project_merge/readme/ROADMAP.md new file mode 100644 index 0000000000..e87aaba57c --- /dev/null +++ b/project_merge/readme/ROADMAP.md @@ -0,0 +1,2 @@ +Task dependencies (Blocked by / Blocking, `depend_on_ids` / `dependent_ids`) +are not transferred to the destination task on merge. diff --git a/project_merge/static/description/index.html b/project_merge/static/description/index.html index d36220bda9..94478b2bef 100644 --- a/project_merge/static/description/index.html +++ b/project_merge/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Project Task Merge -
+
+

Project Task Merge

- - -Odoo Community Association - -
-

Project Task Merge

-

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

+

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

This module adds a wizard to merge project tasks.

A wizard that can be called from tree view of project task.

+

Messages, activities and attachments from the merged tasks are also +moved to the destination task.

Table of contents

-

Usage

+

Usage

To use this module, you need to:

  1. Merge Project Task
+
+

Known issues / Roadmap

+

Task dependencies (Blocked by / Blocking, depend_on_ids / +dependent_ids) are not transferred to the destination task on merge.

+
-

Bug Tracker

+

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 @@ -406,21 +409,22 @@

Bug Tracker

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

-
diff --git a/project_merge/tests/test_project_task_merge.py b/project_merge/tests/test_project_task_merge.py index 2310623415..b319a9c5d7 100644 --- a/project_merge/tests/test_project_task_merge.py +++ b/project_merge/tests/test_project_task_merge.py @@ -334,3 +334,55 @@ def test_create_new_task_on_merge(self): len(self.task_merge_2.dst_task_id._get_all_subtasks()), "Should have 18 subtasks", ) + + def test_merge_moves_messages_and_activities(self): + """messages, activities and attachments of the merged task are + moved to the destination task, with a mention of their origin + task""" + task_A = self.env["project.task"].create( + {"name": "Task A", "project_id": self.project_goats.id} + ) + task_B = self.env["project.task"].create( + {"name": "Task B", "project_id": self.project_goats.id} + ) + attachment_direct = self.env["ir.attachment"].create( + { + "name": "doc.txt", + "res_model": "project.task", + "res_id": task_B.id, + "raw": b"some content", + } + ) + attachment_message = self.env["ir.attachment"].create( + { + "name": "message_doc.txt", + "res_model": "project.task", + "res_id": task_B.id, + "raw": b"some content", + } + ) + message_B = task_B.message_post( + body="Some note on task B", + subject="B note", + message_type="comment", + subtype_xmlid="mail.mt_note", + attachment_ids=[attachment_message.id], + ) + activity_B = task_B.activity_schedule( + "mail.mail_activity_data_todo", summary="Follow up on task B" + ) + + task_merge = ( + self.env["project.task.merge"] + .with_context(active_ids=[task_A.id, task_B.id]) + .create({}) + ) + task_merge.merge_tasks() + + self.assertEqual(message_B.res_id, task_merge.dst_task_id.id) + self.assertEqual(message_B.subject, "From Task B: B note") + self.assertEqual(activity_B.res_id, task_merge.dst_task_id.id) + self.assertEqual(attachment_direct.res_id, task_merge.dst_task_id.id) + self.assertEqual(attachment_direct.name, "doc.txt (from Task B)") + self.assertEqual(attachment_message.res_id, task_merge.dst_task_id.id) + self.assertEqual(attachment_message.name, "message_doc.txt (from Task B)") diff --git a/project_merge/wizard/project_task_merge.py b/project_merge/wizard/project_task_merge.py index c67e3828c1..1f27089c58 100644 --- a/project_merge/wizard/project_task_merge.py +++ b/project_merge/wizard/project_task_merge.py @@ -31,11 +31,9 @@ def default_get(self, fields): def merge_tasks(self): tag_ids = self.task_ids.mapped("tag_ids").ids - attachment_ids = self.task_ids.mapped("attachment_ids").ids values = { "description": self._get_merge_description(), "tag_ids": [(4, tag_id) for tag_id in tag_ids], - "attachment_ids": [(4, attachment_id) for attachment_id in attachment_ids], "user_ids": self.user_ids.ids, } if self.create_new_task: @@ -56,6 +54,8 @@ def merge_tasks(self): self.dst_task_id.write(values) merged_tasks = self.task_ids - self.dst_task_id self._subscribe_merged_followers(merged_tasks) + self._merge_dependences_history(merged_tasks) + self._merge_dependences_attachments(merged_tasks) for task in merged_tasks: self._add_message("to", self.dst_task_id.name, task) if task.child_ids: @@ -85,6 +85,54 @@ def _subscribe_merged_followers(self, merged_tasks): partner_ids=(merged_tasks).mapped("message_partner_ids").ids ) + def _merge_dependences_history(self, merged_tasks): + """Move messages and activities from merged tasks to the destination + task, keeping track of their origin. + :param merged_tasks : recordset of tasks about to be archived + """ + for task_su in merged_tasks.sudo(): + for message_su in task_su.message_ids: + if message_su.subject: + subject = _( + "From %(source_name)s: %(source_subject)s", + source_name=task_su.name, + source_subject=message_su.subject, + ) + else: + subject = _("From %(source_name)s", source_name=task_su.name) + message_su.write({"res_id": self.dst_task_id.id, "subject": subject}) + merged_tasks.sudo().activity_ids.write({"res_id": self.dst_task_id.id}) + + def _merge_dependences_attachments(self, merged_tasks): + """Move attachments of the merged tasks to the destination task, and + rename them to keep track of their origin. + ir.attachment is reparented directly: project.task.attachment_ids + is a non-stored compute field, it can't be written through values. + :param merged_tasks : recordset of tasks about to be archived + """ + all_attachments = ( + self.env["ir.attachment"] + .sudo() + .search( + [ + ("res_model", "=", "project.task"), + ("res_id", "in", merged_tasks.ids), + ] + ) + ) + task_name_per_id = {task.id: task.name for task in merged_tasks} + for attachment in all_attachments: + attachment.write( + { + "res_id": self.dst_task_id.id, + "name": _( + "%(attach_name)s (from %(task_name)s)", + attach_name=attachment.name, + task_name=task_name_per_id[attachment.res_id][:20], + ), + } + ) + def _add_message(self, way, task_names, task): """Send a message post with to advise the project task about the merge. :param way : choice between "from" or "to"