mirror of
https://gitlab.com/flectra-community/server-ux.git
synced 2024-11-15 10:42:08 +00:00
197 lines
6.8 KiB
Python
197 lines
6.8 KiB
Python
|
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||
|
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||
|
|
||
|
from flectra import _, api, exceptions, fields, models
|
||
|
from flectra.tools.safe_eval import safe_eval
|
||
|
|
||
|
|
||
|
class ChainedSwapper(models.Model):
|
||
|
_name = "chained.swapper"
|
||
|
_description = "Chained Swapper"
|
||
|
|
||
|
name = fields.Char(required=True, translate=True, index=1)
|
||
|
model_id = fields.Many2one(
|
||
|
comodel_name="ir.model",
|
||
|
required=True,
|
||
|
ondelete="cascade",
|
||
|
help="Model is used for Selecting Field. This is editable "
|
||
|
"until Contextual Action is not created.",
|
||
|
)
|
||
|
allowed_field_ids = fields.Many2many(
|
||
|
comodel_name="ir.model.fields", compute="_compute_allowed_field_ids"
|
||
|
)
|
||
|
field_id = fields.Many2one(
|
||
|
comodel_name="ir.model.fields",
|
||
|
required=True,
|
||
|
ondelete="cascade",
|
||
|
domain="[('id', 'in', allowed_field_ids)]",
|
||
|
)
|
||
|
sub_field_ids = fields.One2many(
|
||
|
comodel_name="chained.swapper.sub.field",
|
||
|
inverse_name="chained_swapper_id",
|
||
|
string="Sub-fields",
|
||
|
)
|
||
|
constraint_ids = fields.One2many(
|
||
|
comodel_name="chained.swapper.constraint",
|
||
|
inverse_name="chained_swapper_id",
|
||
|
string="Constraints",
|
||
|
)
|
||
|
ref_ir_act_window_id = fields.Many2one(
|
||
|
comodel_name="ir.actions.act_window",
|
||
|
string="Action",
|
||
|
readonly=True,
|
||
|
help="Action to make this template available on records "
|
||
|
"of the related document model.",
|
||
|
)
|
||
|
group_ids = fields.Many2many(
|
||
|
comodel_name="res.groups",
|
||
|
relation="mass_group_rel",
|
||
|
column1="mass_id",
|
||
|
column2="group_id",
|
||
|
string="Groups",
|
||
|
)
|
||
|
|
||
|
_sql_constraints = [
|
||
|
(
|
||
|
"model_id_field_id_unique",
|
||
|
"unique (model_id, field_id)",
|
||
|
"Model and Field must be unique!",
|
||
|
),
|
||
|
]
|
||
|
|
||
|
@api.depends("model_id")
|
||
|
def _compute_allowed_field_ids(self):
|
||
|
model_obj = self.env["ir.model"]
|
||
|
field_obj = self.env["ir.model.fields"]
|
||
|
for record in self:
|
||
|
allowed_field_ids = False
|
||
|
if record.model_id:
|
||
|
all_models = record.model_id
|
||
|
active_model_obj = self.env[record.model_id.model]
|
||
|
if active_model_obj._inherits:
|
||
|
keys = list(active_model_obj._inherits.keys())
|
||
|
all_models |= model_obj.search([("model", "in", keys)])
|
||
|
allowed_field_ids = field_obj.search(
|
||
|
[
|
||
|
("ttype", "not in", ["reference", "function", "one2many"]),
|
||
|
("model_id", "in", all_models.ids),
|
||
|
]
|
||
|
)
|
||
|
record.allowed_field_ids = allowed_field_ids
|
||
|
|
||
|
@api.constrains("model_id", "field_id")
|
||
|
def _check_sub_field_ids(self):
|
||
|
self.mapped("sub_field_ids")._check_sub_field_chain()
|
||
|
|
||
|
@api.onchange("model_id")
|
||
|
def _onchange_model_id(self):
|
||
|
self.field_id = False
|
||
|
|
||
|
def write(self, vals):
|
||
|
res = super().write(vals)
|
||
|
if "name" in vals:
|
||
|
self.mapped("ref_ir_act_window_id").write({"name": vals["name"]})
|
||
|
return res
|
||
|
|
||
|
def unlink(self):
|
||
|
self.unlink_action()
|
||
|
return super().unlink()
|
||
|
|
||
|
def add_action(self):
|
||
|
self.ensure_one()
|
||
|
action = self.env["ir.actions.act_window"].create(
|
||
|
{
|
||
|
"name": _("Chained swap") + ": " + self.name,
|
||
|
"type": "ir.actions.act_window",
|
||
|
"res_model": "chained.swapper.wizard",
|
||
|
"groups_id": [(4, x.id) for x in self.group_ids],
|
||
|
"context": "{'chained_swapper_id': %d}" % (self.id),
|
||
|
"view_mode": "form",
|
||
|
"target": "new",
|
||
|
"binding_model_id": self.model_id.id,
|
||
|
"binding_type": "action",
|
||
|
}
|
||
|
)
|
||
|
self.write({"ref_ir_act_window_id": action.id})
|
||
|
return True
|
||
|
|
||
|
def unlink_action(self):
|
||
|
self.mapped("ref_ir_act_window_id").unlink()
|
||
|
return True
|
||
|
|
||
|
|
||
|
class ChainedSwapperSubField(models.Model):
|
||
|
_name = "chained.swapper.sub.field"
|
||
|
_description = "Chained Swapper Sub-field"
|
||
|
|
||
|
chained_swapper_id = fields.Many2one(
|
||
|
comodel_name="chained.swapper", ondelete="cascade"
|
||
|
)
|
||
|
sub_field_chain = fields.Char(
|
||
|
required=True,
|
||
|
help="You can specify here a field of related fields as "
|
||
|
"dotted names. Ex.: 'child_ids.lang'.",
|
||
|
)
|
||
|
|
||
|
@api.constrains("chained_swapper_id", "sub_field_chain")
|
||
|
def _check_sub_field_chain(self):
|
||
|
for rec in self:
|
||
|
# Check sub-field exist
|
||
|
try:
|
||
|
chain_list = rec.sub_field_chain.split(".")
|
||
|
chain_field_name = chain_list.pop()
|
||
|
chain_model = self.env[rec.chained_swapper_id.model_id.model]
|
||
|
for name in chain_list:
|
||
|
chain_model = chain_model[name]
|
||
|
chain_model[chain_field_name] # pylint: disable=W0104
|
||
|
except KeyError:
|
||
|
raise exceptions.ValidationError(
|
||
|
_("Incorrect sub-field expression:") + " " + rec.sub_field_chain
|
||
|
)
|
||
|
# Check sub-field and original field are the same type
|
||
|
swap_field = rec.chained_swapper_id.field_id
|
||
|
chain_field = self.env["ir.model.fields"].search(
|
||
|
[
|
||
|
("model_id", "=", rec.chained_swapper_id.model_id.id),
|
||
|
("name", "=", chain_field_name),
|
||
|
]
|
||
|
)
|
||
|
if (
|
||
|
chain_field.ttype != swap_field.ttype
|
||
|
or chain_field.relation != swap_field.relation
|
||
|
):
|
||
|
raise exceptions.ValidationError(
|
||
|
_("The sub-field '%s' is not compatible with the main" " field.")
|
||
|
% rec.sub_field_chain
|
||
|
)
|
||
|
|
||
|
|
||
|
class ChainedSwapperConstraint(models.Model):
|
||
|
_name = "chained.swapper.constraint"
|
||
|
_description = "Chained Swapper Constraint"
|
||
|
|
||
|
chained_swapper_id = fields.Many2one(
|
||
|
comodel_name="chained.swapper", ondelete="cascade"
|
||
|
)
|
||
|
name = fields.Char(required=True, translate=True)
|
||
|
expression = fields.Text(
|
||
|
string="Constraint expression",
|
||
|
required=True,
|
||
|
help="Boolean python expression. You can use the keyword "
|
||
|
"'records' as the records selected to execute the "
|
||
|
"contextual action. Ex.: bool(records.mapped('parent_id'))",
|
||
|
default="True",
|
||
|
)
|
||
|
|
||
|
@api.constrains("expression")
|
||
|
def _check_expression(self):
|
||
|
for record in self:
|
||
|
model = self.env[record.chained_swapper_id.model_id.model]
|
||
|
try:
|
||
|
safe_eval(record.expression, {"records": model})
|
||
|
except Exception:
|
||
|
raise exceptions.ValidationError(
|
||
|
_("Invalid constraint expression:" + " " + record.expression)
|
||
|
)
|