From 0d3f4200077144c2e36357e2aa78aeaec998e625 Mon Sep 17 00:00:00 2001 From: Ilyas Date: Tue, 6 Feb 2024 10:22:19 +0100 Subject: [PATCH] [IMP] web_field_required_invisible_manager: compatibility with tier validation --- .../models/__init__.py | 4 +- .../models/base.py | 66 +++++++++ .../models/ir_model.py | 20 +++ .../models/{models.py => ir_ui_view.py} | 136 ++++-------------- .../static/description/index.html | 1 + 5 files changed, 119 insertions(+), 108 deletions(-) create mode 100644 web_field_required_invisible_manager/models/base.py create mode 100644 web_field_required_invisible_manager/models/ir_model.py rename web_field_required_invisible_manager/models/{models.py => ir_ui_view.py} (53%) diff --git a/web_field_required_invisible_manager/models/__init__.py b/web_field_required_invisible_manager/models/__init__.py index 379b396c7..4f7d30749 100644 --- a/web_field_required_invisible_manager/models/__init__.py +++ b/web_field_required_invisible_manager/models/__init__.py @@ -1,2 +1,4 @@ from . import custom_field_restriction -from . import models +from . import base +from . import ir_ui_view +from . import ir_model diff --git a/web_field_required_invisible_manager/models/base.py b/web_field_required_invisible_manager/models/base.py new file mode 100644 index 000000000..ba609fc33 --- /dev/null +++ b/web_field_required_invisible_manager/models/base.py @@ -0,0 +1,66 @@ +# Copyright 2023 ooops404 +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +from odoo import models +from odoo.tools.safe_eval import safe_eval + + +class Base(models.AbstractModel): + _inherit = "base" + + def default_get(self, fields_list): + res = super(Base, self).default_get(fields_list) + if self.env.user.has_group("base.group_user"): + vals = self._default_get_compute_restrictions_fields() + if vals: + res.update(vals) + return res + + def _default_get_compute_restrictions_fields(self): + restrictions = self.env["custom.field.restriction"].search( + [("model_name", "=", self._name)] + ) + values = {} + if not restrictions: + return values + for r in restrictions: + if r.visibility_field_id: + field_name = r.visibility_field_id.name + values[field_name] = False + if r.required_field_id: + field_name = r.required_field_id.name + values[field_name] = False + if r.readonly_field_id: + field_name = r.readonly_field_id.name + values[field_name] = False + if r.group_ids: + if r.group_ids & self.env.user.groups_id: + values[field_name] = True + return values + + 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: + filtered_rec_id = record.filtered_domain( + safe_eval(r.condition_domain) + ) + if filtered_rec_id and r.group_ids & self.env.user.groups_id: + record[field_name] = True + elif r.group_ids: + if r.group_ids & self.env.user.groups_id: + record[field_name] = True diff --git a/web_field_required_invisible_manager/models/ir_model.py b/web_field_required_invisible_manager/models/ir_model.py new file mode 100644 index 000000000..527cb761c --- /dev/null +++ b/web_field_required_invisible_manager/models/ir_model.py @@ -0,0 +1,20 @@ +# Copyright 2023 ooops404 +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +from odoo import fields, models + + +class IrModel(models.Model): + _inherit = "ir.model" + + custom_required_restriction_ids = fields.One2many( + "custom.field.restriction", + "required_model_id", + ) + custom_invisible_restriction_ids = fields.One2many( + "custom.field.restriction", + "invisible_model_id", + ) + custom_readonly_restriction_ids = fields.One2many( + "custom.field.restriction", + "readonly_model_id", + ) diff --git a/web_field_required_invisible_manager/models/models.py b/web_field_required_invisible_manager/models/ir_ui_view.py similarity index 53% rename from web_field_required_invisible_manager/models/models.py rename to web_field_required_invisible_manager/models/ir_ui_view.py index 8594a23a0..c8fac3733 100644 --- a/web_field_required_invisible_manager/models/models.py +++ b/web_field_required_invisible_manager/models/ir_ui_view.py @@ -1,60 +1,40 @@ # Copyright 2023 ooops404 # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) -import json as simplejson +import json from lxml import etree -from odoo import api, fields, models -from odoo.tools.safe_eval import safe_eval +from odoo import models from odoo.addons.base.models.ir_ui_view import NameManager -class IrModel(models.Model): - _inherit = "ir.model" +class IrUiView(models.Model): + _inherit = "ir.ui.view" - custom_required_restriction_ids = fields.One2many( - "custom.field.restriction", - "required_model_id", - ) - custom_invisible_restriction_ids = fields.One2many( - "custom.field.restriction", - "invisible_model_id", - ) - custom_readonly_restriction_ids = fields.One2many( - "custom.field.restriction", - "readonly_model_id", - ) - - -class Base(models.AbstractModel): - _inherit = "base" - - @api.model - def fields_view_get( - self, view_id=None, view_type=False, toolbar=False, submenu=False - ): - arch = super().fields_view_get( - view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu - ) - if view_type not in ["form", "tree"]: - return arch - # TODO speed up somehow + def postprocess_and_fields(self, node, model=None, validate=False): + arch, new_fields = super().postprocess_and_fields(node, model, validate) + if self.type not in ["form", "tree"] and node.tag not in ["form", "tree"]: + return arch, new_fields restrictions = self.env["custom.field.restriction"].search( [ - ("model_name", "=", self._name), + ("model_name", "=", model or self.model), ("group_ids", "in", self.env.user.groups_id.ids), ] ) + view_type = node.tag or self.type if restrictions: arch = self.create_restrictions_fields(restrictions, view_type, arch) - arch = self.process_child_fields(arch, view_type) - return arch + arch = self.process_child_fields(arch, new_fields, view_type) + return arch, new_fields - def process_child_fields(self, arch, view_type): + def process_child_fields(self, arch, new_fields, view_type): """Collect all relational fields and update their views""" + view_model = self.env["ir.model"]._get(self.model) related_fields = [ - (k, v.comodel_name) for k, v in self._fields.items() if v.comodel_name + (k.name, k.relation) + for k in view_model.field_id + if k.ttype in ["many2many", "many2one", "one2many"] ] related_models_names = [r[1] for r in related_fields] restrictions = self.env["custom.field.restriction"].search( @@ -69,16 +49,16 @@ class Base(models.AbstractModel): filter(lambda x: x[1] == restr.model_name, related_fields) ) for todo_field in todo_fields: - if not arch["fields"].get(todo_field[0]): + if not new_fields.get(todo_field[0]): continue - for sub_view_type, sub_view in arch["fields"][todo_field[0]][ + for sub_view_type, sub_view in new_fields[todo_field[0]][ "views" ].items(): if sub_view_type not in ["form", "tree"]: continue z_arch = sub_view - z_arch = self.create_restrictions_fields( - restr, view_type, z_arch + z_arch["arch"] = self.create_restrictions_fields( + restr, view_type, z_arch["arch"] ) if type(z_arch["arch"]) is bytes: z_arch["arch"] = z_arch["arch"].decode("utf-8") @@ -107,7 +87,7 @@ class Base(models.AbstractModel): return arch def create_restrictions_fields(self, restrictions, view_type, arch): - doc = etree.XML(arch["arch"]) + doc = etree.XML(arch) for node in doc.xpath("//field"): name = node.attrib.get("name") restrictions_filtered = restrictions.filtered( @@ -125,91 +105,33 @@ class Base(models.AbstractModel): "" ) if r.field_invisible and r.invisible_model_id: - modifiers = simplejson.loads(node.get("modifiers")) + modifiers = json.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)) + node.set("modifiers", json.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")) + modifiers = json.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)) + node.set("modifiers", json.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")) + modifiers = json.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)) + node.set("modifiers", json.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) + 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: - filtered_rec_id = record.filtered_domain( - safe_eval(r.condition_domain) - ) - if filtered_rec_id and r.group_ids & self.env.user.groups_id: - record[field_name] = True - elif r.group_ids: - if r.group_ids & self.env.user.groups_id: - record[field_name] = True - - def default_get(self, fields_list): - res = super(Base, self).default_get(fields_list) - if self.env.user.has_group("base.group_user"): - vals = self._default_get_compute_restrictions_fields() - if vals: - res.update(vals) - return res - - def _default_get_compute_restrictions_fields(self): - restrictions = self.env["custom.field.restriction"].search( - [("model_name", "=", self._name)] - ) - values = {} - if not restrictions: - return values - for r in restrictions: - if r.visibility_field_id: - field_name = r.visibility_field_id.name - values[field_name] = False - if r.required_field_id: - field_name = r.required_field_id.name - values[field_name] = False - if r.readonly_field_id: - field_name = r.readonly_field_id.name - values[field_name] = False - if r.group_ids: - if r.group_ids & self.env.user.groups_id: - values[field_name] = True - return values diff --git a/web_field_required_invisible_manager/static/description/index.html b/web_field_required_invisible_manager/static/description/index.html index 27b796da2..8b8d43405 100644 --- a/web_field_required_invisible_manager/static/description/index.html +++ b/web_field_required_invisible_manager/static/description/index.html @@ -1,3 +1,4 @@ +