# Copyright 2021 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models from odoo.exceptions import ValidationError from odoo.tools.cache import ormcache from odoo.tools.safe_eval import safe_eval class M2xCreateEditOption(models.Model): _name = "m2x.create.edit.option" _description = "Manage Options 'Create/Edit' For Fields" field_id = fields.Many2one( "ir.model.fields", domain=[("ttype", "in", ("many2many", "many2one"))], ondelete="cascade", required=True, string="Field", ) field_name = fields.Char( related="field_id.name", store=True, string="Field Name", ) model_id = fields.Many2one( "ir.model", ondelete="cascade", required=True, string="Model", ) model_name = fields.Char( compute="_compute_model_name", inverse="_inverse_model_name", store=True, string="Model Name", ) option_create = fields.Selection( [ ("none", "Do nothing"), ("set_true", "Add"), ("force_true", "Force Add"), ("set_false", "Remove"), ("force_false", "Force Remove"), ], default="set_false", help="Defines behaviour for 'Create' option:\n" "* Do nothing: nothing is done\n" "* Add/Remove: option 'Create' is set to True/False only if not" " already present in view definition\n" "* Force Add/Remove: option 'Create' is always set to True/False," " overriding any pre-existing option", required=True, string="Create Option", ) option_create_edit = fields.Selection( [ ("none", "Do nothing"), ("set_true", "Add"), ("force_true", "Force Add"), ("set_false", "Remove"), ("force_false", "Force Remove"), ], default="set_false", help="Defines behaviour for 'Create & Edit' option:\n" "* Do nothing: nothing is done\n" "* Add/Remove: option 'Create & Edit' is set to True/False only if not" " already present in view definition\n" "* Force Add/Remove: option 'Create & Edit' is always set to" " True/False, overriding any pre-existing option", required=True, string="Create & Edit Option", ) option_create_edit_wizard = fields.Boolean( default=True, help="Defines behaviour for 'Create & Edit' Wizard\n" "Set to False to prevent 'Create & Edit' Wizard to pop up", string="Create & Edit Wizard", ) _sql_constraints = [ ( "model_field_uniqueness", "unique(field_id,model_id)", "Options must be unique for each model/field couple!", ), ] @api.model_create_multi def create(self, vals_list): # Clear cache to avoid misbehavior from cached :meth:`_get()` type(self)._get.clear_cache(self.browse()) return super().create(vals_list) def write(self, vals): # Clear cache to avoid misbehavior from cached :meth:`_get()` type(self)._get.clear_cache(self.browse()) return super().write(vals) def unlink(self): # Clear cache to avoid misbehavior from cached :meth:`_get()` type(self)._get.clear_cache(self.browse()) return super().unlink() @api.depends("model_id") def _compute_model_name(self): for opt in self: opt.model_name = opt.model_id.model def _inverse_model_name(self): getter = self.env["ir.model"]._get for opt in self: # This also works as a constrain: if ``model_name`` is not a # valid model name, then ``model_id`` will be emptied, but it's # a required field! opt.model_id = getter(opt.model_name) @api.constrains("model_id", "field_id") def _check_field_in_model(self): for opt in self: if opt.field_id.model_id != opt.model_id: msg = _("'%s' is not a valid field for model '%s'!") raise ValidationError(msg % (opt.field_name, opt.model_name)) @api.constrains("field_id") def _check_field_type(self): ttypes = ("many2many", "many2one") if any(o.field_id.ttype not in ttypes for o in self): msg = _("Only Many2many and Many2one fields can be chosen!") raise ValidationError(msg) def _apply_options(self, node): """Applies options ``self`` to ``node``""" self.ensure_one() options = node.attrib.get("options") or {} if isinstance(options, str): options = safe_eval(options, dict(self.env.context or [])) or {} for k in ("create", "create_edit"): opt = self["option_%s" % k] if opt == "none": continue mode, val = opt.split("_") if mode == "force" or k not in options: options[k] = val == "true" node.set("options", str(options)) if not self.option_create_edit_wizard: node.set("can_create", "false") node.set("can_write", "false") @api.model def get(self, model_name, field_name): """Returns specific record for ``field_name`` in ``model_name`` :param str model_name: technical model name (i.e. "sale.order") :param str field_name: technical field name (i.e. "partner_id") """ return self.browse(self._get(model_name, field_name)) @api.model @ormcache("model_name", "field_name") def _get(self, model_name, field_name): """Inner implementation of ``get``. An ID is returned to allow caching (see :class:`ormcache`); :meth:`get` will then convert it to a proper record. :param str model_name: technical model name (i.e. "sale.order") :param str field_name: technical field name (i.e. "partner_id") """ dom = [ ("model_name", "=", model_name), ("field_name", "=", field_name), ] # `_check_field_model_uniqueness()` grants uniqueness if existing return self.search(dom, limit=1).id