Skip to content

Commit

Permalink
[DEV][FIX] attribute_set: restore some features
Browse files Browse the repository at this point in the history
  • Loading branch information
kobros-tech committed Feb 10, 2025
1 parent 3daa101 commit 7884d75
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 50 deletions.
66 changes: 41 additions & 25 deletions attribute_set/models/attribute_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@

from lxml import etree

from odoo import Command, _, api, fields, models
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools.safe_eval import safe_eval

from ..utils.orm import setup_modifiers

Expand Down Expand Up @@ -83,10 +82,6 @@ class AttributeAttribute(models.Model):
"ir.model", "Relational Model", ondelete="cascade"
)

relation_model_name = fields.Char(
"Relational Model Name", related="relation_model_id.model"
)

widget = fields.Char(help="Specify widget to add to the field on the views.")

required_on_views = fields.Boolean(
Expand All @@ -102,6 +97,10 @@ class AttributeAttribute(models.Model):
column1="attribute_id",
column2="attribute_set_id",
)
allowed_attribute_set_ids = fields.Many2many(
comodel_name="attribute.set",
compute="_compute_allowed_attribute_set_ids",
)

attribute_group_id = fields.Many2one(
"attribute.group", "Attribute Group", required=True, ondelete="cascade"
Expand Down Expand Up @@ -135,7 +134,7 @@ def _build_attribute_field(self, attribute_egroup):
Conditional invisibility based on its attribute sets.
"""
self.ensure_one()
kwargs = {"name": "%s" % self.name}
kwargs = {"name": f"{self.name}"}
kwargs["attrs"] = str(self._get_attrs())
if self.widget:
kwargs["widget"] = self.widget

Check warning on line 140 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L140

Added line #L140 was not covered by tests
Expand All @@ -159,7 +158,7 @@ def _build_attribute_field(self, attribute_egroup):
else:
# Display only options linked to an existing object
ids = [op.value_ref.id for op in self.option_ids if op.value_ref]
kwargs["domain"] = "[('id', 'in', %s)]" % ids
kwargs["domain"] = f"[('id', 'in', {ids})]"

Check warning on line 161 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L160-L161

Added lines #L160 - L161 were not covered by tests
# Add color options if the attribute's Relational Model
# has a color field
relation_model_obj = self.env[self.relation_model_id.model]

Check warning on line 164 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L164

Added line #L164 was not covered by tests
Expand All @@ -168,8 +167,8 @@ def _build_attribute_field(self, attribute_egroup):
elif self.nature == "custom":
# Define field's domain and context with attribute's id to go along with
# Attribute Options search and creation
kwargs["domain"] = "[('attribute_id', '=', %s)]" % (self.id)
kwargs["context"] = "{'default_attribute_id': %s}" % (self.id)
kwargs["domain"] = f"[('attribute_id', '=', {self.id})]"
kwargs["context"] = f"{{'default_attribute_id': {self.id}}}"
elif self.nature != "custom":
kwargs["context"] = self._get_native_field_context()

Expand Down Expand Up @@ -229,6 +228,18 @@ def _build_attribute_eview(self):

return attribute_eview

def _get_attribute_set_allowed_model(self):
return self.model_id

Check warning on line 232 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L232

Added line #L232 was not covered by tests

@api.depends("model_id")
def _compute_allowed_attribute_set_ids(self):
AttributeSet = self.env["attribute.set"]

Check warning on line 236 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L236

Added line #L236 was not covered by tests
for record in self:
allowed_models = record._get_attribute_set_allowed_model()
record.allowed_attribute_set_ids = AttributeSet.search(

Check warning on line 239 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L238-L239

Added lines #L238 - L239 were not covered by tests
[("model_id", "in", allowed_models.ids)]
)

@api.onchange("model_id")
def onchange_model_id(self):
return {"domain": {"field_id": [("model_id", "=", self.model_id.id)]}}

Check warning on line 245 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L245

Added line #L245 was not covered by tests
Expand All @@ -242,7 +253,7 @@ def onchange_field_description(self):
def onchange_name(self):
name = self.name

Check warning on line 254 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L254

Added line #L254 was not covered by tests
if not name.startswith("x_"):
self.name = "x_%s" % name
self.name = f"x_{name}"

Check warning on line 256 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L256

Added line #L256 was not covered by tests

@api.onchange("attribute_type")
def onchange_attribute_type(self):
Expand Down Expand Up @@ -274,18 +285,22 @@ def _onchange_domain(self):

def button_add_options(self):
self.ensure_one()

Check warning on line 287 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L287

Added line #L287 was not covered by tests
values = self.env[self.relation_model_id.model].search(safe_eval(self.domain))
options = []
for value in values:
options.append(
Command.create(
{
"name": value.display_name,
"value_ref": f"{value._name},{value.id}",
}
)
)
self.option_ids = options
# Before adding another option delete the ones which are linked
# to a deleted object
for option in self.option_ids:
if not option.value_ref:
option.unlink()

Check warning on line 292 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L292

Added line #L292 was not covered by tests
# Then open the Options Wizard which will display an 'opt_ids' m2m field related
# to the 'relation_model_id' model
return {

Check warning on line 295 in attribute_set/models/attribute_attribute.py

View check run for this annotation

Codecov / codecov/patch

attribute_set/models/attribute_attribute.py#L295

Added line #L295 was not covered by tests
"context": dict(self.env.context, attribute_id=self.id),
"name": _("Options Wizard"),
"view_type": "form",
"view_mode": "form",
"res_model": "attribute.option.wizard",
"type": "ir.actions.act_window",
"target": "new",
}

@api.model_create_multi
def create(self, vals_list):
Expand Down Expand Up @@ -325,8 +340,9 @@ def create(self, vals_list):
elif attr_type == "multiselect":
vals["ttype"] = "many2many"
vals["relation"] = relation
# Specify the relation_table's name in case of m2m not serialized to
# avoid creating the same default relation_table name for any attribute
# Specify the relation_table's name in case of m2m not serialized
# to avoid creating the same default
# relation_table name for any attribute
# linked to the same attribute.option or relation_model_id's model.
if not vals.get("serialized"):
att_model_id = self.env["ir.model"].browse(vals["model_id"])
Expand Down
1 change: 0 additions & 1 deletion attribute_set/models/attribute_option.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ def _selection_model_list(self):
value_ref = fields.Reference(
selection="_selection_model_list",
string="Reference",
groups="base.group_erp_manager",
)

attribute_id = fields.Many2one(
Expand Down
11 changes: 7 additions & 4 deletions attribute_set/tests/test_build_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ def setUpClass(cls):
cls.demo = cls.env.ref("base.user_demo")

# This user will have access to
cls.attribute_manager_user = cls.env["res.users"].create(
cls.attribute_manager_user = cls.env.ref("base.user_admin")
cls.attribute_manager_user.write(
{
"name": "Attribute Manager",
"login": "attribute_manager",
Expand Down Expand Up @@ -344,18 +345,20 @@ def test_unlink_native_attribute(self):
)

# TEST form views rendering
@users("demo")
@users("attribute_manager")
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)
self.partner.with_user(self.attribute_manager_user).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)
partner = partner_form.save().with_user(self.attribute_manager_user)
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
Expand Down
6 changes: 4 additions & 2 deletions attribute_set/tests/test_custom_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ def _create_attribute(self, vals):
{
"nature": "custom",
"model_id": self.model_id,
"field_description": "Attribute %s" % vals["attribute_type"],
"name": "x_%s" % vals["attribute_type"],
"field_description": "Attribute {key}".format(
key=vals["attribute_type"]
),
"name": "x_{key}".format(key=vals["attribute_type"]),
"attribute_group_id": self.group.id,
}
)
Expand Down
39 changes: 21 additions & 18 deletions attribute_set/views/attribute_attribute_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@
nolabel="1"
required="1"
/>
<group col="4" colspan="4" invisible="context.get('default_model_id')">
<group
col="2"
class="col-6"
invisible="context.get('default_model_id')"
>
<!-- The field created by this Attribute will be related to the model given
by 'default_model_id' through the menu's context that led to this view -->
<field name="model_id" />
</group>
<separator invisible="nature == 'native'" />
<group col="4" colspan="4" invisible="nature == 'native'">
<group col="2" class="col-6" invisible="nature == 'native'">
<field name="field_description" required="nature == 'custom'" />
<field name="help" />
<field
name="name"
readonly="create_date"
Expand All @@ -31,12 +36,12 @@
<field name="size" invisible="attribute_type != 'char'" />
<field
name="translate"
invisible="attribute_type not in ('char', 'text')"
invisible="attribute_type not in ['char', 'text']"
/>
<field name="serialized" readonly="create_date" />
</group>
<separator />
<group col="4" colspan="4">
<group col="2" class="col-6">
<field
name="field_id"
string="Related native field"
Expand All @@ -51,10 +56,11 @@
domain="[('model_id', '=', model_id)]"
/>
<field name="sequence" />
<field name="allowed_attribute_set_ids" invisible="1" />
<field
name="attribute_set_ids"
widget="many2many_tags"
domain="[('model_id', '=', model_id)]"
domain="[('id', 'in', allowed_attribute_set_ids)]"
options="{'no_create': 1}"
invisible="context.get('from_attribute_set')"
/>
Expand All @@ -63,7 +69,7 @@
<field name="create_date" invisible="1" />
</group>
<group
invisible="attribute_type not in ('select', 'multiselect') or nature == 'native'"
invisible="attribute_type not in ['select', 'multiselect'] or nature == 'native'"
col="4"
colspan="4"
>
Expand All @@ -74,25 +80,21 @@
>
<group colspan="2">
<field name="relation_model_id" readonly="create_date" />
<field name="relation_model_name" invisible="1" />
<field name="domain" invisible="relation_model_id" />
</group>
<group invisible="not relation_model_id" colspan="4">
<field
name="domain"
widget="domain"
options="{'model': 'relation_model_name', 'in_dialog': true}"
invisible="not relation_model_id"
/>
<newline />
<group colspan="2" invisible="relation_model_id">
<button
name="button_add_options"
invisible="domain not in [False, '[]']"
type="object"
string="Load Attribute Options"
class="oe_highlight"
colspan="2"
colspan="1"
/>
</group>
</group>
<group col="4" colspan="4">
<group colspan="4">
<field name="option_ids" string="Attribute Options" colspan="4">
<tree editable="top">
<field
Expand All @@ -103,7 +105,8 @@
<field name="sequence" column_invisible="True" />
<field
name="name"
invisible="relation_model_id and not value_ref"
column_invisible="relation_model_id and value_ref in [False, '']"
groups="base.group_erp_manager"
/>
</tree>
</field>
Expand Down Expand Up @@ -154,7 +157,7 @@
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="view_attribute_attribute_search" />
<field name="context">
{"search_default_attribute_group_id": id, "active_attribute_sort":
{"search_default_attribute_group_id": active_id, "active_attribute_sort":
True}
</field>
<field name="help" />
Expand Down

0 comments on commit 7884d75

Please sign in to comment.