From 8fa9f7d6d4bc75bb78547358c3cb1c398b1a52ca Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Wed, 14 Dec 2016 10:38:16 +0100 Subject: [PATCH] [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. --- addons/crm/data/base_action_rule_demo.xml | 2 - addons/crm/views/crm_lead_views.xml | 1 - addons/hr_contract/data/hr_contract_data.xml | 2 - .../data/hr_timesheet_sheet_data.xml | 1 - addons/lunch/views/lunch_views.xml | 1 - addons/mail/models/ir_actions.py | 5 - addons/mail/views/ir_actions_views.xml | 21 -- .../data/marketing_campaign_crm_demo.xml | 1 - addons/survey/views/survey_views.xml | 1 - addons/website/data/website_demo.xml | 2 - addons/website/views/ir_actions_views.xml | 2 - odoo/addons/base/ir/ir_actions.py | 281 ++---------------- odoo/addons/base/ir/ir_actions.xml | 160 +++------- .../wizard/base_module_immediate_install.xml | 1 - odoo/addons/base/tests/test_ir_actions.py | 106 +------ 15 files changed, 70 insertions(+), 517 deletions(-) diff --git a/addons/crm/data/base_action_rule_demo.xml b/addons/crm/data/base_action_rule_demo.xml index 6307c778996..a4fcca49b46 100644 --- a/addons/crm/data/base_action_rule_demo.xml +++ b/addons/crm/data/base_action_rule_demo.xml @@ -11,7 +11,6 @@ Reminder to User - True ir.actions.server email @@ -38,7 +37,6 @@ Set team to Sales Department - True ir.actions.server code record.write({'team_id': env.ref('sales_team.team_sales_department').id}) diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index c76e4154f3e..856d88b6fe9 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -774,7 +774,6 @@ if record: code action = model.action_your_pipeline() - diff --git a/addons/hr_contract/data/hr_contract_data.xml b/addons/hr_contract/data/hr_contract_data.xml index 699e7160b6b..39ffc30e598 100644 --- a/addons/hr_contract/data/hr_contract_data.xml +++ b/addons/hr_contract/data/hr_contract_data.xml @@ -51,7 +51,6 @@ HR Contract: set as pending - True ir.actions.server code record.set_as_pending() @@ -82,7 +81,6 @@ HR Contract: set as close - True ir.actions.server code record.set_as_close() diff --git a/addons/hr_timesheet_sheet/data/hr_timesheet_sheet_data.xml b/addons/hr_timesheet_sheet/data/hr_timesheet_sheet_data.xml index ccd9b5e2498..e49e3d2cbbb 100644 --- a/addons/hr_timesheet_sheet/data/hr_timesheet_sheet_data.xml +++ b/addons/hr_timesheet_sheet/data/hr_timesheet_sheet_data.xml @@ -8,7 +8,6 @@ ir.actions.server action = model.open_timesheet() - True My Timesheet diff --git a/addons/lunch/views/lunch_views.xml b/addons/lunch/views/lunch_views.xml index 849a3237054..b5358238c49 100644 --- a/addons/lunch/views/lunch_views.xml +++ b/addons/lunch/views/lunch_views.xml @@ -415,7 +415,6 @@ Archive/Restore - True ir.actions.server code diff --git a/addons/mail/models/ir_actions.py b/addons/mail/models/ir_actions.py index 33056302562..8486e00d9a1 100644 --- a/addons/mail/models/ir_actions.py +++ b/addons/mail/models/ir_actions.py @@ -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)]", diff --git a/addons/mail/views/ir_actions_views.xml b/addons/mail/views/ir_actions_views.xml index 2167449417f..8ed68b586d5 100644 --- a/addons/mail/views/ir_actions_views.xml +++ b/addons/mail/views/ir_actions_views.xml @@ -16,27 +16,6 @@ -

- Choose a template to display its values. -

-

- The values displayed hereunder are informative. When sending the email, the values - will be taken from the email template. -

-
- - diff --git a/addons/marketing_campaign_crm_demo/data/marketing_campaign_crm_demo.xml b/addons/marketing_campaign_crm_demo/data/marketing_campaign_crm_demo.xml index b959aefac4d..dc2c70ba513 100644 --- a/addons/marketing_campaign_crm_demo/data/marketing_campaign_crm_demo.xml +++ b/addons/marketing_campaign_crm_demo/data/marketing_campaign_crm_demo.xml @@ -5,7 +5,6 @@ code True -
diff --git a/addons/survey/views/survey_views.xml b/addons/survey/views/survey_views.xml index c84aaa3879c..a810f095637 100644 --- a/addons/survey/views/survey_views.xml +++ b/addons/survey/views/survey_views.xml @@ -635,7 +635,6 @@ Clean test answers - True ir.actions.server code diff --git a/addons/website/data/website_demo.xml b/addons/website/data/website_demo.xml index bd75e56363c..482ad1f1832 100644 --- a/addons/website/data/website_demo.xml +++ b/addons/website/data/website_demo.xml @@ -36,7 +36,6 @@ Website Partner Post and Thanks Demo - True partner_thanks @@ -112,7 +111,6 @@ response = request.website.render("website.template_partner_post", {'partner': p Website Partners Comment Form - True partner_comment diff --git a/addons/website/views/ir_actions_views.xml b/addons/website/views/ir_actions_views.xml index d692ce845cd..9f06b0448f7 100644 --- a/addons/website/views/ir_actions_views.xml +++ b/addons/website/views/ir_actions_views.xml @@ -11,8 +11,6 @@ - - diff --git a/odoo/addons/base/ir/ir_actions.py b/odoo/addons/base/ir/ir_actions.py index 3c458a7c723..92de0cb2f4f 100644 --- a/odoo/addons/base/ir/ir_actions.py +++ b/odoo/addons/base/ir/ir_actions.py @@ -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_ method is called. This - allows easy overriding of the server actions. + """ Runs the server action. For each server action, the + run_action_ 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_ 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) diff --git a/odoo/addons/base/ir/ir_actions.xml b/odoo/addons/base/ir/ir_actions.xml index 488434c5333..3bf07f1c798 100644 --- a/odoo/addons/base/ir/ir_actions.xml +++ b/odoo/addons/base/ir/ir_actions.xml @@ -262,128 +262,66 @@ ir.actions.server
+
+ +
-
- -
- - - + + + - - - - + + + + - + - - - - - - - -

- Please set the Base Model before setting the action details. + Please set the Model to Create before choosing values

- - - - - - + + + + + + +
-

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.

- +
- +

Help with Python expressions.

@@ -394,13 +332,6 @@
  • time, datetime, timezone: Python module
  • log(message, level='info'): a logging function to create ir_logging records
  • -
    -

    Example of condition expression using Python

    -
      -
    • condition: True
    • -
    • condition: record.list_price > 5000
    • -
    -

    Example of python code

    @@ -409,25 +340,6 @@ env["res.partner"].create({"name": partner_name})
    - -

    Dynamic expression builder

    -

    - Please set the Base Model of the action to enable the dynamic expression buidler. -

    - - - - -

    Find the ID of a record in the database

    - - -
    @@ -440,9 +352,11 @@ env["res.partner"].create({"name": partner_name}) ir.actions.server + +
    @@ -457,6 +371,7 @@ env["res.partner"].create({"name": partner_name}) +
    @@ -540,7 +455,6 @@ env["res.partner"].create({"name": partner_name}) Run Remaining Action Todo - True ir.actions.server code diff --git a/odoo/addons/base/module/wizard/base_module_immediate_install.xml b/odoo/addons/base/module/wizard/base_module_immediate_install.xml index 70a79f26c31..c9e0edc1b12 100644 --- a/odoo/addons/base/module/wizard/base_module_immediate_install.xml +++ b/odoo/addons/base/module/wizard/base_module_immediate_install.xml @@ -3,7 +3,6 @@ Module Immediate Install - True ir.actions.server code diff --git a/odoo/addons/base/tests/test_ir_actions.py b/odoo/addons/base/tests/test_ir_actions.py index 0d5174b179c..74d220b05bb 100644 --- a/odoo/addons/base/tests/test_ir_actions.py +++ b/odoo/addons/base/tests/test_ir_actions.py @@ -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')