Skip to content

Commit

Permalink
[FIX] mail_activity: prevent inconsistent team value
Browse files Browse the repository at this point in the history
Fixes 'Assigned user is not member of the team' when an activity is created
in the backend for a user that is not in the same teams as the user creating
the activity.

An example of such an activity is the one created for tax closing moves in
enterprises' account_reports.
  • Loading branch information
StefanRijnhart committed Jun 21, 2024
1 parent 64f4566 commit 4b922c3
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 48 deletions.
110 changes: 78 additions & 32 deletions mail_activity_team/models/mail_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,26 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import SUPERUSER_ID, _, api, fields, models
from odoo.osv.expression import AND
from odoo.exceptions import ValidationError


class MailActivity(models.Model):
_inherit = "mail.activity"

def _get_default_team_id(self, user_id=None):
if not user_id:
user_id = self.env.uid
res_model = self.env.context.get("default_res_model")
model = self.sudo().env["ir.model"].search([("model", "=", res_model)], limit=1)
domain = [("member_ids", "in", [user_id])]
if res_model:
domain.extend(
["|", ("res_model_ids", "=", False), ("res_model_ids", "in", model.ids)]
)
return self.env["mail.activity.team"].search(domain, limit=1)

user_id = fields.Many2one(string="User", required=False)
team_user_id = fields.Many2one(
string="Team user", related="user_id", readonly=False
)

team_id = fields.Many2one(
comodel_name="mail.activity.team",
default=lambda s: s._get_default_team_id(),
compute="_compute_team_id",
index=True,
readonly=False,
store=True,
)

@api.onchange("user_id")
def _onchange_user_id(self):
if not self.user_id or (
self.team_id and self.user_id in self.team_id.member_ids
):
return
self.team_id = self.with_context(
default_res_model=self.sudo().res_model_id.model
)._get_default_team_id(user_id=self.user_id.id)

@api.onchange("team_id")
def _onchange_team_id(self):
if self.team_id and self.user_id not in self.team_id.member_ids:
Expand All @@ -51,6 +32,62 @@ def _onchange_team_id(self):
else:
self.user_id = self.env["res.users"]

@api.depends("activity_type_id", "res_model", "user_id")
def _compute_team_id(self):
"""Fetch the team given the user, the model and the activity type"""
for activity in self:
model_id = self.env['ir.model']._get_id(activity.res_model)
if activity.team_id:
# Does the current team still qualify?
if (
(
not activity.user_id
or activity.user_id in activity.team_id.member_ids
)
and (
not model_id
or not activity.team_id.res_model_ids
or model_id in activity.team_id.res_model_ids.ids
)
and (
not activity.activity_type_id.default_team_id
or activity.team_id ==
activity.activity_type_id.default_team_id
)
):
continue
# Does the activity type's default team qualify?
default_team = activity.activity_type_id.default_team_id
if (
default_team
and (
not activity.user_id
or activity.user_id in default_team.member_ids
)
and (
not model_id
or not default_team.res_model_ids
or model_id in default_team.res_model_ids.ids
)
):
activity.team_id = default_team
if not activity.user_id:
activity.user_id = activity.team_id.member_ids[:1]
continue
if not activity.user_id:
continue
domain = [("member_ids", "=", activity.user_id.id)]
if model_id:
domain = AND([
domain,
[
"|",
("res_model_ids", "=", model_id),
("res_model_ids", "=", False),
],
])
activity.team_id = self.env["mail.activity.team"].search(domain, limit=1)

@api.constrains("team_id", "user_id")
def _check_team_and_user(self):
for activity in self:
Expand Down Expand Up @@ -78,12 +115,21 @@ def _check_team_and_user(self):
)
)

@api.onchange("activity_type_id")
def _onchange_activity_type_id(self):
res = super()._onchange_activity_type_id()
if self.activity_type_id.default_team_id:
self.team_id = self.activity_type_id.default_team_id
members = self.activity_type_id.default_team_id.member_ids
if self.user_id not in members and members:
self.user_id = members[:1]
return res
def _prepare_next_activity_values(self):
"""Set the default team, and a member from that team as the user"""
vals = super()._prepare_next_activity_values()
if vals.get("activity_type_id"):
activity_type = self.env["mail.activity.type"].browse(
vals["activity_type_id"]
)
team = activity_type.default_team_id
if team:
vals["team_id"] = team.id
if (
team.member_ids and (
not vals.get("user_id")
or vals["user_id"] not in team.member_ids.ids
)
):
vals["user_id"] = team.member_ids[0].id
return vals
17 changes: 1 addition & 16 deletions mail_activity_team/models/mail_activity_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ def _search_activity_team_user_ids(self, operator, operand):
def activity_schedule(
self, act_type_xmlid="", date_deadline=None, summary="", note="", **act_values
):
"""With automatic activities, the user onchange won't act so we must
ensure the right group is set and no exceptions are raised due to
user-team missmatch. We can hook onto `act_values` dict as it's passed
to the create activity method.
"""
"""Suggest a default user from the default team's members"""
if self.env.context.get("force_activity_team"):
act_values["team_id"] = self.env.context["force_activity_team"].id
if "team_id" not in act_values:
Expand All @@ -64,17 +60,6 @@ def activity_schedule(
act_values.update(
{"user_id": activity_type.default_team_id.member_ids[:1].id}
)
else:
user_id = act_values.get("user_id")
if user_id:
team = (
self.env["mail.activity"]
.with_context(
default_res_model=self._name,
)
._get_default_team_id(user_id=user_id)
)
act_values.update({"team_id": team.id})
return super().activity_schedule(
act_type_xmlid=act_type_xmlid,
date_deadline=date_deadline,
Expand Down
14 changes: 14 additions & 0 deletions mail_activity_team/tests/test_mail_activity_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,17 @@ def test_my_activity_date_deadline(self):
)
self.assertEqual(partner, self.partner_client)
self.assertEqual(partner.my_activity_date_deadline, today)

def test_create_activity_for_user_not_in_team(self):
"""As a member of a team, create an activity for a teamless member"""

activity = self.env["mail.activity"].with_user(self.employee).create(
{
"activity_type_id": self.activity2.id,
"note": "Partner activity 3",
"res_id": self.partner_client.id,
"res_model_id": self.partner_ir_model.id,
"user_id": self.employee3.id,
}
)
self.assertFalse(activity.team_id)

0 comments on commit 4b922c3

Please sign in to comment.