[IMP] base: rework the ir.actions.server views

- inline configuration/display for 'update record' action type
- don't use notebook when there's a single page that gets displayed
- remove default field when updating a record

Task-3450200

Part-of: odoo/odoo#138804
This commit is contained in:
Damien Bouvy
2023-10-26 13:41:07 +00:00
parent e17f06baea
commit 7be6d6e321
3 changed files with 73 additions and 89 deletions
@@ -122,7 +122,7 @@
name="action_server_ids"
widget="one2many"
class="o_base_automation_actions_field"
context="{'default_model_id': model_id, 'default_state': False, 'form_view_ref': 'base_automation.ir_actions_server_view_form_automation'}"
context="{'default_model_id': model_id, 'form_view_ref': 'base_automation.ir_actions_server_view_form_automation'}"
>
<kanban>
<control>
+3 -22
View File
@@ -439,25 +439,6 @@ class IrActionsServer(models.Model):
# - Command: x2many commands namespace
# To return an action, assign: action = {...}\n\n\n\n"""
@api.model
def _default_update_field_id(self):
if self.model_id:
model_id = self.model_id.id
elif 'default_model_id' in self.env.context:
model_id = self.env.context['default_model_id']
else:
return False
ir_model = self.env["ir.model"].browse(model_id)
if ir_model:
fields = self.env[ir_model.model]._fields
if 'state' in fields:
return ir_model.field_id.filtered(lambda ir_field: ir_field.name == 'state')
elif 'stage_id' in fields:
return ir_model.field_id.filtered(lambda ir_field: ir_field.name == 'stage_id')
elif 'priority' in fields:
return ir_model.field_id.filtered(lambda ir_field: ir_field.name == 'priority')
name = fields.Char(compute='_compute_name', store=True, readonly=False, required=True)
type = fields.Char(default='ir.actions.server')
usage = fields.Selection([
@@ -508,7 +489,7 @@ class IrActionsServer(models.Model):
groups_id = fields.Many2many('res.groups', 'ir_act_server_group_rel',
'act_id', 'gid', string='Allowed Groups', help='Groups that can execute the server action. Leave empty to allow everybody.')
update_field_id = fields.Many2one('ir.model.fields', string='Field to Update', default=_default_update_field_id, ondelete='cascade')
update_field_id = fields.Many2one('ir.model.fields', string='Field to Update', ondelete='cascade')
update_related_model_id = fields.Many2one('ir.model', compute='_compute_update_related_model_id')
value = fields.Text(help="For Python expressions, this field may hold a Python expression "
@@ -518,8 +499,8 @@ class IrActionsServer(models.Model):
"For Static values, the value will be used directly without evaluation, e.g."
"`42` or `My custom name` or the selected record.")
evaluation_type = fields.Selection([
('value', 'Custom Value'),
('equation', 'Python expression')
('value', 'Update'),
('equation', 'Compute')
], 'Value Type', default='value', change_default=True)
resource_ref = fields.Reference(
string='Record', selection='_selection_target_model', inverse='_set_resource_ref')
+69 -66
View File
@@ -331,27 +331,52 @@
</h1>
<div class="d-flex flex-column">
<label for="state"/>
<field name="state" widget="selection_badge"/>
<field name="state" widget="selection_badge" options="{'size': 'sm'}"/>
</div>
<group string="Technical Settings" groups="base.group_no_one">
<group>
<field name="model_id" options="{'no_create': True, 'no_open': True}" />
<field name="groups_id" widget="many2many_tags" />
</group>
</group>
<separator string="Action Details" groups="base.group_no_one" />
<div class="d-flex flex-row flex-wrap gap-2" invisible="state != 'object_write'">
<field name="evaluation_type" class="oe_inline"/>
<field name="update_field_id"
placeholder="Select a field..."
options="{'no_create': True, 'no_open': True}" context="{'hide_model': True}"
domain="[('store', '=', True), '|', ('model_id', '=', crud_model_id), ('model_id', '=', model_id)]"/>
<field name="value_field_to_show" invisible="1"/>
<field name="update_related_model_id" invisible="1"/>
<span invisible="evaluation_type != 'value'">to</span>
<field name="value" class="oe_inline" placeholder="Set a value..." invisible="update_field_id == False or value_field_to_show != 'value' or evaluation_type != 'value'" string="Custom Value"/>
<field name="resource_ref" class="oe_inline" placeholder="Choose a value..." string="Custom Value" options="{'model_field': 'update_related_model_id', 'no_create': True, 'no_open': True}" invisible=" update_field_id == False or value_field_to_show != 'resource_ref' or evaluation_type == 'equation'"/>
<field name="selection_value" class="oe_inline" placeholder="Choose a value..." options="{'no_create': True, 'no_open': True}" invisible=" update_field_id == False or value_field_to_show != 'selection_value' or evaluation_type == 'equation'"/>
<span invisible="update_field_id != False or evaluation_type == 'equation'" class="text-muted">Set a value...</span>
<span invisible="evaluation_type != 'equation'">
to this Python expression:
</span>
<div class="w-100 d-flex flex-row gap-3" invisible="evaluation_type != 'equation'">
<field name="value" class="w-50" widget="code" options="{'mode': 'python'}" string="Expression"/>
<div class="w-50 opacity-75">
<span colspan="2" class="text-muted"> Available variables: </span>
<ul colspan="2" class="text-muted">
<li><code>env</code>: environment on which the action is triggered</li>
<li><code>model</code>: model of the record on which the action is triggered; is a void recordset</li>
<li><code>record</code>: record on which the action is triggered</li>
<li><code>records</code>: recordset of all records on which the action is triggered in multi mode</li>
<li><code>time</code>, <code>datetime</code>, <code>dateutil</code>, <code>timezone</code>: useful Python libraries</li>
<li><code>float_compare()</code>: utility function to compare floats based on a specific precision</li>
</ul>
</div>
</div>
</div>
<group name="action_wrapper">
<group name="action_content">
<field name="model_name" invisible="1"/>
<field name="available_model_ids" invisible="1"/>
<field name="model_id" options="{'no_create': True, 'no_open': True}" groups="base.group_no_one"
domain="[('id', 'in', available_model_ids)]"/>
<field name="model_id" options="{'no_create': True, 'no_open': True}" groups="!base.group_no_one" invisible="context.get('default_model_id')"
domain="[('id', 'in', available_model_ids)]"/>
<field name="groups_id" widget="many2many_tags" groups="base.group_no_one" />
<field name="update_field_id" invisible="state != 'object_write'"
options="{'no_create': True, 'no_open': True}" context="{'hide_model': True}"/>
domain="['|', ('model_id', '=', crud_model_id), ('model_id', '=', model_id)]"/>
<field name="evaluation_type" invisible="state != 'object_write'" widget="radio" options="{'horizontal': True}"/>
<field name="value_field_to_show" invisible="1"/>
<field name="update_related_model_id" invisible="1"/>
<field name="value" invisible="state != 'object_write' or value_field_to_show != 'value' or evaluation_type != 'value'" string="Custom Value"/>
<field name="value" widget="code" options="{'mode': 'python'}" invisible="state != 'object_write' or evaluation_type != 'equation'" string="Expression"/>
<field name="resource_ref" string="Custom Value" options="{'model_field': 'update_related_model_id', 'no_create': True, 'no_open': True}" invisible="state != 'object_write' or value_field_to_show != 'resource_ref' or evaluation_type == 'equation'"/>
<field name="selection_value" options="{'no_create': True, 'no_open': True}" invisible="state != 'object_write' or value_field_to_show != 'selection_value' or evaluation_type == 'equation'"/>
<field name="type" invisible="1"/>
<field name="crud_model_id"
@@ -367,61 +392,39 @@
invisible="state != 'object_create'"
context="{'default_model_id': model_id, 'default_relation': crud_model_name}"/>
</group>
<group invisible="evaluation_type != 'equation' or state != 'object_write'">
<span colspan="2" class="text-muted"> Available variables: </span>
<ul colspan="2" class="text-muted">
<li><code>env</code>: environment on which the action is triggered</li>
<li><code>model</code>: model of the record on which the action is triggered; is a void recordset</li>
<li><code>record</code>: record on which the action is triggered</li>
<li><code>records</code>: recordset of all records on which the action is triggered in multi mode</li>
<li><code>time</code>, <code>datetime</code>, <code>dateutil</code>, <code>timezone</code>: useful Python libraries</li>
<li><code>float_compare()</code>: utility function to compare floats based on a specific precision</li>
</ul>
</group>
</group>
<notebook>
<page string="Python Code" name='code'
invisible="state != 'code'">
<field name="code" widget="code" options="{'mode': 'python'}" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
</page>
<page string="Actions" name="actions"
invisible="state != 'multi'">
<field name="child_ids" domain="[('model_id', '=', model_id)]"/>
<p class="oe_grey">
If several child actions return an action, only the last one will be executed.
This may happen when having server actions executing code that returns an action, or server actions returning a client action.
</p>
</page>
<page string="Help" name="help_info"
invisible="state != 'code'">
<div style="margin-top: 4px;">
<h3>Help with Python expressions</h3>
<p>Various fields may use Python code or Python expressions. The following variables can be used:</p>
<ul>
<li><code>env</code>: environment on which the action is triggered</li>
<li><code>model</code>: model of the record on which the action is triggered; is a void recordset</li>
<li><code>record</code>: record on which the action is triggered; may be be void</li>
<li><code>records</code>: recordset of all records on which the action is triggered in multi mode; may be void</li>
<li><code>time</code>, <code>datetime</code>, <code>dateutil</code>, <code>timezone</code>: useful Python libraries</li>
<li><code>float_compare()</code>: utility function to compare floats based on a specific precision</li>
<li><code>log(message, level='info')</code>: logging function to record debug information in <code>ir.logging</code> table</li>
<li><code>_logger.info(message)</code>: logger to emit messages in server logs</li>
<li><code>UserError</code>: exception class for raising user-facing warning messages</li>
<li><code>Command</code>: x2many commands namespace</li>
<li>To return an action, assign: <code>action = {...}</code></li>
</ul>
<div invisible="state != 'code'">
<p>Example of Python code</p>
<pre style='white-space: pre-wrap'>
partner_name = record.name + '_code'
<field name="child_ids" domain="[('model_id', '=', model_id)]" invisible="state != 'multi'"/>
<p class="oe_grey" invisible="state != 'multi'">
If several child actions return an action, only the last one will be executed.
This may happen when having server actions executing code that returns an action, or server actions returning a client action.
</p>
<label for="code" invisible="state != 'code'"/>
<field name="code" invisible="state != 'code'" widget="code" options="{'mode': 'python'}" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
<div class="mb-3 opacity-75" invisible="state != 'code'">
<button class="btn btn-sm btn-secondary" data-bs-toggle="collapse" data-bs-target="#code-help-collapse" aria-expanded="false" aria-controls="code-help-collapse">Available variables and libraries</button>
<div class="collapse ps-3" id="code-help-collapse">
<p>The following variables can be used:</p>
<ul>
<li><code>env</code>: environment on which the action is triggered</li>
<li><code>model</code>: model of the record on which the action is triggered; is a void recordset</li>
<li><code>record</code>: record on which the action is triggered; may be be void</li>
<li><code>records</code>: recordset of all records on which the action is triggered in multi mode; may be void</li>
<li><code>time</code>, <code>datetime</code>, <code>dateutil</code>, <code>timezone</code>: useful Python libraries</li>
<li><code>float_compare()</code>: utility function to compare floats based on a specific precision</li>
<li><code>log(message, level='info')</code>: logging function to record debug information in <code>ir.logging</code> table</li>
<li><code>_logger.info(message)</code>: logger to emit messages in server logs</li>
<li><code>UserError</code>: exception class for raising user-facing warning messages</li>
<li><code>Command</code>: x2many commands namespace</li>
<li>To return an action, assign: <code>action = {...}</code></li>
</ul>
<div >
<p>Example of Python code:</p>
<pre style='white-space: pre-wrap'>partner_name = record.name + '_code'
env['res.partner'].create({'name': partner_name})
</pre>
</div>
</pre>
</div>
</page>
</notebook>
</div>
</div>
</sheet>
</form>
</field>