Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion mail_activity_team/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ Contributors

- Alexey Pelykh alexey.pelykh@corphub.eu

- Stefan Rijnhart (stefan@opener.amsterdam)
- Stefan Rijnhart (stefan@opener.amsterdam)
- `glueckkanja AG <https://glueckkanja.com/>`__

- Christopher Rogos (crogos@gmail.com)

Other credits
-------------
Expand Down
147 changes: 143 additions & 4 deletions mail_activity_team/models/mail_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

from odoo import SUPERUSER_ID, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools.misc import get_lang

from odoo.addons.mail.tools.discuss import Store


class MailActivity(models.Model):
Expand Down Expand Up @@ -74,15 +77,70 @@ def create(self, vals_list):
# odoo import api, fields, models the default one linked to the activity type.
# We don't want this behavior because using the team_id, we want to assign the
# activity to the whole team.
new_vals_list = []
for vals in vals_list:
new_vals = vals.copy()
# we need to be sure that we are in a context where the team_id is set,
# and we don't want to use user_id
if vals.get("team_id"):
if new_vals.get("team_id"):
# using team, we have user_id = team_user_id,
# so if we don't have a user_team_id we don't want user_id too
if "user_id" in vals and not vals.get("team_user_id"):
del vals["user_id"]
return super().create(vals_list)
if "user_id" in new_vals and not new_vals.get("team_user_id"):
del new_vals["user_id"]
# team_user_id is a related field pointing to user_id (readonly=False).
# If left in vals, the ORM places it in the 'inversed' bucket and
# calls _inverse_related *after* the initial INSERT while user_id is
# still NULL. That triggers Model.write({'user_id': …}) which in turn
# fires action_notify() → action_notify_team() a first time, and then
# core mail.activity.create()'s post-hook fires action_notify() a
# second time — causing duplicate notifications for team members.
# Eagerly resolving team_user_id to user_id here ensures user_id is
# stored in the INSERT so no inverse write occurs.
if "team_user_id" in new_vals:
team_user_id_val = new_vals.pop("team_user_id")
new_vals.setdefault("user_id", team_user_id_val)
new_vals_list.append(new_vals)
activities = super().create(new_vals_list)
if not self.env.context.get("mail_activity_quick_update"):
# Core create() triggers action_notify() only for activities assigned to
# users different from the current one. Notify team members here only for
# activities not covered by that path to avoid duplicate notifications.
activities_without_core_notify = activities.filtered(
lambda activity: activity.user_id == self.env.user
)
activities_without_core_notify.action_notify_team()
return activities

def write(self, values):
# Notify the new team, but prevent duplicate notifications by excluding
# activities that will be notified by core write()->action_notify()
# when the user changes.
team_notify_activities = self.env["mail.activity"]
core_notified_activities = self.env["mail.activity"]
if not self.env.context.get("mail_activity_quick_update", False):
new_team_id = values.get("team_id", False)
team_notify_activities = self.filtered(
lambda activity, new_team_id=new_team_id: new_team_id
and activity.team_id.id != new_team_id
)
new_user_id = values.get("user_id", False)
user_changed_activities = self.filtered(
lambda activity, new_user_id=new_user_id: new_user_id
and activity.user_id.id != new_user_id
)
team_notify_activities |= user_changed_activities

# Core write() calls action_notify() for user changes except when
# assigning to the current user; avoid re-sending team notifications.
if new_user_id != self.env.uid:
core_notified_activities = user_changed_activities

res = super().write(values)
# notify new responsibles

if not self.env.context.get("mail_activity_quick_update", False):
(team_notify_activities - core_notified_activities).action_notify_team()
return res

@api.onchange("team_id")
def _onchange_team_id(self):
Expand Down Expand Up @@ -130,3 +188,84 @@ def _onchange_activity_type_id(self):
if self.user_id not in members and members:
self.user_id = members[:1]
return res

def _to_store_defaults(self, target):
values = super()._to_store_defaults(target)
values.append(Store.One("team_id", "name"))
return values
Comment on lines +192 to +195

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the stuff this is a port forward. But I think there are some overlapping changes with #216. Maybe we could merge the other PR first, than I'll rebase and cleanup this PR to only contain the port of #148 .

As you already mentioned in the other PR, the module is currently not working and therefore a base + port is also not working.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree


def action_notify_team(self):
# Like action_notify(), but for team members.
classified = self._classify_by_model()
for model, activity_data in classified.items():
records_sudo = self.env[model].sudo().browse(activity_data["record_ids"])
# in case record was cascade-deleted in DB, skipping unlink override
activity_data["record_ids"] = records_sudo.exists().ids

for activity in self:
if activity.res_id not in classified[activity.res_model]["record_ids"]:
continue
if not activity.team_id.notify_members:
continue

record = activity.env[activity.res_model].browse(activity.res_id)
# Notify each team member except the assigned user and the current user
members = activity.team_id.member_ids.filtered(
lambda member, assigned_user_id=activity.user_id: self.env.uid
not in member.user_ids.ids
and (
not assigned_user_id
or (assigned_user_id and assigned_user_id not in member.user_ids)
)
)
for member in members:
activity_ctx = (
activity.with_context(lang=member.lang) if member.lang else activity
)
model_description = (
activity_ctx.env["ir.model"]
._get(activity_ctx.res_model)
.display_name
)
body = activity_ctx.env["ir.qweb"]._render(
"mail.message_activity_assigned",
{
"activity": activity_ctx,
"model_description": model_description,
"is_html_empty": lambda value: not value
or value == "<p><br></p>",
},
minimal_qcontext=True,
)
record.message_notify(
partner_ids=member.sudo().partner_id.ids,
body=body,
model_description=model_description,
email_layout_xmlid="mail.mail_notification_layout",
subject=self.env._(
"%(activity_name)s: %(summary)s (Team Activity)",
activity_name=activity_ctx.res_name,
summary=activity_ctx.summary
or activity_ctx.activity_type_id.name,
),
subtitles=[
self.env._("Activity: %s", activity_ctx.activity_type_id.name),
self.env._("Team: %s", activity_ctx.team_id.name),
self.env._(
"Deadline: %s",
(
activity_ctx.date_deadline.strftime(
get_lang(activity_ctx.env).date_format
)
if hasattr(activity_ctx.date_deadline, "strftime")
else str(activity_ctx.date_deadline)
),
),
],
)

def action_notify(self):
"""Override to notify team members when notify_members is enabled."""
result = super().action_notify()
self.action_notify_team()
return result
5 changes: 5 additions & 0 deletions mail_activity_team/models/mail_activity_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ def _compute_missing_activities(self):
string="Team Members",
)
user_id = fields.Many2one(comodel_name="res.users", string="Team Leader")
notify_members = fields.Boolean(
default=False,
help="When enabled, all team members will be notified "
"when an activity is assigned to this team.",
)
count_missing_activities = fields.Integer(
string="Missing Activities", compute="_compute_missing_activities", default=0
)
Expand Down
2 changes: 2 additions & 0 deletions mail_activity_team/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@
- [CorporateHub](https://corporatehub.eu/)
- Alexey Pelykh <alexey.pelykh@corphub.eu>
- Stefan Rijnhart (<stefan@opener.amsterdam>)
- [glueckkanja AG](https://glueckkanja.com/)
- Christopher Rogos (<crogos@gmail.com>)
4 changes: 4 additions & 0 deletions mail_activity_team/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,10 @@ <h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
</ul>
</li>
<li>Stefan Rijnhart (<a class="reference external" href="mailto:stefan&#64;opener.amsterdam">stefan&#64;opener.amsterdam</a>)</li>
<li><a class="reference external" href="https://glueckkanja.com/">glueckkanja AG</a><ul>
<li>Christopher Rogos (<a class="reference external" href="mailto:crogos&#64;gmail.com">crogos&#64;gmail.com</a>)</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="other-credits">
Expand Down
Loading
Loading