Skip to content

Commit bcc5796

Browse files
committed
[IMP] mail_activity_team: Add notify_members field to optional notify team members on activity assignment
1 parent f025692 commit bcc5796

File tree

4 files changed

+353
-0
lines changed

4 files changed

+353
-0
lines changed

mail_activity_team/models/mail_activity.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from odoo import SUPERUSER_ID, _, api, fields, models
55
from odoo.exceptions import ValidationError
6+
from odoo.tools.misc import get_lang
67

78

89
class MailActivity(models.Model):
@@ -105,3 +106,82 @@ def _onchange_activity_type_id(self):
105106
if self.user_id not in members and members:
106107
self.user_id = members[:1]
107108
return res
109+
110+
def action_notify(self):
111+
"""Override to notify team members when notify_members is enabled."""
112+
# Call the parent action_notify to send notification to the assigned user
113+
result = super().action_notify()
114+
115+
# Check if any activity has a team with notify_members enabled
116+
classified = self._classify_by_model()
117+
for model, activity_data in classified.items():
118+
records_sudo = self.env[model].sudo().browse(activity_data["record_ids"])
119+
activity_data["record_ids"] = (
120+
records_sudo.exists().ids
121+
) # in case record was cascade-deleted in DB, skipping unlink override
122+
123+
for activity in self:
124+
if activity.res_id not in classified[activity.res_model]["record_ids"]:
125+
continue
126+
if activity.team_id:
127+
# Because team activities use team_user_id instead of user_id, we need
128+
# notify the assigned.
129+
# (I think this was a bug that team_user_id is not notified.)
130+
team_members = (
131+
activity.team_id.member_ids
132+
if activity.team_id.notify_members
133+
else [activity.team_user_id]
134+
)
135+
136+
if team_members:
137+
# Get the record and model description
138+
model_description = (
139+
activity.env["ir.model"]._get(activity.res_model).display_name
140+
)
141+
record = activity.env[activity.res_model].browse(activity.res_id)
142+
143+
# Notify each team member
144+
for member in team_members:
145+
if member.lang:
146+
# Send notification in member's language
147+
activity_ctx = activity.with_context(lang=member.lang)
148+
else:
149+
activity_ctx = activity
150+
151+
body = activity_ctx.env["ir.qweb"]._render(
152+
"mail.message_activity_assigned",
153+
{
154+
"activity": activity_ctx,
155+
"model_description": model_description,
156+
"is_html_empty": lambda x: not x or x == "<p><br></p>",
157+
},
158+
minimal_qcontext=True,
159+
)
160+
161+
record.message_notify(
162+
partner_ids=member.partner_id.ids,
163+
body=body,
164+
record_name=activity_ctx.res_name,
165+
model_description=model_description,
166+
email_layout_xmlid="mail.mail_notification_layout",
167+
subject=_(
168+
"%(activity_name)s: %(summary)s (Team Activity)",
169+
activity_name=activity_ctx.res_name,
170+
summary=activity_ctx.summary
171+
or activity_ctx.activity_type_id.name,
172+
),
173+
subtitles=[
174+
_("Activity: %s", activity_ctx.activity_type_id.name),
175+
_("Team: %s", activity_ctx.team_id.name),
176+
_(
177+
"Deadline: %s",
178+
activity_ctx.date_deadline.strftime(
179+
get_lang(activity_ctx.env).date_format
180+
)
181+
if hasattr(activity_ctx.date_deadline, "strftime")
182+
else str(activity_ctx.date_deadline),
183+
),
184+
],
185+
)
186+
187+
return result

mail_activity_team/models/mail_activity_team.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ def _compute_missing_activities(self):
4545
string="Team Members",
4646
)
4747
user_id = fields.Many2one(comodel_name="res.users", string="Team Leader")
48+
notify_members = fields.Boolean(
49+
default=False,
50+
help="When enabled, all team members will be notified "
51+
"when an activity is assigned to this team.",
52+
)
4853
count_missing_activities = fields.Integer(
4954
string="Missing Activities", compute="_compute_missing_activities", default=0
5055
)

mail_activity_team/tests/test_mail_activity_team.py

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def setUpClass(cls):
100100
"name": "Team 2",
101101
"res_model_ids": [(6, 0, [cls.partner_ir_model.id])],
102102
"member_ids": [(6, 0, [cls.employee.id, cls.employee2.id])],
103+
"notify_members": True,
103104
}
104105
)
105106
cls.act2 = (
@@ -447,3 +448,269 @@ def test_migration(self):
447448
mod.migrate(self.env.cr, "18.0.1.0.0")
448449

449450
self.assertFalse(rule.perm_create)
451+
452+
def test_notify_members_disabled(self):
453+
"""Test that when notify_members is False, only assigned user is notified."""
454+
# Create an activity for the team
455+
self.team1.member_ids = [
456+
(6, 0, [self.employee.id, self.employee2.id, self.employee3.id])
457+
]
458+
activity = self.env["mail.activity"].create(
459+
{
460+
"activity_type_id": self.activity1.id,
461+
"note": "Test activity without notify_members.",
462+
"res_id": self.partner_client.id,
463+
"res_model_id": self.partner_ir_model.id,
464+
"team_user_id": self.employee.id,
465+
"team_id": self.team1.id,
466+
}
467+
)
468+
469+
# Count initial messages for team members
470+
initial_msg_count_emp = len(
471+
self.env["mail.message"].search(
472+
[("partner_ids", "in", [self.employee.partner_id.id])]
473+
)
474+
)
475+
initial_msg_count_emp2 = len(
476+
self.env["mail.message"].search(
477+
[("partner_ids", "in", [self.employee2.partner_id.id])]
478+
)
479+
)
480+
initial_msg_count_emp3 = len(
481+
self.env["mail.message"].search(
482+
[("partner_ids", "in", [self.employee3.partner_id.id])]
483+
)
484+
)
485+
486+
# Call action_notify
487+
activity.action_notify()
488+
489+
# Verify only the assigned user (employee) received a notification
490+
final_msg_count_emp = len(
491+
self.env["mail.message"].search(
492+
[("partner_ids", "in", [self.employee.partner_id.id])]
493+
)
494+
)
495+
final_msg_count_emp2 = len(
496+
self.env["mail.message"].search(
497+
[("partner_ids", "in", [self.employee2.partner_id.id])]
498+
)
499+
)
500+
final_msg_count_emp3 = len(
501+
self.env["mail.message"].search(
502+
[("partner_ids", "in", [self.employee3.partner_id.id])]
503+
)
504+
)
505+
506+
self.assertGreater(
507+
final_msg_count_emp,
508+
initial_msg_count_emp,
509+
"Assigned user should receive notification",
510+
)
511+
self.assertEqual(
512+
final_msg_count_emp2,
513+
initial_msg_count_emp2,
514+
"Non-assigned team member should not receive notification",
515+
)
516+
self.assertEqual(
517+
final_msg_count_emp3,
518+
initial_msg_count_emp3,
519+
"Non-assigned team member should not receive notification",
520+
)
521+
522+
def test_notify_members_enabled(self):
523+
"""Test that when notify_members is True, all team members are notified."""
524+
# Create an activity for the team
525+
self.team2.member_ids = [
526+
(6, 0, [self.employee.id, self.employee2.id, self.employee3.id])
527+
]
528+
activity = self.env["mail.activity"].create(
529+
{
530+
"activity_type_id": self.activity1.id,
531+
"note": "Test activity with notify_members enabled.",
532+
"res_id": self.partner_client.id,
533+
"res_model_id": self.partner_ir_model.id,
534+
"team_user_id": self.employee.id,
535+
"team_id": self.team2.id,
536+
}
537+
)
538+
539+
# Count initial messages for team members
540+
initial_msg_count_emp = len(
541+
self.env["mail.message"].search(
542+
[("partner_ids", "in", [self.employee.partner_id.id])]
543+
)
544+
)
545+
initial_msg_count_emp2 = len(
546+
self.env["mail.message"].search(
547+
[("partner_ids", "in", [self.employee2.partner_id.id])]
548+
)
549+
)
550+
initial_msg_count_emp3 = len(
551+
self.env["mail.message"].search(
552+
[("partner_ids", "in", [self.employee3.partner_id.id])]
553+
)
554+
)
555+
556+
# Call action_notify
557+
activity.action_notify()
558+
559+
# Verify all team members received notifications
560+
final_msg_count_emp = len(
561+
self.env["mail.message"].search(
562+
[("partner_ids", "in", [self.employee.partner_id.id])]
563+
)
564+
)
565+
final_msg_count_emp2 = len(
566+
self.env["mail.message"].search(
567+
[("partner_ids", "in", [self.employee2.partner_id.id])]
568+
)
569+
)
570+
final_msg_count_emp3 = len(
571+
self.env["mail.message"].search(
572+
[("partner_ids", "in", [self.employee3.partner_id.id])]
573+
)
574+
)
575+
576+
self.assertGreater(
577+
final_msg_count_emp,
578+
initial_msg_count_emp,
579+
"Assigned user should receive notification",
580+
)
581+
self.assertGreater(
582+
final_msg_count_emp2,
583+
initial_msg_count_emp2,
584+
"Team member 2 should receive notification",
585+
)
586+
self.assertGreater(
587+
final_msg_count_emp3,
588+
initial_msg_count_emp3,
589+
"Team member 3 should receive notification",
590+
)
591+
592+
def test_notify_members_no_duplicate_for_assigned_user(self):
593+
"""Test that the assigned user doesn't get duplicate notifications."""
594+
# Create an activity assigned to employee (who is in the team)
595+
activity = self.env["mail.activity"].create(
596+
{
597+
"activity_type_id": self.activity1.id,
598+
"note": "Test no duplicate notifications.",
599+
"res_id": self.partner_client.id,
600+
"res_model_id": self.partner_ir_model.id,
601+
"user_id": self.employee.id,
602+
"team_id": self.team2.id,
603+
}
604+
)
605+
606+
# Count initial messages for the assigned user
607+
initial_msg_count = len(
608+
self.env["mail.message"].search(
609+
[("partner_ids", "in", [self.employee.partner_id.id])]
610+
)
611+
)
612+
613+
# Call action_notify
614+
activity.action_notify()
615+
616+
# Verify the assigned user received exactly one new notification
617+
final_msg_count = len(
618+
self.env["mail.message"].search(
619+
[("partner_ids", "in", [self.employee.partner_id.id])]
620+
)
621+
)
622+
623+
# The assigned user should receive only 1 notification (from parent method)
624+
# not 2 (parent + team member notification)
625+
self.assertEqual(
626+
final_msg_count - initial_msg_count,
627+
1,
628+
"Assigned user should receive exactly one notification, not duplicates",
629+
)
630+
631+
def test_notify_members_activity_without_team(self):
632+
"""Test that activities without a team still work correctly."""
633+
# Create an activity without a team
634+
activity = self.env["mail.activity"].create(
635+
{
636+
"activity_type_id": self.activity1.id,
637+
"note": "Test activity without team.",
638+
"res_id": self.partner_client.id,
639+
"res_model_id": self.partner_ir_model.id,
640+
"user_id": self.employee.id,
641+
}
642+
)
643+
644+
# Count initial messages
645+
initial_msg_count = len(
646+
self.env["mail.message"].search(
647+
[("partner_ids", "in", [self.employee.partner_id.id])]
648+
)
649+
)
650+
651+
# Call action_notify - should not raise any error
652+
activity.action_notify()
653+
654+
# Verify the assigned user received a notification
655+
final_msg_count = len(
656+
self.env["mail.message"].search(
657+
[("partner_ids", "in", [self.employee.partner_id.id])]
658+
)
659+
)
660+
661+
self.assertGreater(
662+
final_msg_count,
663+
initial_msg_count,
664+
"Assigned user should receive notification even without team",
665+
)
666+
667+
def test_notify_members_activity_without_assigned_user(self):
668+
"""Test that team notifications work when activity has no assigned user."""
669+
# Create an activity without an assigned user
670+
activity = self.env["mail.activity"].create(
671+
{
672+
"activity_type_id": self.activity1.id,
673+
"note": "Test activity without assigned user.",
674+
"res_id": self.partner_client.id,
675+
"res_model_id": self.partner_ir_model.id,
676+
"team_id": self.team2.id,
677+
}
678+
)
679+
680+
# Count initial messages for team members
681+
initial_msg_count_emp = len(
682+
self.env["mail.message"].search(
683+
[("partner_ids", "in", [self.employee.partner_id.id])]
684+
)
685+
)
686+
initial_msg_count_emp2 = len(
687+
self.env["mail.message"].search(
688+
[("partner_ids", "in", [self.employee2.partner_id.id])]
689+
)
690+
)
691+
692+
# Call action_notify
693+
activity.action_notify()
694+
695+
# Verify all team members received notifications
696+
final_msg_count_emp = len(
697+
self.env["mail.message"].search(
698+
[("partner_ids", "in", [self.employee.partner_id.id])]
699+
)
700+
)
701+
final_msg_count_emp2 = len(
702+
self.env["mail.message"].search(
703+
[("partner_ids", "in", [self.employee2.partner_id.id])]
704+
)
705+
)
706+
707+
self.assertGreater(
708+
final_msg_count_emp,
709+
initial_msg_count_emp,
710+
"Team member 1 should receive notification",
711+
)
712+
self.assertGreater(
713+
final_msg_count_emp2,
714+
initial_msg_count_emp2,
715+
"Team member 2 should receive notification",
716+
)

mail_activity_team/views/mail_activity_team_views.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<field name="active" invisible="1" />
3434
<field name="name" />
3535
<field name="user_id" />
36+
<field name="notify_members" />
3637
</group>
3738
<group name="models">
3839
<field

0 commit comments

Comments
 (0)