Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][IMP] attribute_set: Several improvements #171

Merged
merged 6 commits into from
Nov 16, 2023
Merged
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
6 changes: 4 additions & 2 deletions attribute_set/models/attribute_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ def _build_attribute_eview(self):
"""
attribute_eview = etree.Element("group", name="attributes_group", col="4")
groups = []

for attribute in self:
att_group = attribute.attribute_group_id
att_group_name = att_group.name.capitalize()
Expand All @@ -218,7 +217,10 @@ def _build_attribute_eview(self):
groups.append(att_group)

setup_modifiers(attribute_egroup)
attribute._build_attribute_field(attribute_egroup)
attribute_with_env = (
attribute.sudo() if attribute.check_access_rights("read") else self
)
attribute_with_env._build_attribute_field(attribute_egroup)

return attribute_eview

Expand Down
6 changes: 5 additions & 1 deletion attribute_set/models/attribute_option.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ def _selection_model_list(self):

name = fields.Char(translate=True, required=True)

value_ref = fields.Reference(selection="_selection_model_list", string="Reference")
value_ref = fields.Reference(
selection="_selection_model_list",
string="Reference",
groups="base.group_erp_manager",
)

attribute_id = fields.Many2one(
"attribute.attribute",
Expand Down
8 changes: 7 additions & 1 deletion attribute_set/models/attribute_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,11 @@ class AttributeSet(models.Model):
column1="attribute_set_id",
column2="attribute_id",
)

model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="cascade")
model = fields.Char(
related="model_id.model",
string="Model Name",
store=True,
help="This is a technical field in order to build filters on this one to avoid"
"access on ir.model",
)
42 changes: 31 additions & 11 deletions attribute_set/models/attribute_set_owner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,22 @@ class AttributeSetOwnerMixin(models.AbstractModel):
_name = "attribute.set.owner.mixin"
_description = "Attribute set owner mixin"

attribute_set_id = fields.Many2one("attribute.set", "Attribute Set")
attribute_set_id = fields.Many2one(
"attribute.set",
"Attribute Set",
domain=lambda self: self._get_attribute_set_owner_model(),
)

@api.model
def _get_attribute_set_owner_model(self):
return [("model", "=", self._name)]

@api.model
def _build_attribute_eview(self):
"""Override Attribute's method _build_attribute_eview() to build an
attribute eview with the mixin model's attributes"""
domain = [
("model_id.model", "=", self._name),
("model", "=", self._name),
("attribute_set_ids", "!=", False),
]
if not self._context.get("include_native_attribute"):
Expand All @@ -36,7 +44,7 @@ def remove_native_fields(self, eview):
"""Remove native fields related to native attributes from eview"""
native_attrs = self.env["attribute.attribute"].search(
[
("model_id.model", "=", self._name),
("model", "=", self._name),
("attribute_set_ids", "!=", False),
("nature", "=", "native"),
]
Expand Down Expand Up @@ -71,12 +79,24 @@ def _insert_attribute(self, arch):
placeholder[0].getparent().replace(placeholder[0], attribute_eview)
return etree.tostring(eview, pretty_print=True)

@api.model
def get_views(self, views, options=None):
result = super().get_views(views, options=options)
form_arch = result.get("views", {}).get("form", {}).get("arch")
if form_arch:
result["views"]["form"]["arch"] = self._insert_attribute(
result["views"]["form"]["arch"]
)
def get_view(self, view_id=None, view_type="form", **options):
result = super().get_view(view_id=view_id, view_type=view_type, **options)
if view_type == "form":
form_arch = result.get("arch")
if form_arch:
result["arch"] = self._insert_attribute(result["arch"])
return result

@api.model
def _get_view_fields(self, view_type, models):
models = super()._get_view_fields(view_type, models)
if self._name in models and view_type == "form":
# we must ensure that the fields defined in the attributes set
# are declared into the list of fields to load for the form view
domain = [
("model", "=", self._name),
("attribute_set_ids", "!=", False),
]
attributes = self.env["attribute.attribute"].search(domain)
models[self._name].update(attributes.sudo().mapped("name"))
return models
5 changes: 5 additions & 0 deletions attribute_set/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@
class ResPartner(models.Model):
_inherit = ["res.partner", "attribute.set.owner.mixin"]
_name = "res.partner"


class ResCountry(models.Model):
_inherit = ["res.country", "attribute.set.owner.mixin"]
_name = "res.country"
90 changes: 84 additions & 6 deletions attribute_set/tests/test_build_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from lxml import etree
from odoo_test_helper import FakeModelLoader

from odoo.tests import TransactionCase
from odoo.tests import Form, TransactionCase, users


class BuildViewCase(TransactionCase):
Expand All @@ -29,11 +29,25 @@ def _create_attribute(cls, vals):
@classmethod
def setUpClass(cls):
super().setUpClass()

# Demo user will be a base user to read model
cls.demo = cls.env.ref("base.user_demo")

# This user will have access to
cls.attribute_manager_user = cls.env["res.users"].create(
{
"name": "Attribute Manager",
"login": "attribute_manager",
"email": "attribute.manager@test.odoo.com",
}
)
cls.attribute_manager_user.groups_id |= cls.env.ref("base.group_erp_manager")

cls.loader = FakeModelLoader(cls.env, cls.__module__)
cls.loader.backup_registry()
from .models import ResPartner
from .models import ResCountry, ResPartner

cls.loader.update_registry((ResPartner,))
cls.loader.update_registry((ResPartner, ResCountry))

# Create a new inherited view with the 'attributes' placeholder.
cls.view = cls.env["ir.ui.view"].create(
Expand All @@ -44,6 +58,7 @@ def setUpClass(cls):
"arch": """
<xpath expr="//notebook" position="inside">
<page name="partner_attributes">
<field name="attribute_set_id"/>
<separator name="attributes_placeholder" />
</page>
</xpath>
Expand Down Expand Up @@ -126,12 +141,32 @@ def setUpClass(cls):
}
)

cls.multi_attribute = cls._create_attribute(
{
"attribute_type": "multiselect",
"name": "x_multi_attribute",
"option_ids": [
(0, 0, {"name": "Value 1"}),
(0, 0, {"name": "Value 2"}),
],
"attribute_set_ids": [(6, 0, [cls.set_1.id])],
"attribute_group_id": cls.group_1.id,
}
)

# Add attributes for country
cls.model_id = cls.env.ref("base.model_res_country").id
cls.be = cls.env.ref("base.be")
cls.set_country = cls._create_set("Set Country")
cls.model_id = cls.env.ref("base.model_res_partner").id

@classmethod
def tearDownClass(cls):
cls.loader.restore_registry()
return super(BuildViewCase, cls).tearDownClass()

# TEST write on attributes
@users("demo")
def test_write_attribute_values_text(self):
self.partner.write({"x_attr_2": "abcd"})
self.assertEqual(self.partner.x_attr_2, "abcd")
Expand Down Expand Up @@ -198,7 +233,7 @@ def test_attribute_order(self):
for item in eview.getchildren()[0].getchildren()
if item.tag == "field"
]
self.assertEqual(attrs, ["x_attr_1", "x_attr_2"])
self.assertEqual(attrs, ["x_attr_1", "x_attr_2", "x_multi_attribute"])

self.attr_1.sequence = 3
eview = self.env["res.partner"]._build_attribute_eview()
Expand All @@ -207,7 +242,7 @@ def test_attribute_order(self):
for item in eview.getchildren()[0].getchildren()
if item.tag == "field"
]
self.assertEqual(attrs, ["x_attr_2", "x_attr_1"])
self.assertEqual(attrs, ["x_attr_2", "x_attr_1", "x_multi_attribute"])

def test_attr_visibility(self):
attrs = self._get_attr_element("x_attr_1").get("attrs")
Expand All @@ -226,6 +261,7 @@ def test_attr_required(self):
attrs = self._get_attr_element("x_attr_1").get("attrs")
self._check_attrset_required(attrs, [self.set_1.id])

@users("attribute_manager")
def test_render_all_field_type(self):
field = self.env["attribute.attribute"]._fields["attribute_type"]
for attr_type, _name in field.selection:
Expand All @@ -240,7 +276,9 @@ def test_render_all_field_type(self):
"attribute_set_ids": [(6, 0, [self.set_1.id])],
}
)
attr = self._get_attr_element(name)
new_self = self
new_self.env = self.env(user=self.demo, su=False)
attr = new_self._get_attr_element(name)
self.assertIsNotNone(attr)
if attr_type == "text":
self.assertTrue(attr.get("nolabel"))
Expand Down Expand Up @@ -304,3 +342,43 @@ def test_unlink_native_attribute(self):
self.assertTrue(
self.env["ir.model.fields"].browse([attr_native_field_id]).exists()
)

# TEST form views rendering
@users("demo")
def test_model_form(self):
# Test attributes modifications through form
self.assertFalse(self.partner.x_attr_3)
with Form(
self.partner.with_user(self.demo).with_context(load_all_views=True)
) as partner_form:
partner_form.attribute_set_id = self.set_1
partner_form.x_attr_3 = True
partner_form.x_attr_select = self.attr_select_option
partner_form.x_multi_attribute.add(self.multi_attribute.option_ids[0])
partner = partner_form.save().with_user(self.demo)
self.assertTrue(partner.x_attr_3)
self.assertTrue(partner.x_attr_select)
# As options are Many2many, Form() is not able to render the sub form
# This should pass, checking fields are rendered without error with
# demo user
with Form(partner.x_multi_attribute):
pass

def test_models_fields_for_get_views(self):
# this test is here to ensure that attributes defined in attribute_set
# and added to the view are correctly added to the list of fields
# to load for the view
result = self.env["res.partner"].get_views([(self.view.id, "form")])
fields = result["models"].get("res.partner")
self.assertIn("x_attr_1", fields)
self.assertIn("x_attr_2", fields)
self.assertIn("x_attr_3", fields)
self.assertIn("x_attr_4", fields)

@users("demo")
def test_model_form_domain(self):
# Test attributes modifications through form
partner = self.partner.with_user(self.env.user)
self.assertFalse(partner.x_attr_3)
sets = partner.attribute_set_id.search(partner._get_attribute_set_owner_model())
self.assertEqual(self.set_1 | self.set_2, sets)
Loading