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
35 changes: 35 additions & 0 deletions project_customer_access/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,41 @@
class ResUsers(models.Model):
_inherit = "res.users"

def _get_support_partner_domain(self):
"""Domain of the partners visible by a customer user (used by the record
rule on res.partner): the partners of the users sharing the same
cross-connect client (i.e. the members of his company), the internal
users (internal users without cross-connect client, needed to display
the assignees / authors / followers of the tasks) and the customer
partners of the projects of this client, plus his commercial partner, the
companies and OdooBot.

The relations are traversed by SQL, so the domain stays up to date when
users/projects change. Users without a cross-connect client are not
restricted.
"""
self.ensure_one()
client = self.cross_connect_client_id
if not client:
return [(1, "=", 1)]
return [
"|",
"|",
"|",
"|",
"|",
"|",
("user_ids.cross_connect_client_id", "in", client.ids),
"&",
("user_ids.groups_id", "in", [self.env.ref("base.group_user").id]),
("user_ids.cross_connect_client_id", "=", False),
("project_ids.cross_connect_client_id", "in", client.ids),
("task_ids.project_id.cross_connect_client_id", "in", client.ids),
("id", "=", self.commercial_partner_id.id),
("id", "in", self.company_ids.partner_id.ids),
("id", "=", self.env.ref("base.partner_root").id),
]

@api.depends("cross_connect_client_id")
def _compute_display_name(self):
return super()._compute_display_name()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@
<field name="groups" eval="[(4,ref('project_customer_access.group_budget'))]" />
</record>

<record id="rule_project_customer_access_partners" model="ir.rule">
<field name="name">Project Customer Access: Partners</field>
<field name="model_id" ref="base.model_res_partner" />
<field name="domain_force">user._get_support_partner_domain()</field>
<field name="perm_read" eval="1" />
<field name="perm_write" eval="0" />
<field name="perm_create" eval="0" />
<field name="perm_unlink" eval="0" />
<field
name="groups"
eval="[(4,ref('project_customer_access.group_customer'))]"
/>
</record>

<record id="mail.mail_message_subtype_rule_public" model="ir.rule">
<field
name="groups"
Expand Down
25 changes: 25 additions & 0 deletions project_customer_access/tests/test_project_customer_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,28 @@ def test_create_task_customer_no_project(self):
"Uh-oh! Looks like you have stumbled upon some top-secret records.",
):
f.save()

def test_visible_partners(self):
"""Customer users see the members of their company, the users working on
their projects and the partners of these projects."""
colleague = new_test_user(
self.env,
login="test_colleague",
groups="project_customer_access.group_customer",
)
colleague.cross_connect_client_id = self.client
support_user = new_test_user(
self.env, login="test_support_user", groups="base.group_user"
)
self.task.user_ids = [Command.link(support_user.id)]
other_partner = self.env["res.partner"].create({"name": "Other Company"})

partners = self.env["res.partner"].with_user(self.customer).search([])
self.assertIn(self.customer.partner_id, partners)
self.assertIn(colleague.partner_id, partners)
self.assertIn(support_user.partner_id, partners)
self.assertNotIn(other_partner, partners)
with self.assertRaises(AccessError):
other_partner.with_user(self.customer).read(["name"])
# a user without cross-connect client is not restricted
self.assertIn(other_partner, self.env["res.partner"].search([]))
Loading