Skip to content

Conversation

@stpa-odoo
Copy link
Contributor

@stpa-odoo stpa-odoo commented Dec 3, 2024

Steps to reproduce the issue:

(Easier to reproduce client side with CRM installed)

  1. Install crm
  2. Create Second Company
  3. Create New User with Current Company as Default Company and Second Company in Allowed Companies
  4. Create CRM Team belonging to Second Company
  5. Add New User to Members of CRM Team
  6. Receive error:
[New User] belongs to company [Current Company] and "Sales Team" (crm_team_id: [CRM Team]) belongs to another company.

Explanation:

crm.team.member is created when adding res.users to crm.team.member_ids. Contrary to res.users, a company_check is done when linking crm.team and crm.team.member together. crm.team.member.company_id is related to user_id.company_id and, in the case above, does not match crm.team.company_id, raising an error because of it.

Fix reasoning:

Removing restriction, as we want to avoid other multi-company issues by changing the behaviour.

opw-4214192


@nd-dew note:
Situation recap
image

So it was proposed to modify the field definition, seems to me that removing check_company from the field definition is a legal move, since it is an ORM level constraint (doesn't change db schema).

However looking at the tests it seems like this is desired limitation.

Note that #171079 introduced checking for allowed companies, but the default company still takes priority.

@robodoo
Copy link
Contributor

robodoo commented Dec 3, 2024

Pull request status dashboard

@C3POdoo C3POdoo added the OE the report is linked to a support ticket (opw-...) label Dec 3, 2024
@stpa-odoo stpa-odoo force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch from 8bea5a0 to 18968fa Compare December 5, 2024 10:21
@nd-dew
Copy link
Contributor

nd-dew commented Dec 12, 2024

Don't we need to generate translations for the error that you added 🤔

Copy link
Contributor

@nd-dew nd-dew left a comment

Choose a reason for hiding this comment

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

Would add note that "crm" module is needed to reproduce.

@stpa-odoo stpa-odoo force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch 2 times, most recently from b5c7352 to 77ccaf3 Compare December 16, 2024 15:27
@stpa-odoo stpa-odoo marked this pull request as ready for review December 17, 2024 06:40
@C3POdoo C3POdoo requested a review from a team December 17, 2024 06:42
@stpa-odoo stpa-odoo force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch 3 times, most recently from 78543f4 to 471e474 Compare January 21, 2025 13:13
@nd-dew nd-dew marked this pull request as draft March 25, 2025 10:56
@nd-dew
Copy link
Contributor

nd-dew commented Mar 25, 2025

Taking over the PR, recap: on the write

  • in BaseModel._check_company we grab company_id from crm.team.member (that's the default company of the user)
  • we check if crm.team is a subset of that company ---> It is not

Reconsidering STPA solution, it was rather deep for 16 version as it modified field.

Experiment: can we actually skip thee additional check in the CrmTeam.write method ?

@nd-dew nd-dew force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch from 87a1bdb to 6941f8a Compare March 25, 2025 12:35
@nd-dew
Copy link
Contributor

nd-dew commented Mar 25, 2025

Experiment 1: Not sure what's the purpose of that manual check, but it doesn't matter in this case. _check_company will be called either way because crm_team_id is a field marked with _check_company=True

reverting

EDIT: The purpose of the manual check was the same as in my PR, so to enforce constraint both ways (when writing on crm_team)

@nd-dew nd-dew force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch from 6941f8a to 0257bbf Compare March 25, 2025 13:24
@nd-dew
Copy link
Contributor

nd-dew commented Mar 26, 2025

Moved company check into a constraint

Inferred Specs

Before doing so grabbing all requirements from tests and related task to ensure consistency

image

Motivation

Seems like this case wasn't considered at the time and it is possible to fix that and by using api.constraint, so why not since we don't touch db schema, and make constraints cleaner

@nd-dew nd-dew force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch 2 times, most recently from c0d050c to bb41d93 Compare March 26, 2025 16:44
@nd-dew nd-dew marked this pull request as ready for review March 27, 2025 09:51
Copy link
Contributor

@reth-odoo reth-odoo left a comment

Choose a reason for hiding this comment

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

Note for reviewer, if we're willing to ask for an exception for old stable we could probably just add a related "company_ids" field and get framework support for free.
Probably not worth it though, current fix seems ok.

Leaving mostly documentation comments, just one thing for the test we usually prefer using the test helpers in our modules.
Tested manually, seems to work fine otherwise.

@jeh-odoo 🍞 hi
Do you have an opinion on this? It's relatively touchy so not sure for old stable but it could block valid flows (this comes from a ticket, in the first place)

Would say worth fixing at least in more recent versions, if nothing else

Copy link
Contributor

@jeh-odoo jeh-odoo left a comment

Choose a reason for hiding this comment

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

Hello there

Not the first time I see this feedback I think, and the fix seems ok to me so why not.
I remember reading some mail about fix in older version and the need to be more strict.
So is it considered a bug? I would say yes.
Is it a traceback or a critical issue ? I would say no to this one, and there should be some workaround if I'm not wrong.
Is it a touchy behavior ? Yes.

So I think that I would be more at ease to fix this in a newish version than in 16.0. As a side note, it made me think about the task 2951093.

Cheers!

@nd-dew
Copy link
Contributor

nd-dew commented Mar 28, 2025

Re-Target: 16 -> 18

Agreed, moving the fix into 18.0

@nd-dew nd-dew marked this pull request as draft March 28, 2025 07:51
@nd-dew nd-dew changed the base branch from 16.0 to 18.0 March 28, 2025 07:54
@nd-dew nd-dew force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch 3 times, most recently from bbbc817 to 9d49c47 Compare March 28, 2025 09:48
@nd-dew nd-dew force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch 2 times, most recently from 4081770 to d33b416 Compare March 28, 2025 16:57
@nd-dew nd-dew marked this pull request as ready for review March 28, 2025 17:57
@C3POdoo C3POdoo requested a review from a team March 28, 2025 17:59
@nd-dew nd-dew force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch from d33b416 to 44f4654 Compare April 1, 2025 10:42
@reth-odoo
Copy link
Contributor

oop 🆙

@nd-dew
Copy link
Contributor

nd-dew commented May 14, 2025

hey @jeh-odoo, can we merge it?

Copy link
Contributor

@jeh-odoo jeh-odoo left a comment

Choose a reason for hiding this comment

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

Hello @nd-dew
Thanks for the reminder on this one 🙂
Had another look and retested the whole thing. Seems good enough for me, just one last picky comment that I saw.

Cheers!

This change ensures users can be members of the Sales Teams
as long as the team's company is in their `company_ids`,
regardless of their default company.

Previously, membership checks relied on the user's default company
(`company_id`), which was too strict in multi-company setups.
This led to errors when assigning a user to a team that belongs to
a company they had access to, but wasn't their default.

Now, validations are handled via an explicit `@api.constrains`,
and enforced on write for both user assignments and team company changes.

Visual specs
---

 entities       entities
 connected      connected
   to             to
 company 1      company 2
    │                │
    │                │
┌─────────────────────────┐
│   │      UserC1C2  │    │
│ default            │    │
│   │                │    │
│ allowed         allowed │
└─────────────────────────┘
    │                │
    │            ┌───┴────┐
    │            │ TeamC2 │
    │            └────────┘
In the above scenario UserC1C2 should be allowed to join TeamC2
-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -
    │                │
┌───┴──────┐         │
│ UserC1   │         │
│ default  │         │
│   │      │         │
│ allowed  │         │
└───┬──────┘     ┌───┴────┐
    │            │ TeamC2 │
    │            └────────┘
In the above scenario UserC1 should NOT be allowed to join TeamC2
-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -
    │                │
┌───┴──────┐      ─ ─┴─ ─ ──
│ UserC1   │     │          │
│ default  ├──X─►
│   │      │     │          │
│ allowed  │
└───┬──────┘     └─ ─┬─ ─ ─ ┘
┌───┴────┐       ┌ ─ └─ ─ ┐
│ TeamC1 ├───X─►│
└───┬────┘        ─ ─┬─ ─ ┘

In the above scenario UserC1 should be allowed to joing TeamC1
 ,but after he does:
we can't move him to another company (case above)
nor we can't switch team to another company (also case above)
-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -

Reproduce
---
1. Create Second Company
2. Create New User with Current Company as Default Company and Second Company in Allowed Companies
3. Create CRM Team belonging to Second Company  <--- HERE I can't do that, need to be in the 2nd company
4. Add New User to Members of CRM Team
5. Receive error:
Error
```
[New User] belongs to company [Current Company] and "Sales Team" (crm_team_id: [CRM Team]) belongs to another company.
```

References
---
(ref.1)
[REF] sales_team: improve multi company management
6bacc1c

opw-4214192
@nd-dew
Copy link
Contributor

nd-dew commented Oct 2, 2025

Revisiting this!

Thanks you all for reviews. Applied all suggestions.

I went over this one more time and I've noticed one edge-case that isn't covered.
Constraint on the crm_team_member is sufficient to for changing members on the sales team, but it doesn't cover a case when a user is in already in a sales team and the sales team changes companies.

I solve that by one more constraint on the CrmTeam ;)

@nd-dew nd-dew force-pushed the 16.0-opw-4214192-crm_team_member_company-stpa branch from 44f4654 to 0f345d8 Compare October 2, 2025 09:17
Copy link
Contributor

@tde-banana-odoo tde-banana-odoo left a comment

Choose a reason for hiding this comment

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

Zboing


@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

@nd-dew
Copy link
Contributor

nd-dew commented Oct 20, 2025

Trying tor reduce the constrains method with check_company -> Can't

index 6c8f52655aec..520ab435c9c9 100644
--- a/addons/sales_team/models/crm_team.py
+++ b/addons/sales_team/models/crm_team.py
@@ -114,7 +114,8 @@ class CrmTeam(models.Model):
     crm_team_member_ids = fields.One2many(
         'crm.team.member', 'crm_team_id', string='Sales Team Members',
         context={'active_test': True},
-        help="Add members to automatically assign their documents to this sales team.")
+        help="Add members to automatically assign their documents to this sales team.",
+        check_company=True)
     crm_team_member_all_ids = fields.One2many(
         'crm.team.member', 'crm_team_id', string='Sales Team Members (incl. inactive)',
         context={'active_test': False})
@@ -129,19 +130,6 @@ class CrmTeam(models.Model):
     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 _constrains_company_members(self):
-        for team in self.filtered('company_id'):
-            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'))
-                ))
-

Can't do that
The check_company attribute cannot replace the custom 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OE the report is linked to a support ticket (opw-...)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants