[REF] ir_actions: refactor and simplify ir.actions.server
Main idea of this commit is to simplify the various options of ir.actions.server model. Purpose is to make it a simple tool to execute some actions or python code. This has two main consequences * create and update server actions are simplified to update only the current record. Complex options can always be achieved by python code instead of error-prone option building mechanism; * condition of server actions is removed. If a condition is necessary it can either be included in a code server action, or integrated in an automated action; This commit features : * remove 'client_action' server action type. Indeed this can be achieved with a code server action returning an action; * remove 'condition' on ir_actions_server model. From now on conditions and triggers are managed in server action code or using automated or scheduled action; * clean 'create and copy a record'. Create and link are the proposed options. Copy of the record or of a selected record is removed. Link is simplified by removing the parsed expression that allowed complex path update. It is simpler to write code than write python-like expression in char fields; * remove 'use_write' options. It now simply updates a record; * remove 'write_expression' as it is replaced by code actions; * remove 'ref_object' not used anymore with the removal of "choose a record"; * remove helper to build an expression not used anymore with the removal of "write expression"; * remove object ID finder; * removed template display when choosing an email template; just go on the template if you want more details; * refactor server action form view to reflect changes. It will be used in future commits as base view for automated and scheduled actions; * add 'sequence' in list view; * add new 'usage' selection field allowing to distinguish which model uses the server action. Indeed as server action will soon be used in scheduled or automated actions having a clue about the usage is always helpful; Thanks to @fpodoo for the original idea and preliminary work. Thanks to @jpr-odoo for first developments. Thanks to @jem-odoo and @rco-odoo for reviewing.
This commit is contained in:
committed by
Thibault Delavallée
parent
452a97d9d4
commit
8fa9f7d6d4
@@ -11,7 +11,6 @@
|
||||
<record id="action_email_reminder_lead" model="ir.actions.server">
|
||||
<field name="name">Reminder to User</field>
|
||||
<field name="model_id" ref="model_crm_lead"/>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">email</field>
|
||||
<field name="template_id" ref="email_template_opportunity_reminder_mail"/>
|
||||
@@ -38,7 +37,6 @@
|
||||
<record id="action_set_team_sales_department" model="ir.actions.server">
|
||||
<field name="name">Set team to Sales Department</field>
|
||||
<field name="model_id" ref="model_crm_lead"/>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">record.write({'team_id': env.ref('sales_team.team_sales_department').id})</field>
|
||||
|
||||
@@ -774,7 +774,6 @@ if record:
|
||||
<field name="model_id" ref="crm.model_crm_team"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.action_your_pipeline()</field>
|
||||
<field eval="True" name="condition"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="crm_lead_opportunities_tree_view">
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
<record id="contract_set_as_pending" model="ir.actions.server">
|
||||
<field name="name">HR Contract: set as pending</field>
|
||||
<field name="model_id" ref="model_hr_contract"/>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">record.set_as_pending()</field>
|
||||
@@ -82,7 +81,6 @@
|
||||
<record id="contract_set_as_close" model="ir.actions.server">
|
||||
<field name="name">HR Contract: set as close</field>
|
||||
<field name="model_id" ref="model_hr_contract"/>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">record.set_as_close()</field>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_hr_timesheet_current_open"/>
|
||||
<field name="code">action = model.open_timesheet()</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="name">My Timesheet</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -415,7 +415,6 @@
|
||||
|
||||
<record id="action_server_lunch_archive_product" model="ir.actions.server">
|
||||
<field name="name">Archive/Restore</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_lunch_product" />
|
||||
<field name="state">code</field>
|
||||
|
||||
@@ -11,11 +11,6 @@ class ServerActions(models.Model):
|
||||
_inherit = ['ir.actions.server']
|
||||
|
||||
state = fields.Selection(selection_add=[('email', 'Send Email')])
|
||||
email_from = fields.Char('From', related='template_id.email_from', readonly=True)
|
||||
email_to = fields.Char('To (Emails)', related='template_id.email_to', readonly=True)
|
||||
partner_to = fields.Char('To (Partners)', related='template_id.partner_to', readonly=True)
|
||||
subject = fields.Char('Subject', related='template_id.subject', readonly=True)
|
||||
body_html = fields.Html('Body', related='template_id.body_html', readonly=True)
|
||||
template_id = fields.Many2one(
|
||||
'mail.template', 'Email Template', ondelete='set null',
|
||||
domain="[('model_id', '=', model_id)]",
|
||||
|
||||
@@ -16,27 +16,6 @@
|
||||
<group attrs="{'invisible': [('model_id', '=', False)]}">
|
||||
<field name="template_id"
|
||||
attrs="{'required': [('state', '=', 'email')]}"/>
|
||||
<p colspan="2" attrs="{'invisible': [('template_id', '!=', False)]}">
|
||||
Choose a template to display its values.
|
||||
</p>
|
||||
<p colspan="2" attrs="{'invisible': [('template_id', '=', False)]}">
|
||||
The values displayed hereunder are informative. When sending the email, the values
|
||||
will be taken from the email template.
|
||||
</p>
|
||||
</group>
|
||||
<group attrs="{'invisible': ['|', ('model_id', '=', False), ('template_id', '=', False)]}">
|
||||
<label for="email_from"/>
|
||||
<div>
|
||||
<field name="email_from" nolabel="1'" readonly="1"
|
||||
attrs="{'required': [('state', '=', 'email')]}"/>
|
||||
<p attrs="{'invisible': [('email_from', '!=', False)]}">
|
||||
Your template does not defined the email address of the sender. Please update your template.
|
||||
</p>
|
||||
</div>
|
||||
<field name="email_to" readonly="1"/>
|
||||
<field name="partner_to" readonly="1"/>
|
||||
<field name="subject" readonly="1" attrs="{'required': [('state', '=', 'email')]}"/>
|
||||
<field name="body_html" readonly="1" attrs="{'required': [('state', '=', 'email')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<field name="state">code</field>
|
||||
<field name="code">True</field>
|
||||
<field name="sequence" eval="5"/>
|
||||
<field name="condition" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Email template -->
|
||||
|
||||
@@ -635,7 +635,6 @@
|
||||
|
||||
<record id="survey_action_server_clean_test_answers" model="ir.actions.server">
|
||||
<field name="name">Clean test answers</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_survey_survey" />
|
||||
<field name="state">code</field>
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
|
||||
<record id="action_partner_post" model="ir.actions.server">
|
||||
<field name="name">Website Partner Post and Thanks Demo</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_path">partner_thanks</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
@@ -112,7 +111,6 @@ response = request.website.render("website.template_partner_post", {'partner': p
|
||||
|
||||
<record id="action_partner_comment" model="ir.actions.server">
|
||||
<field name="name">Website Partners Comment Form</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_path">partner_comment</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
<xpath expr="//field[@name='state']" position="after">
|
||||
<field name="website_published"
|
||||
attrs="{'invisible': [('state', '!=', 'code')]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='sequence']" position="after">
|
||||
<field name="xml_id" invisible="1"/>
|
||||
<field name="website_path"
|
||||
attrs="{'invisible': ['|', ('website_published', '!=', True), ('state', '!=', 'code')]}"/>
|
||||
|
||||
@@ -457,23 +457,17 @@ class IrActionsServer(models.Model):
|
||||
|
||||
name = fields.Char(string='Action Name', translate=True)
|
||||
type = fields.Char(default='ir.actions.server')
|
||||
|
||||
condition = fields.Char(default="True", groups='base.group_system',
|
||||
help="Condition verified before executing the server action. If it "
|
||||
"is not verified, the action will not be executed. The condition is "
|
||||
"a Python expression, like 'record.list_price > 5000'. A void "
|
||||
"condition is considered as always True. Help about python expression "
|
||||
"is given in the help tab.")
|
||||
usage = fields.Selection([
|
||||
('ir_actions_server', 'Server Action')], string='Usage',
|
||||
default='ir_actions_server', required=True)
|
||||
state = fields.Selection([
|
||||
('code', 'Execute Python Code'),
|
||||
('client_action', 'Run a Client Action'),
|
||||
('object_create', 'Create or Copy a new Record'),
|
||||
('object_write', 'Write on a Record'),
|
||||
('object_create', 'Create a new Record'),
|
||||
('object_write', 'Update the Record'),
|
||||
('multi', 'Execute several actions')], string='Action To Do',
|
||||
default='code', required=True, # set default to write ?
|
||||
help="Type of server action. The following values are available:\n"
|
||||
"- 'Execute Python Code': a block of python code that will be executed\n"
|
||||
"- 'Run a Client Action': choose a client action to launch\n"
|
||||
"- 'Create or Copy a new Record': create a new record with new values, or copy an existing record in your database\n"
|
||||
"- 'Write on a Record': update the values of a record\n"
|
||||
"- 'Execute several actions': define an action that triggers several other server actions\n"
|
||||
@@ -488,9 +482,6 @@ class IrActionsServer(models.Model):
|
||||
menu_ir_values_id = fields.Many2one('ir.values', string='Action on Object',
|
||||
copy=False, readonly=True,
|
||||
help='IrValues entry of the related more menu entry action')
|
||||
# Client Action
|
||||
action_id = fields.Many2one('ir.actions.actions', string='Client Action',
|
||||
help="Select the client action that has to be executed.")
|
||||
# Python code
|
||||
code = fields.Text(string='Python Code', groups='base.group_system',
|
||||
default=DEFAULT_PYTHON_CODE,
|
||||
@@ -499,79 +490,15 @@ class IrActionsServer(models.Model):
|
||||
# Multi
|
||||
child_ids = fields.Many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id',
|
||||
string='Child Actions', help='Child server actions that will be executed. Note that the last return returned action value will be used as global return value.')
|
||||
# Create/Copy/Write
|
||||
use_create = fields.Selection([('new', 'Create a new record in the Base Model'),
|
||||
('new_other', 'Create a new record in another model'),
|
||||
('copy_current', 'Copy the current record'),
|
||||
('copy_other', 'Choose and copy a record in the database')],
|
||||
string="Creation Policy", default='new', required=True)
|
||||
# Create
|
||||
crud_model_id = fields.Many2one('ir.model', string='Create/Write Target Model',
|
||||
oldname='srcmodel_id', help="Model for record creation / update. Set this field only to specify a different model than the base model.")
|
||||
crud_model_name = fields.Char(string='Create/Write Target Model Name', related='crud_model_id.model', store=True, readonly=True)
|
||||
ref_object = fields.Reference(string='Reference record', selection='_select_objects', oldname='copy_object')
|
||||
link_new_record = fields.Boolean(string='Attach the new record',
|
||||
help="Check this if you want to link the newly-created record "
|
||||
"to the current record on which the server action runs.")
|
||||
crud_model_name = fields.Char(related='crud_model_id.name', readonly=True)
|
||||
link_field_id = fields.Many2one('ir.model.fields', string='Link using field',
|
||||
oldname='record_id', help="Provide the field where the record id is stored after the operations.")
|
||||
use_write = fields.Selection([('current', 'Update the current record'),
|
||||
('expression', 'Update a record linked to the current record using python'),
|
||||
('other', 'Choose and Update a record in the database')],
|
||||
string='Update Policy', default='current', required=True)
|
||||
write_expression = fields.Char(string='Expression', oldname='write_id',
|
||||
help="Provide an expression that, applied on the current record, gives the field to update.")
|
||||
help="Provide the field used to link the newly created record"
|
||||
"on the record on used by the server action.")
|
||||
fields_lines = fields.One2many('ir.server.object.lines', 'server_id', string='Value Mapping', copy=True)
|
||||
|
||||
# Fake fields used to implement the placeholder assistant
|
||||
model_object_field = fields.Many2one('ir.model.fields', string="Field",
|
||||
help="Select target field from the related document model.\n"
|
||||
"If it is a relationship field you will be able to select "
|
||||
"a target field at the destination of the relationship.")
|
||||
sub_object = fields.Many2one('ir.model', string='Sub-model', readonly=True,
|
||||
help="fWhen a relationship field is selected as first field, "
|
||||
"this field shows the document model the relationship goes to.")
|
||||
sub_model_object_field = fields.Many2one('ir.model.fields', string='Sub-field',
|
||||
help="When a relationship field is selected as first field, "
|
||||
"this field lets you select the target field within the "
|
||||
"destination document model (sub-model).")
|
||||
copyvalue = fields.Char(string='Placeholder Expression', help="Final placeholder expression, to be copy-pasted in the desired template field.")
|
||||
# Fake fields used to implement the ID finding assistant
|
||||
id_object = fields.Reference(string='Record', selection='_select_objects')
|
||||
id_value = fields.Char(string='Record ID')
|
||||
|
||||
def _check_expression(self, expression, model):
|
||||
""" Check python expression (condition, write_expression). Each step of
|
||||
the path must be a valid many2one field, or an integer field for the last
|
||||
step.
|
||||
|
||||
:param str expression: a python expression, beginning by 'obj' or 'object'
|
||||
:param model: a record of the model 'ir.model'
|
||||
:returns tuple: (is_valid, target_model_name, error_msg)
|
||||
"""
|
||||
if not model:
|
||||
return (False, None, 'Your expression cannot be validated because the Base Model is not set.')
|
||||
# fetch current model
|
||||
model_name = model.model
|
||||
# transform expression into a path that should look like 'object.many2onefield.many2onefield'
|
||||
path = expression.split('.')
|
||||
initial = path.pop(0)
|
||||
if initial not in ['obj', 'object', 'record']:
|
||||
return (False, None, 'Your expression should begin with obj, object, record.\nAn expression builder is available in the help tab.')
|
||||
# analyze path
|
||||
while path:
|
||||
step = path.pop(0)
|
||||
field = self.env[model_name]._fields.get(step)
|
||||
if not field:
|
||||
return (False, None, 'Part of the expression (%s) is not recognized as a column in the model %s.' % (step, model_name))
|
||||
ftype = field.type
|
||||
if ftype not in ['many2one', 'int']:
|
||||
return (False, None, 'Part of the expression (%s) is not a valid column type (is %s, should be a many2one or an int)' % (step, ftype))
|
||||
if ftype == 'int' and path:
|
||||
return (False, None, 'Part of the expression (%s) is an integer field that is only allowed at the end of an expression' % (step))
|
||||
if ftype == 'many2one':
|
||||
model_name = field.comodel_name
|
||||
return (True, model_name, None)
|
||||
|
||||
@api.constrains('code')
|
||||
def _check_python_code(self):
|
||||
for action in self.sudo().filtered('code'):
|
||||
@@ -579,142 +506,19 @@ class IrActionsServer(models.Model):
|
||||
if msg:
|
||||
raise ValidationError(msg)
|
||||
|
||||
@api.constrains('write_expression', 'model_id')
|
||||
def _check_write_expression(self):
|
||||
for record in self:
|
||||
if record.write_expression and record.model_id:
|
||||
correct, model_name, message = self._check_expression(record.write_expression, record.model_id)
|
||||
if not correct:
|
||||
_logger.warning('Invalid expression: %s' % message)
|
||||
raise ValidationError(_('Incorrect Write Record Expression'))
|
||||
|
||||
@api.constrains('child_ids')
|
||||
def _check_recursion(self):
|
||||
if not self._check_m2m_recursion('child_ids'):
|
||||
raise ValidationError(_('Recursion found in child server actions'))
|
||||
|
||||
@api.onchange('model_id')
|
||||
def _onchange_model_id(self):
|
||||
""" When changing the action base model, reset crud config
|
||||
to ease value coherence. """
|
||||
self.use_create = 'new'
|
||||
self.use_write = 'current'
|
||||
self.crud_model_id = self.model_id
|
||||
|
||||
@api.onchange('use_create', 'use_write', 'ref_object')
|
||||
def _onchange_crud_config(self):
|
||||
""" Wrapper on CRUD-type (create or write) on_change """
|
||||
if self.state == 'object_create':
|
||||
self._onchange_create_config()
|
||||
elif self.state == 'object_write':
|
||||
self._onchange_write_config()
|
||||
|
||||
def _onchange_create_config(self):
|
||||
""" When changing the object_create type configuration:
|
||||
|
||||
- `new` and `copy_current`: crud_model_id is the same as base model
|
||||
- `new_other`: user choose crud_model_id
|
||||
- `copy_other`: disassemble the reference object to have its model
|
||||
- if the target model has changed, then reset the link field that is
|
||||
probably not correct anymore
|
||||
"""
|
||||
crud_model_id = self.crud_model_id
|
||||
|
||||
if self.use_create == 'new':
|
||||
self.crud_model_id = self.model_id
|
||||
elif self.use_create == 'new_other':
|
||||
pass
|
||||
elif self.use_create == 'copy_current':
|
||||
self.crud_model_id = self.model_id
|
||||
elif self.use_create == 'copy_other' and self.ref_object:
|
||||
ref_model = self.ref_object._name
|
||||
self.crud_model_id = self.env['ir.model'].search([('model', '=', ref_model)])
|
||||
|
||||
if self.crud_model_id != crud_model_id:
|
||||
self.link_field_id = False
|
||||
|
||||
def _onchange_write_config(self):
|
||||
""" When changing the object_write type configuration:
|
||||
|
||||
- `current`: crud_model_id is the same as base model
|
||||
- `other`: disassemble the reference object to have its model
|
||||
- `expression`: has its own on_change, nothing special here
|
||||
"""
|
||||
crud_model_id = self.crud_model_id
|
||||
|
||||
if self.use_write == 'current':
|
||||
self.crud_model_id = self.model_id
|
||||
elif self.use_write == 'other' and self.ref_object:
|
||||
ref_model = self.ref_object._name
|
||||
self.crud_model_id = self.env['ir.model'].search([('model', '=', ref_model)])
|
||||
elif self.use_write == 'expression':
|
||||
pass
|
||||
|
||||
if self.crud_model_id != crud_model_id:
|
||||
self.link_field_id = False
|
||||
|
||||
@api.onchange('crud_model_id')
|
||||
def _onchange_crud_model_id(self):
|
||||
""" When changing the CRUD model, update its stored name also """
|
||||
self.link_field_id = False
|
||||
self.crud_model_name = self.crud_model_id.model
|
||||
|
||||
@api.onchange('write_expression')
|
||||
def _onchange_write_expression(self):
|
||||
""" Check the write_expression and update crud_model_id accordingly """
|
||||
values = {}
|
||||
if self.write_expression:
|
||||
valid, model_name, message = self._check_expression(self.write_expression, self.model_id)
|
||||
else:
|
||||
valid, model_name, message = True, None, False
|
||||
if self.model_id:
|
||||
model_name = self.model_id.model
|
||||
if not valid:
|
||||
return {
|
||||
'warning': {
|
||||
'title': _('Incorrect expression'),
|
||||
'message': message or _('Invalid expression'),
|
||||
}
|
||||
}
|
||||
if model_name:
|
||||
self.crud_model_id = self.env['ir.model'].search([('model', '=', model_name)])
|
||||
|
||||
def _build_expression(self, field_name, sub_field_name):
|
||||
""" Returns a placeholder expression for use in a template field,
|
||||
based on the values provided in the placeholder assistant.
|
||||
|
||||
:param field_name: main field name
|
||||
:param sub_field_name: sub field name (M2O)
|
||||
:return: final placeholder expression
|
||||
"""
|
||||
expression = ''
|
||||
if field_name:
|
||||
expression = "object." + field_name
|
||||
if sub_field_name:
|
||||
expression += "." + sub_field_name
|
||||
return expression
|
||||
|
||||
@api.onchange('model_object_field', 'sub_model_object_field')
|
||||
def _onchange_model_object_field(self):
|
||||
field = self.model_object_field
|
||||
sub_field = self.sub_model_object_field
|
||||
|
||||
self.sub_object = False
|
||||
self.sub_model_object_field = False
|
||||
self.copyvalue = False
|
||||
|
||||
if field:
|
||||
if field.ttype in ['many2one', 'one2many', 'many2many']:
|
||||
comodel = self.env['ir.model'].search([('model', '=', field.relation)])
|
||||
if comodel:
|
||||
self.sub_object = comodel
|
||||
self.copyvalue = self._build_expression(field.name, sub_field.name)
|
||||
self.sub_model_object_field = sub_field
|
||||
else:
|
||||
self.copyvalue = self._build_expression(field.name, False)
|
||||
|
||||
@api.onchange('id_object')
|
||||
def _onchange_id_object(self):
|
||||
self.id_value = self.id_object.id if self.id_object else False
|
||||
@api.onchange('model_id')
|
||||
def _onchange_model_id(self):
|
||||
self.model_name = self.model_id.model
|
||||
|
||||
@api.multi
|
||||
def create_action(self):
|
||||
@@ -741,13 +545,6 @@ class IrActionsServer(models.Model):
|
||||
raise UserError(_('Deletion of the action record failed.'))
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def run_action_client_action(self, action, eval_context=None):
|
||||
if not action.action_id:
|
||||
raise UserError(_("Please specify an action to launch!"))
|
||||
record = self.env[action.action_id.type].browse(action.action_id.id)
|
||||
return record.read()[0]
|
||||
|
||||
@api.model
|
||||
def run_action_code_multi(self, action, eval_context=None):
|
||||
safe_eval(action.sudo().code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action'
|
||||
@@ -778,21 +575,7 @@ class IrActionsServer(models.Model):
|
||||
for exp in action.fields_lines:
|
||||
res[exp.col1.name] = exp.eval_value(eval_context=eval_context)[exp.id]
|
||||
|
||||
if action.use_write == 'current':
|
||||
model = action.model_id.model
|
||||
ref_id = self._context.get('active_id')
|
||||
elif action.use_write == 'other':
|
||||
model = action.crud_model_id.model
|
||||
ref_id = action.ref_object.id
|
||||
elif action.use_write == 'expression':
|
||||
model = action.crud_model_id.model
|
||||
ref = safe_eval(action.write_expression, eval_context)
|
||||
if isinstance(ref, models.BaseModel):
|
||||
ref_id = ref.id
|
||||
else:
|
||||
ref_id = int(ref)
|
||||
|
||||
self.env[model].browse(ref_id).write(res)
|
||||
self.env[action.model_id.model].browse(self._context.get('active_id')).write(res)
|
||||
|
||||
@api.model
|
||||
def run_action_object_create(self, action, eval_context=None):
|
||||
@@ -811,27 +594,16 @@ class IrActionsServer(models.Model):
|
||||
for exp in action.fields_lines:
|
||||
res[exp.col1.name] = exp.eval_value(eval_context=eval_context)[exp.id]
|
||||
|
||||
if action.use_create in ['new', 'copy_current']:
|
||||
model = action.model_id.model
|
||||
elif action.use_create in ['new_other', 'copy_other']:
|
||||
model = action.crud_model_id.model
|
||||
res = self.env[action.crud_model_id.model].create(res)
|
||||
|
||||
if action.use_create == 'copy_current':
|
||||
ref_id = self._context.get('active_id')
|
||||
res = self.env[model].browse(ref_id).copy(res)
|
||||
elif action.use_create == 'copy_other':
|
||||
res = action.ref_object.copy(res)
|
||||
else:
|
||||
res = self.env[model].create(res)
|
||||
|
||||
if action.link_new_record and action.link_field_id:
|
||||
if action.link_field_id:
|
||||
record = self.env[action.model_id.model].browse(self._context.get('active_id'))
|
||||
record.write({action.link_field_id.name: res.id})
|
||||
|
||||
@api.model
|
||||
def _get_eval_context(self, action=None):
|
||||
""" Prepare the context used when evaluating python code, like the
|
||||
condition or code server actions.
|
||||
python formulas or code server actions.
|
||||
|
||||
:param action: the current server action
|
||||
:type action: browse record
|
||||
@@ -868,10 +640,9 @@ class IrActionsServer(models.Model):
|
||||
|
||||
@api.multi
|
||||
def run(self):
|
||||
""" Runs the server action. For each server action, the condition is
|
||||
checked. Note that a void (``False``) condition is considered as always
|
||||
valid. If it is verified, the run_action_<STATE> method is called. This
|
||||
allows easy overriding of the server actions.
|
||||
""" Runs the server action. For each server action, the
|
||||
run_action_<STATE> method is called. This allows easy overriding
|
||||
of the server actions.
|
||||
|
||||
:param dict context: context should contain following keys
|
||||
|
||||
@@ -888,14 +659,7 @@ class IrActionsServer(models.Model):
|
||||
"""
|
||||
for action in self:
|
||||
eval_context = self._get_eval_context(action)
|
||||
condition = action.sudo().condition
|
||||
if condition is False:
|
||||
# Void (aka False) conditions are considered as True
|
||||
condition = True
|
||||
if hasattr(self, 'run_action_%s_multi' % action.state):
|
||||
expr = safe_eval(str(condition), eval_context)
|
||||
if not expr:
|
||||
continue
|
||||
# call the multi method
|
||||
run_self = self.with_context(eval_context['env'].context)
|
||||
func = getattr(run_self, 'run_action_%s_multi' % action.state)
|
||||
@@ -908,9 +672,6 @@ class IrActionsServer(models.Model):
|
||||
# run context dedicated to a particular active_id
|
||||
run_self = self.with_context(active_ids=[active_id], active_id=active_id)
|
||||
eval_context["env"].context = run_self._context
|
||||
expr = safe_eval(str(condition), eval_context)
|
||||
if not expr:
|
||||
continue
|
||||
# call the single method related to the action: run_action_<STATE>
|
||||
func = getattr(run_self, 'run_action_%s' % action.state)
|
||||
res = func(action, eval_context=eval_context)
|
||||
@@ -934,7 +695,7 @@ class IrServerObjectLines(models.Model):
|
||||
col1 = fields.Many2one('ir.model.fields', string='Field', required=True)
|
||||
value = fields.Text(required=True, help="Expression containing a value specification. \n"
|
||||
"When Formula type is selected, this field may be a Python expression "
|
||||
" that can use the same values as for the condition field on the server action.\n"
|
||||
" that can use the same values as for the code field on the server action.\n"
|
||||
"If Value type is selected, the value will be used directly without evaluation.")
|
||||
type = fields.Selection([('value', 'Value'), ('equation', 'Python expression')], 'Evaluation Type', default='value', required=True, change_default=True)
|
||||
|
||||
|
||||
@@ -262,128 +262,66 @@
|
||||
<field name="model">ir.actions.server</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Server Action">
|
||||
<header>
|
||||
<field name="menu_ir_values_id" invisible="1"/>
|
||||
<button name="create_action" string="Create Action" type="object"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible':[('menu_ir_values_id','!=',False)]}"
|
||||
help="Display an option in the 'More' top-menu or related document, to run this action."/>
|
||||
<button name="unlink_action" string="Remove Action" type="object"
|
||||
attrs="{'invisible':[('menu_ir_values_id','=',False)]}"
|
||||
help="Remove the contextual action related to this server action"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<field name="menu_ir_values_id" invisible="1"/>
|
||||
<button name="create_action" string="Add in the 'More' menu" type="object"
|
||||
attrs="{'invisible':[('menu_ir_values_id','!=',False)]}"
|
||||
help="Display an option on related documents to run this sever action"/>
|
||||
<button name="unlink_action" string="Remove from the 'More' menu" type="object"
|
||||
attrs="{'invisible':[('menu_ir_values_id','=',False)]}"
|
||||
help="Remove the contextual action related to this server action"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name" placeholder="e.g. Update order quantity"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="type" invisible="1"/>
|
||||
<group name="action_wrapper">
|
||||
<group name="action_content">
|
||||
<field name="model_id"/>
|
||||
<field name="model_name" invisible="1"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="condition"/>
|
||||
<field name="sequence"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="crud_model_id"
|
||||
attrs="{'invisible': [('state', '!=', 'object_create')], 'required': [('state', '=', 'object_create')]}"/>
|
||||
<field name="crud_model_name" invisible="1"/>
|
||||
<field name="link_field_id"
|
||||
domain="[('model_id', '=', model_id), ('relation', '=', crud_model_name), ('ttype', 'in', ['many2one'])]"
|
||||
attrs="{'invisible': [('state', '!=', 'object_create')]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<notebook>
|
||||
<page string="Python Code" name='code' autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'code')]}">
|
||||
<field name="code" widget="ace" options="{'mode': 'python'}" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
|
||||
</page>
|
||||
|
||||
<page string="Client" autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'client_action')]}">
|
||||
<group>
|
||||
<field name="action_id" attrs="{'required':[('state', '=', 'client_action')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
|
||||
<page string="Create / Write / Copy" autofocus="autofocus"
|
||||
<page string="Data to Write" autofocus="autofocus"
|
||||
attrs="{'invisible':[('state', 'not in', ['object_create', 'object_write'])]}">
|
||||
<p attrs="{'invisible': [('model_id', '!=', False)]}">
|
||||
Please set the Base Model before setting the action details.
|
||||
Please set the Model to Create before choosing values
|
||||
</p>
|
||||
<group attrs="{'invisible': [('model_id', '=', False)]}">
|
||||
<field name="use_create" widget="radio"
|
||||
attrs="{'invisible': [('state', '!=', 'object_create')]}"/>
|
||||
|
||||
<field name="use_write" widget="radio"
|
||||
attrs="{'invisible': [('state', '!=', 'object_write')]}"/>
|
||||
|
||||
<label for="ref_object" string=" "
|
||||
attrs="{'invisible': ['&',
|
||||
'|', ('state', '!=', 'object_write'), ('use_write', '!=', 'other'),
|
||||
'|', ('state', '!=', 'object_create'), ('use_create', '!=', 'copy_other')]}"/>
|
||||
<div style="margin-left: 24px;"
|
||||
attrs="{'invisible': ['&',
|
||||
'|', ('state', '!=', 'object_write'), ('use_write', '!=', 'other'),
|
||||
'|', ('state', '!=', 'object_create'), ('use_create', '!=', 'copy_other')]}">
|
||||
<field name="ref_object" nolabel="1"/>
|
||||
</div>
|
||||
|
||||
<field name="crud_model_id"
|
||||
attrs="{'invisible': ['|', ('state', '!=', 'object_create'), ('use_create', '!=', 'new_other')]}"/>
|
||||
<field name="crud_model_name" invisible="1"/>
|
||||
|
||||
<label for="link_new_record" attrs="{'invisible': [('state', '!=', 'object_create')]}"/>
|
||||
<div attrs="{'invisible': [('state', '!=', 'object_create')]}">
|
||||
<field name="link_new_record" nolabel="1" style="display: inline-block;"/>
|
||||
<p class="oe_grey oe_edit_only" style="display: inline-block; margin: 0px 0px 0px 8px;">
|
||||
Check to attach the newly created record to the record on which the server action runs.
|
||||
</p>
|
||||
<group>
|
||||
<field name="model_name" invisible="1"/>
|
||||
<field name="link_field_id"
|
||||
domain="[('model_id', '=', crud_model_id), ('relation', '=', model_name), ('ttype', 'in', ['many2one'])]"
|
||||
attrs="{'required': [('state', '=', 'object_create'), ('link_new_record', '=', True)],
|
||||
'invisible': ['|', ('state', '!=', 'object_create'), ('link_new_record', '=', False)]}"/>
|
||||
</group>
|
||||
</div>
|
||||
|
||||
<label for="link_new_record" attrs="{'invisible': ['|', ('state', '!=', 'object_write'), ('use_write', '!=', 'expression')]}"/>
|
||||
<div attrs="{'invisible': ['|', ('state', '!=', 'object_write'), ('use_write', '!=', 'expression')]}">
|
||||
<p class="oe_grey oe_edit_only" style="margin: 0px;">
|
||||
Write a python expression, beginning with record, that gives the record to update. An expression builder is available in the help tab. Examples:
|
||||
</p>
|
||||
<ul class="oe_grey oe_edit_only">
|
||||
<li>record.partner_id</li>
|
||||
<li>record.partner_id.currency_id</li>
|
||||
</ul>
|
||||
<field name="write_expression"
|
||||
attrs="{'required': [('state', '=', 'object_write'), ('use_write', '=', 'expression')]}"/>
|
||||
</div>
|
||||
|
||||
<field name="fields_lines">
|
||||
<tree string="Field Mappings" editable="top">
|
||||
<field name="col1" domain="[('model_id', '=', parent.crud_model_id)]"/>
|
||||
<field name="type"/>
|
||||
<field name="value"/>
|
||||
</tree>
|
||||
<form string="Field Mapping">
|
||||
<group >
|
||||
<field name="col1" domain="[('model_id', '=', parent.crud_model_id)]"/>
|
||||
<field name="type"/>
|
||||
<field name="value"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</group>
|
||||
<field name="fields_lines">
|
||||
<tree string="Field Mappings" editable="bottom">
|
||||
<field name="col1" domain="['|', ('model_id', '=', parent.crud_model_id), ('model_id', '=', parent.model_id)]"/>
|
||||
<field name="type"/>
|
||||
<field name="value"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
<page string="Execute several actions" autofocus="autofocus"
|
||||
<page string="Actions" autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'multi')]}">
|
||||
<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>
|
||||
<field name="child_ids"
|
||||
domain="[('model_id', '=', model_id)]"/>
|
||||
<field name="child_ids" domain="[('model_id', '=', model_id)]"/>
|
||||
</page>
|
||||
|
||||
<page string="Help">
|
||||
<page string="Help"
|
||||
attrs="{'invisible': [('state', '!=', 'code')]}">
|
||||
<group>
|
||||
<div style="margin-top: 4px;">
|
||||
<h3>Help with Python expressions.</h3>
|
||||
@@ -394,13 +332,6 @@
|
||||
<li>time, datetime, timezone: Python module</li>
|
||||
<li>log(message, level='info'): a logging function to create ir_logging records</li>
|
||||
</ul>
|
||||
<div>
|
||||
<p>Example of condition expression using Python</p>
|
||||
<ul>
|
||||
<li>condition: True</li>
|
||||
<li>condition: record.list_price > 5000</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div attrs="{'invisible': [('state', '!=', 'code')]}">
|
||||
<p>Example of python code</p>
|
||||
<code style='white-space: pre-wrap'>
|
||||
@@ -409,25 +340,6 @@ env["res.partner"].create({"name": partner_name})
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<group>
|
||||
<h3 colspan="2">Dynamic expression builder</h3>
|
||||
<p colspan="2" attrs="{'invisible': [('model_id', '!=', False)]}">
|
||||
Please set the Base Model of the action to enable the dynamic expression buidler.
|
||||
</p>
|
||||
<field name="model_object_field"
|
||||
attrs="{'invisible': [('model_id', '=', False)]}"
|
||||
domain="[('model_id', '=', model_id), ('ttype', '!=', 'one2many'), ('ttype', '!=', 'many2many')]"/>
|
||||
<field name="sub_object" readonly="1" attrs="{'invisible': [('model_id', '=', False)]}"/>
|
||||
<field name="sub_model_object_field"
|
||||
domain="[('model_id', '=', sub_object), ('ttype', '!=', 'one2many'), ('ttype', '!=', 'many2many')]"
|
||||
attrs="{'readonly':[('sub_object','=',False)],
|
||||
'required':[('sub_object','!=',False)],
|
||||
'invisible': [('model_id', '=', False)]}"/>
|
||||
<field name="copyvalue" attrs="{'invisible': [('model_id', '=', False)]}"/>
|
||||
<h3 colspan="2">Find the ID of a record in the database</h3>
|
||||
<field name="id_object"/>
|
||||
<field name="id_value" />
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
@@ -440,9 +352,11 @@ env["res.partner"].create({"name": partner_name})
|
||||
<field name="model">ir.actions.server</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Server Actions">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="state"/>
|
||||
<field name="model_id"/>
|
||||
<field name="usage"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -457,6 +371,7 @@ env["res.partner"].create({"name": partner_name})
|
||||
<group expand="0" string="Group By" colspan="4" col="4">
|
||||
<filter string="Action Type" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Model" domain="[]" context="{'group_by':'model_id'}"/>
|
||||
<filter string="Usage" domain="[]" context="{'group_by':'usage'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
@@ -540,7 +455,6 @@ env["res.partner"].create({"name": partner_name})
|
||||
<menuitem id="menu_ir_actions_todo_form" action="act_ir_actions_todo_form" parent="base.next_id_6"/>
|
||||
<record id="action_run_ir_action_todo" model="ir.actions.server">
|
||||
<field name="name">Run Remaining Action Todo</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_res_config"/>
|
||||
<field name="state">code</field>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<data>
|
||||
<record id="action_server_module_immediate_install" model="ir.actions.server">
|
||||
<field name="name">Module Immediate Install</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_ir_module_module" />
|
||||
<field name="state">code</field>
|
||||
|
||||
@@ -42,7 +42,6 @@ class TestServerActionsBase(common.TransactionCase):
|
||||
# create server action to
|
||||
self.action = self.env['ir.actions.server'].create({
|
||||
'name': 'TestAction',
|
||||
'condition': 'True',
|
||||
'model_id': self.res_partner_model.id,
|
||||
'state': 'code',
|
||||
'code': 'record.write({"comment": "MyComment"})',
|
||||
@@ -52,16 +51,10 @@ class TestServerActionsBase(common.TransactionCase):
|
||||
class TestServerActions(TestServerActionsBase):
|
||||
|
||||
def test_00_action(self):
|
||||
# Do: eval 'True' condition
|
||||
self.action.with_context(self.context).run()
|
||||
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: invalid condition check')
|
||||
self.test_partner.write({'comment': False})
|
||||
|
||||
# Do: eval False condition, that should be considered as True (void = True)
|
||||
self.action.write({'condition': False})
|
||||
self.action.with_context(self.context).run()
|
||||
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: invalid condition check')
|
||||
|
||||
# Do: create contextual action
|
||||
self.action.create_action()
|
||||
|
||||
@@ -90,27 +83,14 @@ class TestServerActions(TestServerActionsBase):
|
||||
partners = self.test_partner.search([('name', 'ilike', 'TestingPartner_code')])
|
||||
self.assertEqual(len(partners), 1, 'ir_actions_server: 1 new partner should have been created')
|
||||
|
||||
def test_30_client(self):
|
||||
client_action = self.env['ir.actions.client'].create({
|
||||
'name': 'TestAction2',
|
||||
'tag': 'Test',
|
||||
})
|
||||
self.action.write({
|
||||
'state': 'client_action',
|
||||
'action_id': client_action.id,
|
||||
})
|
||||
res = self.action.with_context(self.context).run()
|
||||
self.assertEqual(res['name'], 'TestAction2', 'ir_actions_server: incorrect return result for a client action')
|
||||
|
||||
def test_40_crud_create(self):
|
||||
def test_20_crud_create(self):
|
||||
_city = 'TestCity'
|
||||
_name = 'TestNew'
|
||||
|
||||
# Do: create a new record in the same model and link it
|
||||
self.action.write({
|
||||
'state': 'object_create',
|
||||
'use_create': 'new',
|
||||
'link_new_record': True,
|
||||
'crud_model_id': self.action.model_id.id,
|
||||
'link_field_id': self.res_partner_parent_field.id,
|
||||
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field.id, 'value': _name}),
|
||||
(0, 0, {'col1': self.res_partner_city_field.id, 'value': _city})],
|
||||
@@ -124,29 +104,11 @@ class TestServerActions(TestServerActionsBase):
|
||||
# Test: new partner linked
|
||||
self.assertEqual(self.test_partner.parent_id, partner, 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy current record
|
||||
self.action.write({
|
||||
'state': 'object_create',
|
||||
'use_create': 'copy_current',
|
||||
'link_new_record': False,
|
||||
'fields_lines': [(5,),
|
||||
(0, 0, {'col1': self.res_partner_name_field.id, 'value': 'TestCopyCurrent'}),
|
||||
(0, 0, {'col1': self.res_partner_city_field.id, 'value': 'TestCity'})],
|
||||
})
|
||||
run_res = self.action.with_context(self.context).run()
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new partner created
|
||||
partner = self.test_partner.search([('name', 'ilike', 'TestingPartner (copy)')]) # currently res_partner overrides default['name'] whatever its value
|
||||
self.assertEqual(len(partner), 1, 'ir_actions_server: TODO')
|
||||
self.assertEqual(partner.city, 'TestCity', 'ir_actions_server: TODO')
|
||||
self.assertEqual(partner.country_id, self.test_partner.country_id, 'ir_actions_server: TODO')
|
||||
|
||||
# Do: create a new record in another model
|
||||
self.action.write({
|
||||
'state': 'object_create',
|
||||
'use_create': 'new_other',
|
||||
'crud_model_id': self.res_country_model.id,
|
||||
'link_new_record': False,
|
||||
'link_field_id': False,
|
||||
'fields_lines': [(5,),
|
||||
(0, 0, {'col1': self.res_country_name_field.id, 'value': 'record.name', 'type': 'equation'}),
|
||||
(0, 0, {'col1': self.res_country_code_field.id, 'value': 'record.name[0:2]', 'type': 'equation'})],
|
||||
@@ -158,69 +120,23 @@ class TestServerActions(TestServerActionsBase):
|
||||
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
|
||||
self.assertEqual(country.code, 'TE', 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy a record in another model
|
||||
self.action.write({
|
||||
'state': 'object_create',
|
||||
'use_create': 'copy_other',
|
||||
'crud_model_id': self.res_country_model.id,
|
||||
'link_new_record': False,
|
||||
'ref_object': 'res.country,%s' % self.test_country.id,
|
||||
'fields_lines': [(5,),
|
||||
(0, 0, {'col1': self.res_country_name_field.id, 'value': 'NewCountry', 'type': 'value'}),
|
||||
(0, 0, {'col1': self.res_country_code_field.id, 'value': 'NY', 'type': 'value'})],
|
||||
})
|
||||
run_res = self.action.with_context(self.context).run()
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new country created
|
||||
country = self.test_country.search([('name', 'ilike', 'NewCountry')])
|
||||
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
|
||||
self.assertEqual(country.code, 'NY', 'ir_actions_server: TODO')
|
||||
self.assertEqual(country.address_format, 'SuperFormat', 'ir_actions_server: TODO')
|
||||
|
||||
def test_50_crud_write(self):
|
||||
def test_30_crud_write(self):
|
||||
_name = 'TestNew'
|
||||
|
||||
# Do: create a new record in the same model and link it
|
||||
# Do: update partner name
|
||||
self.action.write({
|
||||
'state': 'object_write',
|
||||
'use_write': 'current',
|
||||
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field.id, 'value': _name})],
|
||||
})
|
||||
run_res = self.action.with_context(self.context).run()
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new partner created
|
||||
# Test: partner updated
|
||||
partner = self.test_partner.search([('name', 'ilike', _name)])
|
||||
self.assertEqual(len(partner), 1, 'ir_actions_server: TODO')
|
||||
self.assertEqual(partner.city, 'OrigCity', 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy current record
|
||||
self.action.write({
|
||||
'use_write': 'other',
|
||||
'crud_model_id': self.res_country_model.id,
|
||||
'ref_object': 'res.country,%s' % self.test_country.id,
|
||||
'fields_lines': [(5,), (0, 0, {'col1': self.res_country_name_field.id, 'value': 'record.name', 'type': 'equation'})],
|
||||
})
|
||||
run_res = self.action.with_context(self.context).run()
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new country created
|
||||
country = self.test_country.search([('name', 'ilike', 'TestNew')])
|
||||
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
|
||||
|
||||
# Do: copy a record in another model
|
||||
self.action.write({
|
||||
'use_write': 'expression',
|
||||
'crud_model_id': self.res_country_model.id,
|
||||
'write_expression': 'record.country_id',
|
||||
'fields_lines': [(5,), (0, 0, {'col1': self.res_country_name_field.id, 'value': 'NewCountry', 'type': 'value'})],
|
||||
})
|
||||
run_res = self.action.with_context(self.context).run()
|
||||
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
|
||||
# Test: new country created
|
||||
country = self.test_country.search([('name', 'ilike', 'NewCountry')])
|
||||
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
|
||||
|
||||
@mute_logger('odoo.addons.base.ir.ir_model', 'odoo.models')
|
||||
def test_60_multi(self):
|
||||
def test_40_multi(self):
|
||||
# Data: 2 server actions that will be nested
|
||||
action1 = self.action.create({
|
||||
'name': 'Subaction1',
|
||||
@@ -233,8 +149,10 @@ class TestServerActions(TestServerActionsBase):
|
||||
'name': 'Subaction2',
|
||||
'sequence': 2,
|
||||
'model_id': self.res_partner_model.id,
|
||||
'crud_model_id': self.res_partner_model.id,
|
||||
'state': 'object_create',
|
||||
'use_create': 'copy_current',
|
||||
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field.id, 'value': 'RaoulettePoiluchette'}),
|
||||
(0, 0, {'col1': self.res_partner_city_field.id, 'value': 'TestingCity'})],
|
||||
})
|
||||
action3 = self.action.create({
|
||||
'name': 'Subaction3',
|
||||
@@ -253,8 +171,8 @@ class TestServerActions(TestServerActionsBase):
|
||||
|
||||
# Test: new partner created
|
||||
# currently res_partner overrides default['name'] whatever its value
|
||||
partner = self.test_partner.search([('name', 'ilike', 'TestingPartner (copy)')])
|
||||
self.assertEqual(len(partner), 1, 'ir_actions_server: TODO')
|
||||
partner = self.test_partner.search([('name', 'ilike', 'RaoulettePoiluchette')])
|
||||
self.assertEqual(len(partner), 1)
|
||||
# Test: action returned
|
||||
self.assertEqual(res.get('type'), 'ir.actions.act_url')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user