[MIG] auditlog: Migration to 17.0

pull/2755/head
Raf Ven 2024-02-05 08:36:22 +01:00
parent e5c22848ff
commit dcab07e824
7 changed files with 102 additions and 92 deletions

View File

@ -3,7 +3,7 @@
{ {
"name": "Audit Log", "name": "Audit Log",
"version": "16.0.2.0.2", "version": "17.0.1.0.0",
"author": "ABF OSIELL, Odoo Community Association (OCA)", "author": "ABF OSIELL, Odoo Community Association (OCA)",
"license": "AGPL-3", "license": "AGPL-3",
"website": "https://github.com/OCA/server-tools", "website": "https://github.com/OCA/server-tools",

View File

@ -61,4 +61,4 @@ class AuditlogLogLineView(models.Model):
@property @property
def _table_query(self): def _table_query(self):
return "SELECT %s FROM %s" % (self._select_query(), self._from_query()) return f"SELECT {self._select_query()} FROM {self._from_query()}"

View File

@ -31,7 +31,6 @@ class AuditlogAutovacuum(models.TransientModel):
order="create_date asc", order="create_date asc",
) )
nb_records = len(records) nb_records = len(records)
with self.env.norecompute():
records.unlink() records.unlink()
_logger.info("AUTOVACUUM - %s '%s' records deleted", nb_records, data_model) _logger.info("AUTOVACUUM - %s '%s' records deleted", nb_records, data_model)
return True return True

View File

@ -51,12 +51,11 @@ class AuditlogRule(models.Model):
_name = "auditlog.rule" _name = "auditlog.rule"
_description = "Auditlog - Rule" _description = "Auditlog - Rule"
name = fields.Char(required=True, states={"subscribed": [("readonly", True)]}) name = fields.Char(required=True)
model_id = fields.Many2one( model_id = fields.Many2one(
"ir.model", "ir.model",
"Model", "Model",
help="Select model for which you want to generate log.", help="Select model for which you want to generate log.",
states={"subscribed": [("readonly", True)]},
ondelete="set null", ondelete="set null",
index=True, index=True,
) )
@ -68,8 +67,7 @@ class AuditlogRule(models.Model):
"user_id", "user_id",
"rule_id", "rule_id",
string="Users", string="Users",
help="if User is not added then it will applicable for all users", help="if no user is added then it will applicable for all users",
states={"subscribed": [("readonly", True)]},
) )
log_read = fields.Boolean( log_read = fields.Boolean(
"Log Reads", "Log Reads",
@ -77,7 +75,6 @@ class AuditlogRule(models.Model):
"Select this if you want to keep track of read/open on any " "Select this if you want to keep track of read/open on any "
"record of the model of this rule" "record of the model of this rule"
), ),
states={"subscribed": [("readonly", True)]},
) )
log_write = fields.Boolean( log_write = fields.Boolean(
"Log Writes", "Log Writes",
@ -86,7 +83,6 @@ class AuditlogRule(models.Model):
"Select this if you want to keep track of modification on any " "Select this if you want to keep track of modification on any "
"record of the model of this rule" "record of the model of this rule"
), ),
states={"subscribed": [("readonly", True)]},
) )
log_unlink = fields.Boolean( log_unlink = fields.Boolean(
"Log Deletes", "Log Deletes",
@ -95,7 +91,6 @@ class AuditlogRule(models.Model):
"Select this if you want to keep track of deletion on any " "Select this if you want to keep track of deletion on any "
"record of the model of this rule" "record of the model of this rule"
), ),
states={"subscribed": [("readonly", True)]},
) )
log_create = fields.Boolean( log_create = fields.Boolean(
"Log Creates", "Log Creates",
@ -104,7 +99,6 @@ class AuditlogRule(models.Model):
"Select this if you want to keep track of creation on any " "Select this if you want to keep track of creation on any "
"record of the model of this rule" "record of the model of this rule"
), ),
states={"subscribed": [("readonly", True)]},
) )
log_type = fields.Selection( log_type = fields.Selection(
[("full", "Full log"), ("fast", "Fast log")], [("full", "Full log"), ("fast", "Fast log")],
@ -118,7 +112,6 @@ class AuditlogRule(models.Model):
"Fast log: only log the changes made through the create and " "Fast log: only log the changes made through the create and "
"write operations (less information, but it is faster)" "write operations (less information, but it is faster)"
), ),
states={"subscribed": [("readonly", True)]},
) )
state = fields.Selection( state = fields.Selection(
@ -129,7 +122,6 @@ class AuditlogRule(models.Model):
action_id = fields.Many2one( action_id = fields.Many2one(
"ir.actions.act_window", "ir.actions.act_window",
string="Action", string="Action",
states={"subscribed": [("readonly", True)]},
) )
capture_record = fields.Boolean( capture_record = fields.Boolean(
help="Select this if you want to keep track of Unlink Record", help="Select this if you want to keep track of Unlink Record",
@ -138,14 +130,12 @@ class AuditlogRule(models.Model):
"res.users", "res.users",
string="Users to Exclude", string="Users to Exclude",
context={"active_test": False}, context={"active_test": False},
states={"subscribed": [("readonly", True)]},
) )
fields_to_exclude_ids = fields.Many2many( fields_to_exclude_ids = fields.Many2many(
"ir.model.fields", "ir.model.fields",
domain="[('model_id', '=', model_id)]", domain="[('model_id', '=', model_id)]",
string="Fields to Exclude", string="Fields to Exclude",
states={"subscribed": [("readonly", True)]},
) )
_sql_constraints = [ _sql_constraints = [
@ -161,7 +151,7 @@ class AuditlogRule(models.Model):
def _register_hook(self): def _register_hook(self):
"""Get all rules and apply them to log method calls.""" """Get all rules and apply them to log method calls."""
super(AuditlogRule, self)._register_hook() super()._register_hook()
if not hasattr(self.pool, "_auditlog_field_cache"): if not hasattr(self.pool, "_auditlog_field_cache"):
self.pool._auditlog_field_cache = {} self.pool._auditlog_field_cache = {}
if not hasattr(self.pool, "_auditlog_model_cache"): if not hasattr(self.pool, "_auditlog_model_cache"):
@ -170,6 +160,24 @@ class AuditlogRule(models.Model):
self = self.search([("state", "=", "subscribed")]) self = self.search([("state", "=", "subscribed")])
return self._patch_methods() return self._patch_methods()
def _patch_method(self, model, method_name, check_attr):
result = new_method = False
model_class = type(model)
if method_name == "create":
new_method = self._make_create()
elif method_name == "read":
new_method = self._make_read()
elif method_name == "write":
new_method = self._make_write()
elif method_name == "unlink":
new_method = self._make_unlink()
if new_method:
new_method.origin = getattr(model_class, method_name)
setattr(model_class, method_name, new_method)
setattr(type(model), check_attr, True)
result = True
return result
def _patch_methods(self): def _patch_methods(self):
"""Patch ORM methods of models defined in rules to log their calls.""" """Patch ORM methods of models defined in rules to log their calls."""
updated = False updated = False
@ -185,27 +193,19 @@ class AuditlogRule(models.Model):
# -> create # -> create
check_attr = "auditlog_ruled_create" check_attr = "auditlog_ruled_create"
if rule.log_create and not hasattr(model_model, check_attr): if rule.log_create and not hasattr(model_model, check_attr):
model_model._patch_method("create", rule._make_create()) updated = rule._patch_method(model_model, "create", check_attr)
setattr(type(model_model), check_attr, True)
updated = True
# -> read # -> read
check_attr = "auditlog_ruled_read" check_attr = "auditlog_ruled_read"
if rule.log_read and not hasattr(model_model, check_attr): if rule.log_read and not hasattr(model_model, check_attr):
model_model._patch_method("read", rule._make_read()) updated = rule._patch_method(model_model, "read", check_attr)
setattr(type(model_model), check_attr, True)
updated = True
# -> write # -> write
check_attr = "auditlog_ruled_write" check_attr = "auditlog_ruled_write"
if rule.log_write and not hasattr(model_model, check_attr): if rule.log_write and not hasattr(model_model, check_attr):
model_model._patch_method("write", rule._make_write()) updated = rule._patch_method(model_model, "write", check_attr)
setattr(type(model_model), check_attr, True)
updated = True
# -> unlink # -> unlink
check_attr = "auditlog_ruled_unlink" check_attr = "auditlog_ruled_unlink"
if rule.log_unlink and not hasattr(model_model, check_attr): if rule.log_unlink and not hasattr(model_model, check_attr):
model_model._patch_method("unlink", rule._make_unlink()) updated = rule._patch_method(model_model, "unlink", check_attr)
setattr(type(model_model), check_attr, True)
updated = True
return updated return updated
def _revert_methods(self): def _revert_methods(self):
@ -217,7 +217,9 @@ class AuditlogRule(models.Model):
if getattr(rule, "log_%s" % method) and hasattr( if getattr(rule, "log_%s" % method) and hasattr(
getattr(model_model, method), "origin" getattr(model_model, method), "origin"
): ):
model_model._revert_method(method) setattr(
type(model_model), method, getattr(model_model, method).origin
)
delattr(type(model_model), "auditlog_ruled_%s" % method) delattr(type(model_model), "auditlog_ruled_%s" % method)
updated = True updated = True
if updated: if updated:
@ -252,7 +254,7 @@ class AuditlogRule(models.Model):
def unlink(self): def unlink(self):
"""Unsubscribe rules before removing them.""" """Unsubscribe rules before removing them."""
self.unsubscribe() self.unsubscribe()
return super(AuditlogRule, self).unlink() return super().unlink()
@api.model @api.model
def get_auditlog_fields(self, model): def get_auditlog_fields(self, model):
@ -315,7 +317,7 @@ class AuditlogRule(models.Model):
vals_list2 = copy.deepcopy(vals_list) vals_list2 = copy.deepcopy(vals_list)
new_records = create_fast.origin(self, vals_list, **kwargs) new_records = create_fast.origin(self, vals_list, **kwargs)
new_values = {} new_values = {}
for vals, new_record in zip(vals_list2, new_records): for vals, new_record in zip(vals_list2, new_records, strict=True):
new_values.setdefault(new_record.id, vals) new_values.setdefault(new_record.id, vals)
if self.env.user in users_to_exclude: if self.env.user in users_to_exclude:
return new_records return new_records
@ -511,10 +513,9 @@ class AuditlogRule(models.Model):
auditlog_rule = self.env["auditlog.rule"].search([("model_id", "=", model_id)]) auditlog_rule = self.env["auditlog.rule"].search([("model_id", "=", model_id)])
fields_to_exclude = auditlog_rule.fields_to_exclude_ids.mapped("name") fields_to_exclude = auditlog_rule.fields_to_exclude_ids.mapped("name")
for res_id in res_ids: for res_id in res_ids:
name = model_model.browse(res_id).name_get() res = model_model.browse(res_id)
res_name = name and name[0] and name[0][1]
vals = { vals = {
"name": res_name, "name": res.display_name,
"model_id": model_id, "model_id": model_id,
"res_id": res_id, "res_id": res_id,
"method": method, "method": method,
@ -600,10 +601,10 @@ class AuditlogRule(models.Model):
"new_value_text": False, "new_value_text": False,
} }
if field["relation"] and "2many" in field["ttype"]: if field["relation"] and "2many" in field["ttype"]:
old_value_text = ( vals["old_value_text"] = [
self.env[field["relation"]].browse(vals["old_value"]).name_get() (x.id, x.display_name)
) for x in self.env[field["relation"]].browse(vals["old_value"])
vals["old_value_text"] = old_value_text ]
return vals return vals
def _create_log_line_on_write( def _create_log_line_on_write(
@ -635,27 +636,27 @@ class AuditlogRule(models.Model):
"new_value": new_values[log.res_id][field["name"]], "new_value": new_values[log.res_id][field["name"]],
"new_value_text": new_values[log.res_id][field["name"]], "new_value_text": new_values[log.res_id][field["name"]],
} }
# for *2many fields, log the name_get # for *2many fields, log the display_name
if log.log_type == "full" and field["relation"] and "2many" in field["ttype"]: if log.log_type == "full" and field["relation"] and "2many" in field["ttype"]:
# Filter IDs to prevent a 'name_get()' call on deleted resources # Filter IDs to prevent a 'display_name' call on deleted resources
existing_ids = self.env[field["relation"]]._search( existing_ids = self.env[field["relation"]]._search(
[("id", "in", vals["old_value"])] [("id", "in", vals["old_value"])]
) )
old_value_text = [] old_value_text = []
if existing_ids: if existing_ids:
existing_values = ( old_value_text = [
self.env[field["relation"]].browse(existing_ids).name_get() (x.id, x.display_name)
) for x in self.env[field["relation"]].browse(existing_ids)
old_value_text.extend(existing_values) ]
# Deleted resources will have a 'DELETED' text representation # Deleted resources will have a 'DELETED' text representation
deleted_ids = set(vals["old_value"]) - set(existing_ids) deleted_ids = set(vals["old_value"]) - set(existing_ids)
for deleted_id in deleted_ids: for deleted_id in deleted_ids:
old_value_text.append((deleted_id, "DELETED")) old_value_text.append((deleted_id, "DELETED"))
vals["old_value_text"] = old_value_text vals["old_value_text"] = old_value_text
new_value_text = ( vals["new_value_text"] = [
self.env[field["relation"]].browse(vals["new_value"]).name_get() (x.id, x.display_name)
) for x in self.env[field["relation"]].browse(vals["new_value"])
vals["new_value_text"] = new_value_text ]
return vals return vals
def _create_log_line_on_create( def _create_log_line_on_create(
@ -686,10 +687,10 @@ class AuditlogRule(models.Model):
"new_value_text": new_values[log.res_id][field["name"]], "new_value_text": new_values[log.res_id][field["name"]],
} }
if log.log_type == "full" and field["relation"] and "2many" in field["ttype"]: if log.log_type == "full" and field["relation"] and "2many" in field["ttype"]:
new_value_text = ( vals["new_value_text"] = [
self.env[field["relation"]].browse(vals["new_value"]).name_get() (x.id, x.display_name)
) for x in self.env[field["relation"]].browse(vals["new_value"])
vals["new_value_text"] = new_value_text ]
return vals return vals
def subscribe(self): def subscribe(self):

View File

@ -2,7 +2,7 @@
# © 2018 Pieter Paulussen <pieter_paulussen@me.com> # © 2018 Pieter Paulussen <pieter_paulussen@me.com>
# © 2021 Stefan Rijnhart <stefan@opener.amsterdam> # © 2021 Stefan Rijnhart <stefan@opener.amsterdam>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.tests.common import Form, TransactionCase from odoo.tests.common import TransactionCase
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
from odoo.addons.base.models.res_users import name_boolean_group from odoo.addons.base.models.res_users import name_boolean_group
@ -50,11 +50,15 @@ class AuditlogCommon:
self.groups_rule.subscribe() self.groups_rule.subscribe()
auditlog_log = self.env["auditlog.log"] auditlog_log = self.env["auditlog.log"]
testgroup3 = testgroup3 = self.env["res.groups"].create({"name": "testgroup3"}) testgroup3 = self.env["res.groups"].create({"name": "testgroup3"})
testgroup4 = self.env["res.groups"].create( testgroup4 = self.env["res.groups"].create({"name": "testgroup4"})
{"name": "testgroup4", "implied_ids": [(4, testgroup3.id)]} testgroup5 = self.env["res.groups"].create(
{
"name": "testgroup5",
"implied_ids": [(4, testgroup3.id), (4, testgroup4.id)],
}
) )
testgroup4.write({"implied_ids": [(2, testgroup3.id)]}) testgroup5.write({"implied_ids": [(2, testgroup3.id)]})
self.assertTrue( self.assertTrue(
auditlog_log.search( auditlog_log.search(
[ [
@ -69,7 +73,7 @@ class AuditlogCommon:
[ [
("model_id", "=", self.groups_model_id), ("model_id", "=", self.groups_model_id),
("method", "=", "create"), ("method", "=", "create"),
("res_id", "=", testgroup4.id), ("res_id", "=", testgroup5.id),
] ]
).ensure_one() ).ensure_one()
) )
@ -78,7 +82,7 @@ class AuditlogCommon:
[ [
("model_id", "=", self.groups_model_id), ("model_id", "=", self.groups_model_id),
("method", "=", "write"), ("method", "=", "write"),
("res_id", "=", testgroup4.id), ("res_id", "=", testgroup5.id),
] ]
).ensure_one() ).ensure_one()
) )
@ -206,7 +210,14 @@ class AuditlogCommon:
def test_LogUpdate(self): def test_LogUpdate(self):
"""Tests write results with different M2O values.""" """Tests write results with different M2O values."""
self.groups_rule.subscribe() self.groups_rule.subscribe()
group = self.env["res.groups"].create({"name": "testgroup1"}) testgroup3 = self.env["res.groups"].create({"name": "testgroup3"})
testgroup4 = self.env["res.groups"].create({"name": "testgroup4"})
group = self.env["res.groups"].create(
{
"name": "testgroup1",
"implied_ids": [(4, testgroup3.id), (4, testgroup4.id)],
}
)
cat = self.env["ir.module.category"].create({"name": "Test Category"}) cat = self.env["ir.module.category"].create({"name": "Test Category"})
group.write( group.write(
{ {
@ -262,7 +273,7 @@ class AuditlogCommon:
class TestAuditlogFull(TransactionCase, AuditlogCommon): class TestAuditlogFull(TransactionCase, AuditlogCommon):
def setUp(self): def setUp(self):
super(TestAuditlogFull, self).setUp() super().setUp()
self.groups_model_id = self.env.ref("base.model_res_groups").id self.groups_model_id = self.env.ref("base.model_res_groups").id
self.groups_rule = self.env["auditlog.rule"].create( self.groups_rule = self.env["auditlog.rule"].create(
{ {
@ -278,12 +289,12 @@ class TestAuditlogFull(TransactionCase, AuditlogCommon):
def tearDown(self): def tearDown(self):
self.groups_rule.unlink() self.groups_rule.unlink()
super(TestAuditlogFull, self).tearDown() super().tearDown()
class TestAuditlogFast(TransactionCase, AuditlogCommon): class TestAuditlogFast(TransactionCase, AuditlogCommon):
def setUp(self): def setUp(self):
super(TestAuditlogFast, self).setUp() super().setUp()
self.groups_model_id = self.env.ref("base.model_res_groups").id self.groups_model_id = self.env.ref("base.model_res_groups").id
self.groups_rule = self.env["auditlog.rule"].create( self.groups_rule = self.env["auditlog.rule"].create(
{ {
@ -299,7 +310,7 @@ class TestAuditlogFast(TransactionCase, AuditlogCommon):
def tearDown(self): def tearDown(self):
self.groups_rule.unlink() self.groups_rule.unlink()
super(TestAuditlogFast, self).tearDown() super().tearDown()
class TestFieldRemoval(TransactionCase): class TestFieldRemoval(TransactionCase):
@ -398,7 +409,7 @@ class TestFieldRemoval(TransactionCase):
class TestAuditlogFullCaptureRecord(TransactionCase, AuditlogCommon): class TestAuditlogFullCaptureRecord(TransactionCase, AuditlogCommon):
def setUp(self): def setUp(self):
super(TestAuditlogFullCaptureRecord, self).setUp() super().setUp()
self.groups_model_id = self.env.ref("base.model_res_groups").id self.groups_model_id = self.env.ref("base.model_res_groups").id
self.groups_rule = self.env["auditlog.rule"].create( self.groups_rule = self.env["auditlog.rule"].create(
{ {
@ -415,13 +426,13 @@ class TestAuditlogFullCaptureRecord(TransactionCase, AuditlogCommon):
def tearDown(self): def tearDown(self):
self.groups_rule.unlink() self.groups_rule.unlink()
super(TestAuditlogFullCaptureRecord, self).tearDown() super().tearDown()
class AuditLogRuleTestForUserFields(TransactionCase): class AuditLogRuleTestForUserFields(TransactionCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super(AuditLogRuleTestForUserFields, cls).setUpClass() super().setUpClass()
# get Contact model id # get Contact model id
cls.contact_model_id = ( cls.contact_model_id = (
cls.env["ir.model"].search([("model", "=", "res.partner")]).id cls.env["ir.model"].search([("model", "=", "res.partner")]).id
@ -551,14 +562,11 @@ class AuditLogRuleTestForUserFields(TransactionCase):
self.assertTrue("phone" not in field_names) self.assertTrue("phone" not in field_names)
def test_03_AuditlogFull_user_exclude_write_log(self): def test_03_AuditlogFull_user_exclude_write_log(self):
# Update email in Form view with excluded user # Update email with excluded user
partner_form = Form( partner = self.testpartner1.with_user(self.user.id).with_context(
self.testpartner1.with_user(self.user.id).with_context(
tracking_disable=True tracking_disable=True
) )
) partner.email = "vendor@mail.com"
partner_form.email = "vendor@mail.com"
testpartner1 = partner_form.save()
# Checking write log not created # Checking write log not created
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
@ -566,7 +574,7 @@ class AuditLogRuleTestForUserFields(TransactionCase):
[ [
("model_id", "=", self.auditlog_rule.model_id.id), ("model_id", "=", self.auditlog_rule.model_id.id),
("method", "=", "write"), ("method", "=", "write"),
("res_id", "=", testpartner1.id), ("res_id", "=", partner.id),
("user_id", "=", self.user.id), ("user_id", "=", self.user.id),
] ]
).ensure_one() ).ensure_one()

View File

@ -7,7 +7,7 @@ from odoo.tests.common import TransactionCase
class TestAuditlogAutovacuum(TransactionCase): class TestAuditlogAutovacuum(TransactionCase):
def setUp(self): def setUp(self):
super(TestAuditlogAutovacuum, self).setUp() super().setUp()
self.groups_model_id = self.env.ref("base.model_res_groups").id self.groups_model_id = self.env.ref("base.model_res_groups").id
self.groups_rule = self.env["auditlog.rule"].create( self.groups_rule = self.env["auditlog.rule"].create(
{ {
@ -24,7 +24,7 @@ class TestAuditlogAutovacuum(TransactionCase):
def tearDown(self): def tearDown(self):
self.groups_rule.unlink() self.groups_rule.unlink()
super(TestAuditlogAutovacuum, self).tearDown() super().tearDown()
def test_autovacuum(self): def test_autovacuum(self):
log_model = self.env["auditlog.log"] log_model = self.env["auditlog.log"]

View File

@ -18,46 +18,48 @@
string="Subscribe" string="Subscribe"
name="subscribe" name="subscribe"
type="object" type="object"
states="draft" invisible="state != 'draft'"
class="oe_highlight" class="oe_highlight"
/> />
<button <button
string="Unsubscribe" string="Unsubscribe"
name="unsubscribe" name="unsubscribe"
type="object" type="object"
states="subscribed" invisible="state != 'subscribed'"
/> />
<field name="state" widget="statusbar" /> <field name="state" widget="statusbar" />
</header> </header>
<sheet> <sheet>
<group string="Rule"> <group string="Rule">
<group colspan="1"> <group colspan="1">
<field name="name" required="1" /> <field name="name" readonly="state == 'subscribed'" />
<field name="model_id" /> <field name="model_id" readonly="state == 'subscribed'" />
<field name="log_type" /> <field name="log_type" readonly="state == 'subscribed'" />
<field <field
name="action_id" name="action_id"
readonly="1" readonly="state == 'subscribed'"
groups="base.group_no_one" groups="base.group_no_one"
/> />
<field <field
name="capture_record" name="capture_record"
attrs="{'invisible':['|' ,('log_type','!=', 'full'), ('log_unlink','!=', True)]}" invisible="log_type != 'full' or log_unlink != True"
/> />
<field <field
name="users_to_exclude_ids" name="users_to_exclude_ids"
widget="many2many_tags" widget="many2many_tags"
readonly="state == 'subscribed'"
/> />
<field <field
name="fields_to_exclude_ids" name="fields_to_exclude_ids"
widget="many2many_tags" widget="many2many_tags"
readonly="state == 'subscribed'"
/> />
</group> </group>
<group colspan="1"> <group colspan="1">
<field name="log_read" /> <field name="log_read" readonly="state == 'subscribed'" />
<field name="log_write" /> <field name="log_write" readonly="state == 'subscribed'" />
<field name="log_unlink" /> <field name="log_unlink" readonly="state == 'subscribed'" />
<field name="log_create" /> <field name="log_create" readonly="state == 'subscribed'" />
</group> </group>
</group> </group>
</sheet> </sheet>
@ -142,12 +144,12 @@
<field name="model_id" readonly="1" /> <field name="model_id" readonly="1" />
<field <field
name="model_name" name="model_name"
attrs="{'invisible': [('model_id', '!=', False)]}" invisible="model_id != False"
readonly="1" readonly="1"
/> />
<field <field
name="model_model" name="model_model"
attrs="{'invisible': [('model_id', '!=', False)]}" invisible="model_id != False"
readonly="1" readonly="1"
/> />
<field name="res_id" readonly="1" /> <field name="res_id" readonly="1" />
@ -165,7 +167,7 @@
<field name="field_id" readonly="1" /> <field name="field_id" readonly="1" />
<field <field
name="field_name" name="field_name"
attrs="{'invisible': [('field_id', '!=', False)]}" invisible="field_id != False"
readonly="1" readonly="1"
/> />
</group> </group>