Skip to content

Commit

Permalink
[FIX] web_field_required_invisible_manager: reset on change
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyasProgrammer committed Jul 31, 2023
1 parent 5f98b52 commit b6ecd52
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 34 deletions.
1 change: 1 addition & 0 deletions web_field_required_invisible_manager/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import models
from .hooks import uninstall_hook
3 changes: 2 additions & 1 deletion web_field_required_invisible_manager/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html )
{
"name": "Web Field Required Invisible Manager",
"category": "Web",
Expand All @@ -12,4 +12,5 @@
"security/ir.model.access.csv",
"views/views.xml",
],
"uninstall_hook": "uninstall_hook",
}
12 changes: 12 additions & 0 deletions web_field_required_invisible_manager/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
from odoo import SUPERUSER_ID, api


def uninstall_hook(cr, registry):
"""Delete all created restrictions fields"""
env = api.Environment(cr, SUPERUSER_ID, {})
recs = env["custom.field.restriction"].search([])

Check warning on line 8 in web_field_required_invisible_manager/hooks.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/hooks.py#L7-L8

Added lines #L7 - L8 were not covered by tests
for rec in recs:
rec.visibility_field_id.unlink()
rec.readonly_field_id.unlink()
rec.required_field_id.unlink()

Check warning on line 12 in web_field_required_invisible_manager/hooks.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/hooks.py#L10-L12

Added lines #L10 - L12 were not covered by tests
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ class CustomFieldRestriction(models.Model):
string="invisible_model_id",
index=True,
)
readonly_model_id = fields.Many2one(
"ir.model",
ondelete="cascade",
string="readonly_model_id",
index=True,
)

model_name = fields.Char(
compute="_compute_model_name",
Expand All @@ -41,18 +47,96 @@ class CustomFieldRestriction(models.Model):
)
condition_domain = fields.Char()
group_ids = fields.Many2many("res.groups", required=True)
required = fields.Boolean()
default_required = fields.Boolean(related="field_id.required")
required = fields.Boolean()
field_invisible = fields.Boolean()
field_readonly = fields.Boolean()
# fields used in form attrs:
visibility_field_id = fields.Many2one("ir.model.fields")
readonly_field_id = fields.Many2one("ir.model.fields")
required_field_id = fields.Many2one("ir.model.fields")

@api.onchange("field_id")
def onchange_field_id(self):
self.required = self.field_id.required
self.field_invisible = False
self.field_readonly = self.field_id.readonly

@api.depends("required_model_id", "invisible_model_id")
@api.depends("required_model_id", "invisible_model_id", "readonly_model_id")
def _compute_model_name(self):
for rec in self:
if rec.required_model_id:
rec.model_name = rec.required_model_id.model
elif rec.invisible_model_id:
rec.model_name = rec.invisible_model_id.model
elif rec.readonly_model_id:
rec.model_name = rec.readonly_model_id.model

Check warning on line 73 in web_field_required_invisible_manager/models/custom_field_restriction.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/custom_field_restriction.py#L73

Added line #L73 was not covered by tests

@api.model_create_multi
def create(self, vals_list):
res = super(CustomFieldRestriction, self).create(vals_list)
for rec in res:
if rec.invisible_model_id and rec.field_invisible:
rec.create_visibility_field()
if rec.readonly_model_id and rec.field_readonly:
rec.create_readonly_field()

Check warning on line 82 in web_field_required_invisible_manager/models/custom_field_restriction.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/custom_field_restriction.py#L82

Added line #L82 was not covered by tests
if rec.required_model_id and rec.required:
rec.create_required_field()
return res

def create_required_field(self):
field_name = "x_computed_%s_required" % self.field_id.name
field_id = self.env["ir.model.fields"].search([("name", "=", field_name)])
if not field_id:
field_id = self.env["ir.model.fields"].create(
{
"name": field_name,
"model_id": self.required_model_id.id,
"state": "manual",
"field_description": "%s required field" % self.field_id.name,
"store": False,
"ttype": "boolean",
"compute": "for r in self: r._compute_restrictions_fields()",
}
)
self.required_field_id = field_id

def create_readonly_field(self):
field_name = "x_computed_%s_readonly" % self.field_id.name
field_id = self.env["ir.model.fields"].search([("name", "=", field_name)])

Check warning on line 106 in web_field_required_invisible_manager/models/custom_field_restriction.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/custom_field_restriction.py#L105-L106

Added lines #L105 - L106 were not covered by tests
if not field_id:
field_id = self.env["ir.model.fields"].create(

Check warning on line 108 in web_field_required_invisible_manager/models/custom_field_restriction.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/custom_field_restriction.py#L108

Added line #L108 was not covered by tests
{
"name": field_name,
"model_id": self.readonly_model_id.id,
"state": "manual",
"field_description": "%s readonly field" % self.field_id.name,
"store": False,
"ttype": "boolean",
"compute": "for r in self: r._compute_restrictions_fields()",
}
)
self.readonly_field_id = field_id

Check warning on line 119 in web_field_required_invisible_manager/models/custom_field_restriction.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/custom_field_restriction.py#L119

Added line #L119 was not covered by tests

def create_visibility_field(self):
field_name = "x_computed_%s_visibility" % self.field_id.name
field_id = self.env["ir.model.fields"].search([("name", "=", field_name)])
if not field_id:
field_id = self.env["ir.model.fields"].create(
{
"name": field_name,
"model_id": self.invisible_model_id.id,
"state": "manual",
"field_description": "%s visibility field" % self.field_id.name,
"store": False,
"ttype": "boolean",
"compute": "for r in self: r._compute_restrictions_fields()",
}
)
self.visibility_field_id = field_id

def unlink(self):
self.visibility_field_id.unlink()
self.readonly_field_id.unlink()
self.required_field_id.unlink()
return super(CustomFieldRestriction, self).unlink()

Check warning on line 142 in web_field_required_invisible_manager/models/custom_field_restriction.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/custom_field_restriction.py#L139-L142

Added lines #L139 - L142 were not covered by tests
104 changes: 76 additions & 28 deletions web_field_required_invisible_manager/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ class IrModel(models.Model):
"custom.field.restriction",
"invisible_model_id",
)
custom_readonly_restriction_ids = fields.One2many(
"custom.field.restriction",
"readonly_model_id",
)


class Base(models.AbstractModel):
Expand All @@ -28,52 +32,96 @@ class Base(models.AbstractModel):
def fields_view_get(
self, view_id=None, view_type=False, toolbar=False, submenu=False
):
res = super().fields_view_get(
arch = super().fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
if view_type not in ["form", "tree", "kanban"]:
return res
if view_type not in ["form", "tree"]:
return arch
# TODO speed up somehow
restrictions = self.env["custom.field.restriction"].search(
[
"|",
("model_name", "=", self._name),
("group_ids", "in", self.env.user.groups_id.ids),
]
)
if not restrictions:
return res
doc = etree.XML(res["arch"])
return arch
else:
return self.create_restrictions_fields(restrictions, view_type, arch)
return arch

Check warning on line 51 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L51

Added line #L51 was not covered by tests

def create_restrictions_fields(self, restrictions, view_type, arch):
doc = etree.XML(arch["arch"])
for node in doc.xpath("//field"):
name = node.attrib.get("name")
restrictions_filtered = restrictions.filtered(
lambda x: x.field_id.name == name
)
if not restrictions_filtered:
continue
for r in restrictions_filtered:
if (
view_type == "form"
and self.env.context.get("params")
and self.env.context["params"].get("id")
):
rec_id = self.env[r.model_name].browse(
self.env.context["params"]["id"]
field_node_str = "<field name='%s' invisible='1'/>"
field_node_mod = bytes(

Check warning on line 64 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L63-L64

Added lines #L63 - L64 were not covered by tests
'{"invisible": true,"column_invisible": true}', "utf-8"
)
if view_type == "tree":
field_node_str = (

Check warning on line 68 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L68

Added line #L68 was not covered by tests
"<field name='%s' column_invisible='1' optional='hide'/>"
)
if r.field_invisible and r.invisible_model_id:
modifiers = simplejson.loads(node.get("modifiers"))
visibility_field_name = "x_computed_%s_visibility" % name
modifiers["invisible"] = (

Check warning on line 74 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L72-L74

Added lines #L72 - L74 were not covered by tests
"[('%s', '=', True)]" % visibility_field_name
)
if r.condition_domain:
filtered_rec_id = rec_id.filtered_domain(
safe_eval(r.condition_domain)
)
if not filtered_rec_id:
continue
if r.required:
node.set("required", "1")
node.set("modifiers", simplejson.dumps(modifiers))
new_node = etree.fromstring(field_node_str % visibility_field_name)
new_node.set("invisible", "1")
new_node.set("modifiers", field_node_mod)
node.getparent().append(new_node)

Check warning on line 81 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L77-L81

Added lines #L77 - L81 were not covered by tests
if r.required_field_id and r.required_model_id:
modifiers = simplejson.loads(node.get("modifiers"))
modifiers["required"] = True
required_field_name = "x_computed_%s_required" % name
modifiers["required"] = "[('%s', '=', True)]" % required_field_name

Check warning on line 85 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L84-L85

Added lines #L84 - L85 were not covered by tests
node.set("modifiers", simplejson.dumps(modifiers))
res["arch"] = etree.tostring(doc)
if r.field_invisible:
node.set("invisible", "1")
new_node = etree.fromstring(field_node_str % required_field_name)
new_node.set("invisible", "1")
new_node.set("modifiers", field_node_mod)
node.getparent().append(new_node)

Check warning on line 90 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L87-L90

Added lines #L87 - L90 were not covered by tests
if r.readonly_field_id and r.readonly_model_id:
modifiers = simplejson.loads(node.get("modifiers"))
modifiers["invisible"] = True
readonly_field_name = "x_computed_%s_readonly" % name
modifiers["readonly"] = "[('%s', '=', True)]" % readonly_field_name

Check warning on line 94 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L93-L94

Added lines #L93 - L94 were not covered by tests
node.set("modifiers", simplejson.dumps(modifiers))
res["arch"] = etree.tostring(doc)
return res
new_node = etree.fromstring(field_node_str % readonly_field_name)
new_node.set("invisible", "1")
new_node.set("modifiers", field_node_mod)
node.getparent().append(new_node)
arch["arch"] = etree.tostring(doc)

Check warning on line 100 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L96-L100

Added lines #L96 - L100 were not covered by tests
return arch

def _compute_restrictions_fields(self):
"""Common compute method for all restrictions types"""
for so in self:
restrictions = self.env["custom.field.restriction"].search(

Check warning on line 106 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L106

Added line #L106 was not covered by tests
[("model_name", "=", self._name)]
)
if not restrictions:
return

Check warning on line 110 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L110

Added line #L110 was not covered by tests
for r in restrictions:
if r.visibility_field_id:
field_name = r.visibility_field_id.name
so[field_name] = False

Check warning on line 114 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L113-L114

Added lines #L113 - L114 were not covered by tests
if r.required_field_id:
field_name = r.required_field_id.name
so[field_name] = False

Check warning on line 117 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L116-L117

Added lines #L116 - L117 were not covered by tests
if r.readonly_field_id:
field_name = r.readonly_field_id.name
so[field_name] = False

Check warning on line 120 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L119-L120

Added lines #L119 - L120 were not covered by tests
if r.condition_domain:
filtered_rec_id = so.filtered_domain(safe_eval(r.condition_domain))

Check warning on line 122 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L122

Added line #L122 was not covered by tests
if filtered_rec_id and r.group_ids & self.env.user.groups_id:
so[field_name] = True

Check warning on line 124 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L124

Added line #L124 was not covered by tests
elif r.group_ids:
if r.group_ids & self.env.user.groups_id:
so[field_name] = True

Check warning on line 127 in web_field_required_invisible_manager/models/models.py

View check run for this annotation

Codecov / codecov/patch

web_field_required_invisible_manager/models/models.py#L127

Added line #L127 was not covered by tests
39 changes: 36 additions & 3 deletions web_field_required_invisible_manager/views/views.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>

<record id="view_model_form_inherit" model="ir.ui.view">
Expand Down Expand Up @@ -33,6 +32,7 @@
/>
<field name="condition_domain" />
<field name="group_ids" widget="many2many_tags" />
<field name="required_field_id" optional="hide" />
</tree>
</field>
</page>
Expand All @@ -59,6 +59,35 @@
<field name="field_invisible" />
<field name="condition_domain" />
<field name="group_ids" widget="many2many_tags" />
<field name="visibility_field_id" optional="hide" />
</tree>
</field>
</page>
<page
string="Custom Readonly Fields"
name="custom_readonly_restriction"
>
<field
name="custom_readonly_restriction_ids"
nolabel="1"
context="{'default_readonly_model_id': model}"
>
<tree string="Fields">
<field name="required_model_id" invisible="1" />
<field name="invisible_model_id" invisible="1" />
<field name="readonly_model_id" invisible="1" />
<field name="model_name" invisible="1" />
<field
name="field_id"
context="{'search_by_technical_name': True, 'display_technical_name': True}"
domain="[('model_id.model', '=', model_name)]"
options="{'create': False, 'create_edit': False}"
/>
<field name="default_required" invisible="1" />
<field name="field_readonly" />
<field name="condition_domain" />
<field name="group_ids" widget="many2many_tags" />
<field name="readonly_field_id" optional="hide" />
</tree>
</field>
</page>
Expand All @@ -76,6 +105,7 @@

<field name="required_model_id" invisible="1" />
<field name="invisible_model_id" invisible="1" />
<field name="readonly_model_id" invisible="1" />
<field name="model_name" invisible="1" />
<field
name="field_id"
Expand All @@ -91,8 +121,11 @@
/>
<field
name="field_invisible"
attrs="{'readonly':[('default_required', '=', True)],
'invisible':[('invisible_model_id', '=', False)]}"
attrs="{'invisible':[('invisible_model_id', '=', False)]}"
/>
<field
name="field_readonly"
attrs="{'invisible':[('readonly_model_id', '=', False)]}"
/>
<field
name="condition_domain"
Expand Down

0 comments on commit b6ecd52

Please sign in to comment.