mirror of https://github.com/OCA/web.git
[FIX] web_field_required_invisible_manager: reset on change
parent
5f98b52439
commit
245f1f1d88
|
@ -1,6 +1,6 @@
|
||||||
# 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",
|
"name": "Web Field Required Invisible Readonly Managerr",
|
||||||
"category": "Web",
|
"category": "Web",
|
||||||
"version": "14.0.1.0.0",
|
"version": "14.0.1.0.0",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2020 ooops404
|
# Copyright 2023 ooops404
|
||||||
# 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)
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
@ -13,13 +13,11 @@ class CustomFieldRestriction(models.Model):
|
||||||
required=True,
|
required=True,
|
||||||
string="Field",
|
string="Field",
|
||||||
)
|
)
|
||||||
|
|
||||||
field_name = fields.Char(
|
field_name = fields.Char(
|
||||||
related="field_id.name",
|
related="field_id.name",
|
||||||
store=True,
|
store=True,
|
||||||
string="Field Name",
|
string="Field Name",
|
||||||
)
|
)
|
||||||
|
|
||||||
required_model_id = fields.Many2one(
|
required_model_id = fields.Many2one(
|
||||||
"ir.model",
|
"ir.model",
|
||||||
ondelete="cascade",
|
ondelete="cascade",
|
||||||
|
@ -32,7 +30,12 @@ class CustomFieldRestriction(models.Model):
|
||||||
string="invisible_model_id",
|
string="invisible_model_id",
|
||||||
index=True,
|
index=True,
|
||||||
)
|
)
|
||||||
|
readonly_model_id = fields.Many2one(
|
||||||
|
"ir.model",
|
||||||
|
ondelete="cascade",
|
||||||
|
string="readonly_model_id",
|
||||||
|
index=True,
|
||||||
|
)
|
||||||
model_name = fields.Char(
|
model_name = fields.Char(
|
||||||
compute="_compute_model_name",
|
compute="_compute_model_name",
|
||||||
store=True,
|
store=True,
|
||||||
|
@ -41,18 +44,77 @@ class CustomFieldRestriction(models.Model):
|
||||||
)
|
)
|
||||||
condition_domain = fields.Char()
|
condition_domain = fields.Char()
|
||||||
group_ids = fields.Many2many("res.groups", required=True)
|
group_ids = fields.Many2many("res.groups", required=True)
|
||||||
required = fields.Boolean()
|
|
||||||
default_required = fields.Boolean(related="field_id.required")
|
default_required = fields.Boolean(related="field_id.required")
|
||||||
|
required = fields.Boolean()
|
||||||
field_invisible = fields.Boolean()
|
field_invisible = fields.Boolean()
|
||||||
|
field_readonly = fields.Boolean()
|
||||||
|
# generated technical fields used in form attrs:
|
||||||
|
visibility_field_id = fields.Many2one("ir.model.fields", ondelete="cascade")
|
||||||
|
readonly_field_id = fields.Many2one("ir.model.fields", ondelete="cascade")
|
||||||
|
required_field_id = fields.Many2one("ir.model.fields", ondelete="cascade")
|
||||||
|
|
||||||
@api.onchange("field_id")
|
@api.onchange("field_id")
|
||||||
def onchange_field_id(self):
|
def onchange_field_id(self):
|
||||||
self.required = self.field_id.required
|
self.update(
|
||||||
|
{
|
||||||
|
"required": self.field_id.required,
|
||||||
|
"field_invisible": False,
|
||||||
|
"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):
|
def _compute_model_name(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.required_model_id:
|
if rec.required_model_id:
|
||||||
rec.model_name = rec.required_model_id.model
|
rec.model_name = rec.required_model_id.model
|
||||||
elif rec.invisible_model_id:
|
elif rec.invisible_model_id:
|
||||||
rec.model_name = rec.invisible_model_id.model
|
rec.model_name = rec.invisible_model_id.model
|
||||||
|
elif rec.readonly_model_id:
|
||||||
|
rec.model_name = rec.readonly_model_id.model
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
rec = super().create(vals)
|
||||||
|
if rec.invisible_model_id and rec.field_invisible:
|
||||||
|
rec.create_restriction_field("visibility")
|
||||||
|
elif rec.readonly_model_id and rec.field_readonly:
|
||||||
|
rec.create_restriction_field("readonly")
|
||||||
|
elif rec.required_model_id and rec.required:
|
||||||
|
rec.create_restriction_field("required")
|
||||||
|
return rec
|
||||||
|
|
||||||
|
def create_restriction_field(self, f_type):
|
||||||
|
field_name = self.get_field_name(f_type)
|
||||||
|
field_id = self.env["ir.model.fields"].search([("name", "=", field_name)])
|
||||||
|
if f_type == "required":
|
||||||
|
rec_model_id = self.required_model_id.id
|
||||||
|
rec_field_name = "required_field_id"
|
||||||
|
elif f_type == "readonly":
|
||||||
|
rec_model_id = self.readonly_model_id.id
|
||||||
|
rec_field_name = "readonly_field_id"
|
||||||
|
elif f_type == "visibility":
|
||||||
|
rec_model_id = self.invisible_model_id.id
|
||||||
|
rec_field_name = "visibility_field_id"
|
||||||
|
if not field_id:
|
||||||
|
field_id = self.env["ir.model.fields"].create(
|
||||||
|
{
|
||||||
|
"name": field_name,
|
||||||
|
"model_id": rec_model_id,
|
||||||
|
"state": "manual",
|
||||||
|
"field_description": "%s %s field" % (self.field_id.name, f_type),
|
||||||
|
"store": False,
|
||||||
|
"ttype": "boolean",
|
||||||
|
"compute": "for r in self: r._compute_restrictions_fields()",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self[rec_field_name] = field_id
|
||||||
|
|
||||||
|
def get_field_name(self, f_type):
|
||||||
|
# e.g. x_computed_res_partner_name_readonly
|
||||||
|
res = "x_computed_%s_%s_%s" % (
|
||||||
|
self.field_id.model.replace(".", "_"),
|
||||||
|
self.field_id.name,
|
||||||
|
f_type,
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2020 ooops404
|
# Copyright 2023 ooops404
|
||||||
# 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)
|
||||||
import json as simplejson
|
import json as simplejson
|
||||||
|
|
||||||
|
@ -19,6 +19,10 @@ class IrModel(models.Model):
|
||||||
"custom.field.restriction",
|
"custom.field.restriction",
|
||||||
"invisible_model_id",
|
"invisible_model_id",
|
||||||
)
|
)
|
||||||
|
custom_readonly_restriction_ids = fields.One2many(
|
||||||
|
"custom.field.restriction",
|
||||||
|
"readonly_model_id",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Base(models.AbstractModel):
|
class Base(models.AbstractModel):
|
||||||
|
@ -28,52 +32,98 @@ class Base(models.AbstractModel):
|
||||||
def fields_view_get(
|
def fields_view_get(
|
||||||
self, view_id=None, view_type=False, toolbar=False, submenu=False
|
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
|
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
|
||||||
)
|
)
|
||||||
if view_type not in ["form", "tree", "kanban"]:
|
if view_type not in ["form", "tree"]:
|
||||||
return res
|
return arch
|
||||||
# TODO speed up somehow
|
# TODO speed up somehow
|
||||||
restrictions = self.env["custom.field.restriction"].search(
|
restrictions = self.env["custom.field.restriction"].search(
|
||||||
[
|
[
|
||||||
"|",
|
|
||||||
("model_name", "=", self._name),
|
("model_name", "=", self._name),
|
||||||
("group_ids", "in", self.env.user.groups_id.ids),
|
("group_ids", "in", self.env.user.groups_id.ids),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
if not restrictions:
|
if not restrictions:
|
||||||
return res
|
return arch
|
||||||
doc = etree.XML(res["arch"])
|
else:
|
||||||
|
return self.create_restrictions_fields(restrictions, view_type, arch)
|
||||||
|
return arch
|
||||||
|
|
||||||
|
def create_restrictions_fields(self, restrictions, view_type, arch):
|
||||||
|
doc = etree.XML(arch["arch"])
|
||||||
for node in doc.xpath("//field"):
|
for node in doc.xpath("//field"):
|
||||||
name = node.attrib.get("name")
|
name = node.attrib.get("name")
|
||||||
restrictions_filtered = restrictions.filtered(
|
restrictions_filtered = restrictions.filtered(
|
||||||
lambda x: x.field_id.name == name
|
lambda x: x.field_id.name == name
|
||||||
)
|
)
|
||||||
|
if not restrictions_filtered:
|
||||||
|
continue
|
||||||
for r in restrictions_filtered:
|
for r in restrictions_filtered:
|
||||||
if (
|
field_node_str = "<field name='%s' invisible='1'/>"
|
||||||
view_type == "form"
|
field_node_mod = bytes(
|
||||||
and self.env.context.get("params")
|
'{"invisible": true,"column_invisible": true}', "utf-8"
|
||||||
and self.env.context["params"].get("id")
|
|
||||||
):
|
|
||||||
rec_id = self.env[r.model_name].browse(
|
|
||||||
self.env.context["params"]["id"]
|
|
||||||
)
|
)
|
||||||
|
if view_type == "tree":
|
||||||
|
field_node_str = (
|
||||||
|
"<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 = r.get_field_name("visibility")
|
||||||
|
modifiers["invisible"] = (
|
||||||
|
"[('%s', '=', True)]" % visibility_field_name
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
if r.required_field_id and r.required_model_id:
|
||||||
|
modifiers = simplejson.loads(node.get("modifiers"))
|
||||||
|
required_field_name = r.get_field_name("required")
|
||||||
|
modifiers["required"] = "[('%s', '=', True)]" % required_field_name
|
||||||
|
node.set("modifiers", simplejson.dumps(modifiers))
|
||||||
|
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)
|
||||||
|
if r.readonly_field_id and r.readonly_model_id:
|
||||||
|
modifiers = simplejson.loads(node.get("modifiers"))
|
||||||
|
readonly_field_name = r.get_field_name("readonly")
|
||||||
|
modifiers["readonly"] = "[('%s', '=', True)]" % readonly_field_name
|
||||||
|
node.set("modifiers", simplejson.dumps(modifiers))
|
||||||
|
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)
|
||||||
|
return arch
|
||||||
|
|
||||||
|
def _compute_restrictions_fields(self):
|
||||||
|
"""Common compute method for all restrictions types"""
|
||||||
|
for record in self:
|
||||||
|
restrictions = self.env["custom.field.restriction"].search(
|
||||||
|
[("model_name", "=", self._name)]
|
||||||
|
)
|
||||||
|
if not restrictions:
|
||||||
|
return
|
||||||
|
for r in restrictions:
|
||||||
|
if r.visibility_field_id:
|
||||||
|
field_name = r.visibility_field_id.name
|
||||||
|
record[field_name] = False
|
||||||
|
if r.required_field_id:
|
||||||
|
field_name = r.required_field_id.name
|
||||||
|
record[field_name] = False
|
||||||
|
if r.readonly_field_id:
|
||||||
|
field_name = r.readonly_field_id.name
|
||||||
|
record[field_name] = False
|
||||||
if r.condition_domain:
|
if r.condition_domain:
|
||||||
filtered_rec_id = rec_id.filtered_domain(
|
filtered_rec_id = record.filtered_domain(
|
||||||
safe_eval(r.condition_domain)
|
safe_eval(r.condition_domain)
|
||||||
)
|
)
|
||||||
if not filtered_rec_id:
|
if filtered_rec_id and r.group_ids & self.env.user.groups_id:
|
||||||
continue
|
record[field_name] = True
|
||||||
if r.required:
|
elif r.group_ids:
|
||||||
node.set("required", "1")
|
if r.group_ids & self.env.user.groups_id:
|
||||||
modifiers = simplejson.loads(node.get("modifiers"))
|
record[field_name] = True
|
||||||
modifiers["required"] = True
|
|
||||||
node.set("modifiers", simplejson.dumps(modifiers))
|
|
||||||
res["arch"] = etree.tostring(doc)
|
|
||||||
if r.field_invisible:
|
|
||||||
node.set("invisible", "1")
|
|
||||||
modifiers = simplejson.loads(node.get("modifiers"))
|
|
||||||
modifiers["invisible"] = True
|
|
||||||
node.set("modifiers", simplejson.dumps(modifiers))
|
|
||||||
res["arch"] = etree.tostring(doc)
|
|
||||||
return res
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
This module allows to set a field required or invisible for users belonging to a specific group.
|
This module allows to set a field required, invisible or readonly for users belonging to a specific group.
|
||||||
|
|
||||||
The field can be required or invisible in any case, or according to specific conditions.
|
The field can be required, invisible or readonly in any case, or according to specific conditions.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Go to Settings > Technical > Models
|
Go to Settings > Technical > Models
|
||||||
|
|
||||||
Select a model > in tab "Custom required fields" or "Custom invisible fields" add a line
|
Select a model > in tab "Custom required fields", "Custom invisible fields" or "Custom readonly fields" add a line
|
||||||
|
|
||||||
Select a field, add one or more group and enable flag "Required" or "Invisible"
|
Select a field, add one or more group and enable flag "Required", "Invisible" or "Readonly"
|
||||||
|
|
||||||
If needed, set a condition for which the field should be required or invisible for users of those groups.
|
If needed, set a condition for which the field should be required, invisible or readonly for users of those groups.
|
||||||
|
|
|
@ -12,11 +12,11 @@ class TestFieldRequiredIvisibleManager(common.SavepointCase):
|
||||||
[("model", "=", "res.partner"), ("name", "=", "name")]
|
[("model", "=", "res.partner"), ("name", "=", "name")]
|
||||||
)
|
)
|
||||||
cls.partner_id_field_id = cls.env["ir.model.fields"].search(
|
cls.partner_id_field_id = cls.env["ir.model.fields"].search(
|
||||||
[("model", "=", "res.partner"), ("name", "=", "id")]
|
[("model", "=", "res.partner"), ("name", "=", "name")]
|
||||||
)
|
)
|
||||||
cls.partner_model_id = cls.partner_name_field_id.model_id
|
cls.partner_model_id = cls.partner_name_field_id.model_id
|
||||||
cls.partner_title_model_id = cls.env["ir.model"].search(
|
cls.partner_title_model_id = cls.env["ir.model"].search(
|
||||||
[("name", "=", "res.partner.title")]
|
[("model", "=", "res.partner.title")]
|
||||||
)
|
)
|
||||||
cls.partner_title_name_field_id = cls.env["ir.model.fields"].search(
|
cls.partner_title_name_field_id = cls.env["ir.model.fields"].search(
|
||||||
[("model", "=", "res.partner.title"), ("name", "=", "name")]
|
[("model", "=", "res.partner.title"), ("name", "=", "name")]
|
||||||
|
@ -50,10 +50,29 @@ class TestFieldRequiredIvisibleManager(common.SavepointCase):
|
||||||
"condition_domain": "[('id', '>', 0)]",
|
"condition_domain": "[('id', '>', 0)]",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
cls.readonly_rec_id = cls.env["custom.field.restriction"].create(
|
||||||
|
{
|
||||||
|
"readonly_model_id": cls.partner_model_id.id,
|
||||||
|
"field_id": cls.partner_id_field_id.id,
|
||||||
|
"group_ids": [(6, 0, cls.env.ref("base.group_user").ids)],
|
||||||
|
"field_readonly": True,
|
||||||
|
"condition_domain": "[('id', '>', 0)]",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cls.res_partner_title_madam = cls.env.ref("base.res_partner_title_madam")
|
||||||
cls.deco_addict = cls.env.ref("base.res_partner_2")
|
cls.deco_addict = cls.env.ref("base.res_partner_2")
|
||||||
cls.partner_view = cls.env.ref("base.view_partner_simple_form")
|
cls.partner_form_view = cls.env.ref("base.view_partner_simple_form")
|
||||||
|
cls.partner_tree_view = cls.env.ref("base.view_partner_tree")
|
||||||
|
cls.view_partner_title_form = cls.env.ref("base.view_partner_title_form")
|
||||||
|
cls.view_partner_title_tree = cls.env.ref("base.view_partner_title_tree")
|
||||||
|
cls.view_users_form = cls.env.ref("base.view_users_form")
|
||||||
|
cls.view_users_tree = cls.env.ref("base.view_users_tree")
|
||||||
|
|
||||||
def test_all_web_field_required_invisible_manager(self):
|
def test_all_web_field_required_invisible_manager(self):
|
||||||
|
# related fields are created
|
||||||
|
self.assertTrue(self.invisible_rec_id.visibility_field_id)
|
||||||
|
self.assertTrue(self.required_rec_id.required_field_id)
|
||||||
|
self.assertTrue(self.readonly_rec_id.readonly_field_id)
|
||||||
# onchange_field_id()
|
# onchange_field_id()
|
||||||
self.assertFalse(self.invisible_title_rec_id.required)
|
self.assertFalse(self.invisible_title_rec_id.required)
|
||||||
self.invisible_title_rec_id.field_id = self.partner_title_name_field_id
|
self.invisible_title_rec_id.field_id = self.partner_title_name_field_id
|
||||||
|
@ -65,4 +84,49 @@ class TestFieldRequiredIvisibleManager(common.SavepointCase):
|
||||||
self.required_rec_id._compute_model_name()
|
self.required_rec_id._compute_model_name()
|
||||||
self.assertEqual(self.required_rec_id.model_name, "res.partner")
|
self.assertEqual(self.required_rec_id.model_name, "res.partner")
|
||||||
# fields_view_get()
|
# fields_view_get()
|
||||||
self.deco_addict.fields_view_get(view_id=self.partner_view.id, view_type="form")
|
self.deco_addict.fields_view_get(
|
||||||
|
view_id=self.partner_form_view.id, view_type="form"
|
||||||
|
)
|
||||||
|
self.deco_addict.fields_view_get(
|
||||||
|
view_id=self.partner_tree_view.id, view_type="tree"
|
||||||
|
)
|
||||||
|
self.res_partner_title_madam.fields_view_get(
|
||||||
|
view_id=self.view_partner_title_form.id, view_type="form"
|
||||||
|
)
|
||||||
|
self.res_partner_title_madam.fields_view_get(
|
||||||
|
view_id=self.view_partner_title_tree.id, view_type="tree"
|
||||||
|
)
|
||||||
|
self.env.user.fields_view_get(view_id=self.view_users_form.id, view_type="form")
|
||||||
|
self.env.user.fields_view_get(view_id=self.view_users_tree.id, view_type="tree")
|
||||||
|
# read
|
||||||
|
self.deco_addict.read(
|
||||||
|
[
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"x_computed_res_partner_name_readonly",
|
||||||
|
"x_computed_res_partner_name_required",
|
||||||
|
"x_computed_res_partner_name_visibility",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.res_partner_title_madam.read(
|
||||||
|
["id", "name", "x_computed_res_partner_title_shortcut_visibility"]
|
||||||
|
)
|
||||||
|
self.env.user.read(["id", "name"])
|
||||||
|
self.env.user._compute_restrictions_fields()
|
||||||
|
# computed value
|
||||||
|
self.assertTrue(self.deco_addict.x_computed_res_partner_name_readonly)
|
||||||
|
self.assertTrue(self.deco_addict.x_computed_res_partner_name_required)
|
||||||
|
self.assertFalse(self.deco_addict.x_computed_res_partner_name_visibility)
|
||||||
|
self.assertTrue(
|
||||||
|
self.res_partner_title_madam.x_computed_res_partner_title_shortcut_visibility
|
||||||
|
)
|
||||||
|
# change domain, reset cache. Should be True now
|
||||||
|
self.invisible_rec_id.condition_domain = "[('id', '>', 0)]"
|
||||||
|
self.deco_addict.invalidate_cache()
|
||||||
|
self.deco_addict.read(["x_computed_res_partner_name_visibility"])
|
||||||
|
self.assertTrue(self.deco_addict.x_computed_res_partner_name_visibility)
|
||||||
|
# unlink
|
||||||
|
field_name = self.invisible_title_rec_id.get_field_name("visibility")
|
||||||
|
self.invisible_title_rec_id.unlink()
|
||||||
|
field_id = self.env["ir.model.fields"].search([("name", "=", field_name)])
|
||||||
|
self.assertFalse(field_id)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_model_form_inherit" model="ir.ui.view">
|
<record id="view_model_form_inherit" model="ir.ui.view">
|
||||||
|
@ -33,6 +32,7 @@
|
||||||
/>
|
/>
|
||||||
<field name="condition_domain" />
|
<field name="condition_domain" />
|
||||||
<field name="group_ids" widget="many2many_tags" />
|
<field name="group_ids" widget="many2many_tags" />
|
||||||
|
<field name="required_field_id" optional="hide" />
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
|
@ -59,6 +59,35 @@
|
||||||
<field name="field_invisible" />
|
<field name="field_invisible" />
|
||||||
<field name="condition_domain" />
|
<field name="condition_domain" />
|
||||||
<field name="group_ids" widget="many2many_tags" />
|
<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>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
|
@ -76,6 +105,7 @@
|
||||||
|
|
||||||
<field name="required_model_id" invisible="1" />
|
<field name="required_model_id" invisible="1" />
|
||||||
<field name="invisible_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="model_name" invisible="1" />
|
||||||
<field
|
<field
|
||||||
name="field_id"
|
name="field_id"
|
||||||
|
@ -91,8 +121,11 @@
|
||||||
/>
|
/>
|
||||||
<field
|
<field
|
||||||
name="field_invisible"
|
name="field_invisible"
|
||||||
attrs="{'readonly':[('default_required', '=', True)],
|
attrs="{'invisible':[('invisible_model_id', '=', False)]}"
|
||||||
'invisible':[('invisible_model_id', '=', False)]}"
|
/>
|
||||||
|
<field
|
||||||
|
name="field_readonly"
|
||||||
|
attrs="{'invisible':[('readonly_model_id', '=', False)]}"
|
||||||
/>
|
/>
|
||||||
<field
|
<field
|
||||||
name="condition_domain"
|
name="condition_domain"
|
||||||
|
|
Loading…
Reference in New Issue