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
20 changes: 17 additions & 3 deletions addons/sales_team/models/crm_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,19 @@ def _get_default_favorite_user_ids(self):
dashboard_button_name = fields.Char(string="Dashboard Button", compute='_compute_dashboard_button_name')
dashboard_graph_data = fields.Text(compute='_compute_dashboard_graph')

@api.constrains('company_id')
def _constrain_company_members(self):
for team in self.filtered('company_id'):
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't _check_company called by _check_company_auto sufficient ? Looking quickly at the code, if we change company_id, it should recompute constraints of relational fields. But I see we did not define check_company on specific fields ?

Copy link
Contributor

@nd-dew nd-dew Oct 20, 2025

Choose a reason for hiding this comment

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

Good point, seems like moving it to crm_team_mamber_ids field seem to work in the same way

The check_company cannot replace the custom constrains method because it only checks the company_id against the main record's company_id.
It does not consider allowed companies unless the model defines company_ids, which crm.team does not.
... it would take us back to the error we started with

invalid_members = team.crm_team_member_ids.filtered(
lambda m: team.company_id not in m.user_id.company_ids
)
if invalid_members:
raise UserError(_("The following team members are not allowed in company '%(company)s' of the Sales Team '%(team)s': %(users)s",
company=team.company_id.display_name,
team=team.name,
users=", ".join(invalid_members.mapped('user_id.name'))
))

@api.depends('sequence') # TDE FIXME: force compute in new mode
def _compute_is_membership_multi(self):
multi_enabled = self.env['ir.config_parameter'].sudo().get_param('sales_team.membership_multi', False)
Expand Down Expand Up @@ -224,9 +237,10 @@ def create(self, vals_list):

def write(self, values):
res = super(CrmTeam, self).write(values)
# manually launch company sanity check
if values.get('company_id'):
self.crm_team_member_ids._check_company(fnames=['crm_team_id'])

if values.get('company_id'): # Force re-check of memberships constraint for this team
for team in self:
team.crm_team_member_ids._constrains_membership()

if values.get('member_ids'):
self._add_members_to_favorites()
Expand Down
12 changes: 11 additions & 1 deletion addons/sales_team/models/crm_team_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class CrmTeamMember(models.Model):
'crm.team', string='Sales Team',
group_expand='_read_group_expand_full', # Always display all the teams
default=False, # TDE: temporary fix to activate depending computed fields
check_company=True, index=True, ondelete="cascade", required=True)
index=True, ondelete="cascade", required=True)
user_id = fields.Many2one(
'res.users', string='Salesperson', # TDE FIXME check responsible field
check_company=True, index=True, ondelete='cascade', required=True,
Expand Down Expand Up @@ -77,6 +77,16 @@ def _constrains_membership(self):
duplicates=", ".join("%s (%s)" % (m.user_id.name, m.crm_team_id.name) for m in duplicates)
))

@api.constrains('crm_team_id', 'user_id')
def _constrains_company_membership(self):
for membership in self.filtered(lambda m: m.crm_team_id.company_id):
if membership.crm_team_id.company_id not in membership.user_id.company_ids:
raise exceptions.UserError(_("User '%(user)s' is not allowed in the company '%(company)s' of the Sales Team '%(team)s'.",
user=membership.user_id.name,
company=membership.crm_team_id.company_id.display_name,
team=membership.crm_team_id.name
))

@api.depends('crm_team_id', 'is_membership_multi', 'user_id')
@api.depends_context('default_crm_team_id')
def _compute_user_in_teams_ids(self):
Expand Down
17 changes: 15 additions & 2 deletions addons/sales_team/tests/test_sales_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from odoo import exceptions
from odoo.tests import tagged, users
from odoo.addons.mail.tests.common import mail_new_test_user

from odoo.addons.sales_team.tests.common import SalesTeamCommon, TestSalesCommon, TestSalesMC

Expand Down Expand Up @@ -164,7 +165,7 @@ def test_team_members(self):
team_c2.write({'member_ids': [(4, self.env.user.id)]})
self.assertEqual(team_c2.member_ids, self.env.user)

# cannot add someone from another company
# cannot add someone from another company (when user allowed only in c1 and team is in c2)
with self.assertRaises(exceptions.UserError):
team_c2.write({'member_ids': [(4, self.user_sales_salesman.id)]})

Expand All @@ -174,7 +175,7 @@ def test_team_members(self):
team_c2.write({'member_ids': [(4, self.user_sales_salesman.id)]})
self.assertEqual(team_c2.member_ids, self.user_sales_salesman)

# cannot change company as it breaks memberships mc check
# cannot change team company if it's users aren't allowed in the new company
with self.assertRaises(exceptions.UserError):
team_c2.write({'company_id': self.company_2.id})

Expand Down Expand Up @@ -204,6 +205,18 @@ def test_team_memberships(self):
with self.assertRaises(exceptions.UserError):
team_c2.write({'company_id': self.company_2.id})

def test_user_allowed_to_join_team_in_company_ids(self):
"""User should be allowed in a team if the team's company is in their allowed companies"""
c1, c2 = self.company_main, self.company_2
user_c1_c2 = mail_new_test_user(
self.env,
login=f"Test_user_default_to_c{c1.id}_allowed_{'c'.join(map(str, [c1.id, c2.id]))}",
company_id=c1.id,
company_ids=[(4, company.id) for company in c1 + c2]
)
self.team_c2.write({'member_ids': [(4, user_c1_c2.id)]})
self.assertIn(user_c1_c2, self.team_c2.member_ids)


@tagged('post_install', '-at_install')
class TestAccessRights(SalesTeamCommon):
Expand Down