# Copyright 2023 ooops404 # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) from odoo import api, fields, models from odoo.tools.safe_eval import safe_eval class CustomFieldRestriction(models.Model): _name = "custom.field.restriction" _description = "Make field invisible or required" field_id = fields.Many2one( "ir.model.fields", ondelete="cascade", required=True, string="Field", ) field_name = fields.Char( related="field_id.name", store=True, string="Field Name", ) required_model_id = fields.Many2one( "ir.model", ondelete="cascade", string="Model (required)", index=True, ) invisible_model_id = fields.Many2one( "ir.model", ondelete="cascade", string="Model (visibility)", index=True, ) readonly_model_id = fields.Many2one( "ir.model", ondelete="cascade", string="Model (readonly)", index=True, ) model_name = fields.Char( compute="_compute_model_name", store=True, string="Model Name", index=True, ) condition_domain = fields.Char() group_ids = fields.Many2many("res.groups", required=True) default_required = fields.Boolean( related="field_id.required", string="Required by Default" ) required = 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") 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): vals = {} if self.env.context.get("default_readonly_model_id"): vals["readonly_model_id"] = ( self.env["ir.model"] ._get(self.env.context.get("default_readonly_model_id")) .id ) elif self.env.context.get("default_required_model_id"): vals["required_model_id"] = ( self.env["ir.model"] ._get(self.env.context.get("default_required_model_id")) .id ) elif self.env.context.get("default_invisible_model_id"): vals["invisible_model_id"] = ( self.env["ir.model"] ._get(self.env.context.get("default_invisible_model_id")) .id ) self.update(vals) @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 rec.required = True elif rec.invisible_model_id: rec.model_name = rec.invisible_model_id.model rec.field_invisible = True elif rec.readonly_model_id: rec.model_name = rec.readonly_model_id.model rec.field_readonly = True @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 write(self, vals): res = super().write(vals) if vals.get("field_id"): if self.visibility_field_id: self.visibility_field_id.unlink() self.create_restriction_field("visibility") elif self.readonly_field_id: self.readonly_field_id.unlink() self.create_restriction_field("readonly") elif self.required_field_id: self.required_field_id.unlink() self.create_restriction_field("required") return res 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), ("state", "=", "manual")] ) 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: deps = "" if self.condition_domain: deps = ",".join( {r[0] for r in safe_eval(self.condition_domain)} - {"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()", "depends": deps, } ) 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 def unlink(self): for rec in self: rec.visibility_field_id.unlink() rec.readonly_field_id.unlink() rec.required_field_id.unlink() return super(CustomFieldRestriction, self).unlink()