From aedde9a46c4e0c478cdb3fdebd7036e02753d712 Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Mon, 2 Jan 2017 13:50:19 +0100 Subject: [PATCH] [REF] base_automation: cleaning and refactoring Purpose of this commit is to continue to clean server actions, scheduled actions and automated actions. Server actions hold code execution and automated actions hold triggers and conditions. This commit make automated action inherits from ir_actions_server. Automated actions do not hold several server actions anymore. Instead each new automated action creates its own server action holding the code or actions to perform. Automated actions that held several server actions should now simply use a server action of type multi. This commit also features: * rename 'kind' field to trigger * remove 'act_user_id' as this should be done by the server action itself not the automated action. This can be achieved using a write or code server action; * remove 'act_followers' as this is replaced by a followers server action; * use a primary form view inherited from server action. This way automated actions use the same base form as server actions with triggers and conditions added; 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. --- .../data/base_automation_demo.xml | 36 ++-- addons/base_automation/models/__init__.py | 1 + .../base_automation/models/base_automation.py | 107 ++++++------ addons/base_automation/models/ir_actions.py | 10 ++ .../security/ir.model.access.csv | 2 +- .../views/base_automation_view.xml | 158 +++++++----------- 6 files changed, 143 insertions(+), 171 deletions(-) create mode 100644 addons/base_automation/models/ir_actions.py diff --git a/addons/base_automation/data/base_automation_demo.xml b/addons/base_automation/data/base_automation_demo.xml index ea6b0fa13f2..a4014a64c3e 100644 --- a/addons/base_automation/data/base_automation_demo.xml +++ b/addons/base_automation/data/base_automation_demo.xml @@ -19,34 +19,46 @@ - Test rule on create - on_create + Base Automation: test rule on create + + code + + on_create + - - Test rule on write - on_write + Base Automation: test rule on write + + code + + on_write + - - Test rule on recompute - on_write + Base Automation: test rule on recompute + + code + + on_write + - [('customer', '=', True)] - Test rule on secondary record - on_create + Base Automation: test rule on secondary model + + code + + on_create + - diff --git a/addons/base_automation/models/__init__.py b/addons/base_automation/models/__init__.py index 4ecebd2edee..ef96b3be347 100644 --- a/addons/base_automation/models/__init__.py +++ b/addons/base_automation/models/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import base_automation +import ir_actions diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index f841c9bedcc..051198edc17 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -30,17 +30,19 @@ class BaseAutomation(models.Model): _description = 'Automated Action' _order = 'sequence' - name = fields.Char(string='Rule Name', required=True) - model_id = fields.Many2one('ir.model', string='Related Document Model', required=True, domain=[('transient', '=', False)]) - model = fields.Char(related='model_id.model', readonly=True) + action_server_id = fields.Many2one( + 'ir.actions.server', 'Server Actions', + domain="[('model_id', '=', model_id)]", + delegate=True, required=True, ondelete='restrict') active = fields.Boolean(default=True, help="When unchecked, the rule is hidden and will not be executed.") - sequence = fields.Integer(help="Gives the sequence order when displaying a list of rules.") - kind = fields.Selection([('on_create', 'On Creation'), - ('on_write', 'On Update'), - ('on_create_or_write', 'On Creation & Update'), - ('on_unlink', 'On Deletion'), - ('on_change', 'Based on Form Modification'), - ('on_time', 'Based on Timed Condition')], string='When to Run', required=True) + trigger = fields.Selection([ + ('on_create', 'On Creation'), + ('on_write', 'On Update'), + ('on_create_or_write', 'On Creation & Update'), + ('on_unlink', 'On Deletion'), + ('on_change', 'Based on Form Modification'), + ('on_time', 'Based on Timed Condition') + ], string='Trigger condition', required=True, oldname="kind") trg_date_id = fields.Many2one('ir.model.fields', string='Trigger Date', help="""When should the condition be triggered. If present, will be checked by the scheduler. If empty, will be checked at creation and update.""", @@ -53,34 +55,33 @@ class BaseAutomation(models.Model): string='Delay type', default='day') trg_date_calendar_id = fields.Many2one("resource.calendar", string='Use Calendar', help="When calculating a day-based timed condition, it is possible to use a calendar to compute the date based on working days.") - act_user_id = fields.Many2one('res.users', string='Set Responsible') - act_followers = fields.Many2many("res.partner", string="Add Followers") - server_action_ids = fields.Many2many('ir.actions.server', string='Server Actions', domain="[('model_id', '=', model_id)]", - help="Examples: email reminders, call object service, etc.") - filter_pre_id = fields.Many2one("ir.filters", string='Before Update Filter', ondelete='restrict', domain="[('model_id', '=', model_id.model)]", + filter_pre_id = fields.Many2one("ir.filters", string='Before Update Filter', ondelete='restrict', + domain="[('model_id', '=', model_name)]", help="If present, this condition must be satisfied before the update of the record.") filter_pre_domain = fields.Char(string='Before Update Domain', help="If present, this condition must be satisfied before the update of the record.") - filter_id = fields.Many2one("ir.filters", string='Filter', ondelete='restrict', domain="[('model_id', '=', model_id.model)]", + filter_id = fields.Many2one("ir.filters", string='Filter', ondelete='restrict', + domain="[('model_id', '=', model_name)]", help="If present, this condition must be satisfied before executing the action rule.") filter_domain = fields.Char(string='Domain', help="If present, this condition must be satisfied before executing the action rule.") last_run = fields.Datetime(readonly=True, copy=False) on_change_fields = fields.Char(string="On Change Fields Trigger", help="Comma-separated list of field names that triggers the onchange.") # which fields have an impact on the registry - CRITICAL_FIELDS = ['model_id', 'active', 'kind', 'on_change_fields'] + CRITICAL_FIELDS = ['model_id', 'active', 'trigger', 'on_change_fields'] @api.onchange('model_id') def onchange_model_id(self): self.filter_pre_id = self.filter_id = False + self.model_name = self.model_id.model - @api.onchange('kind') - def onchange_kind(self): - if self.kind in ['on_create', 'on_create_or_write', 'on_unlink']: + @api.onchange('trigger') + def onchange_trigger(self): + if self.trigger in ['on_create', 'on_create_or_write', 'on_unlink']: self.filter_pre_id = self.filter_pre_domain = self.trg_date_id = self.trg_date_range = self.trg_date_range_type = False - elif self.kind in ['on_write', 'on_create_or_write']: + elif self.trigger in ['on_write', 'on_create_or_write']: self.trg_date_id = self.trg_date_range = self.trg_date_range_type = False - elif self.kind == 'on_time': + elif self.trigger == 'on_time': self.filter_pre_id = self.filter_pre_domain = False @api.onchange('filter_pre_id') @@ -93,6 +94,7 @@ class BaseAutomation(models.Model): @api.model def create(self, vals): + vals['usage'] = 'base_automation' base_automation = super(BaseAutomation, self).create(vals) self._update_cron() self._update_registry() @@ -118,7 +120,7 @@ class BaseAutomation(models.Model): based on time conditions. """ cron = self.env.ref('base_automation.ir_cron_data_base_automation_check', raise_if_not_found=False) - return cron and cron.toggle(model=self._name, domain=[('kind', '=', 'on_time')]) + return cron and cron.toggle(model=self._name, domain=[('trigger', '=', 'on_time')]) def _update_registry(self): """ Update the registry after a modification on action rules. """ @@ -129,13 +131,13 @@ class BaseAutomation(models.Model): registry = Registry.new(self._cr.dbname) registry.signal_registry_change() - def _get_actions(self, records, kinds): - """ Return the actions of the given kinds for records' model. The + def _get_actions(self, records, triggers): + """ Return the actions of the given triggers for records' model. The returned actions' context contain an object to manage processing. """ if '__action_done' not in self._context: self = self.with_context(__action_done={}) - domain = [('model', '=', records._name), ('kind', 'in', kinds)] + domain = [('model_name', '=', records._name), ('trigger', 'in', triggers)] actions = self.with_context(active_test=True).search(domain) return actions.with_env(self.env) @@ -189,27 +191,14 @@ class BaseAutomation(models.Model): values = {} if 'date_action_last' in records._fields: values['date_action_last'] = fields.Datetime.now() - if self.act_user_id and 'user_id' in records._fields: - values['user_id'] = self.act_user_id.id if values: records.write(values) - # subscribe followers - if self.act_followers and hasattr(records, 'message_subscribe'): - followers = self.env['mail.followers'].sudo().search( - [('res_model', '=', records._name), - ('res_id', 'in', records.ids), - ('partner_id', 'in', self.act_followers.ids), - ] - ) - if not len(followers) == len(self.act_followers): - records.message_subscribe(self.act_followers.ids) - # execute server actions - if self.server_action_ids: + if self.action_server_id: for record in records: ctx = {'active_model': record._name, 'active_ids': record.ids, 'active_id': record.id} - self.server_action_ids.with_context(**ctx).run() + self.action_server_id.with_context(**ctx).run() @api.model_cr def _register_hook(self): @@ -287,16 +276,16 @@ class BaseAutomation(models.Model): def base_automation_onchange(self): action_rule = self.env['base.automation'].browse(action_rule_id) result = {} - for server_action in action_rule.server_action_ids.with_context(active_model=self._name, onchange_self=self): - res = server_action.run() - if res: - if 'value' in res: - res['value'].pop('id', None) - self.update({key: val for key, val in res['value'].iteritems() if key in self._fields}) - if 'domain' in res: - result.setdefault('domain', {}).update(res['domain']) - if 'warning' in res: - result['warning'] = res['warning'] + server_action = action_rule.action_server_id.with_context(active_model=self._name, onchange_self=self) + res = server_action.run() + if res: + if 'value' in res: + res['value'].pop('id', None) + self.update({key: val for key, val in res['value'].iteritems() if key in self._fields}) + if 'domain' in res: + result.setdefault('domain', {}).update(res['domain']) + if 'warning' in res: + result['warning'] = res['warning'] return result return base_automation_onchange @@ -310,21 +299,21 @@ class BaseAutomation(models.Model): # retrieve all actions, and patch their corresponding model for action_rule in self.with_context({}).search([]): - Model = self.env[action_rule.model] - if action_rule.kind == 'on_create': + Model = self.env[action_rule.model_name] + if action_rule.trigger == 'on_create': patch(Model, 'create', make_create()) - elif action_rule.kind == 'on_create_or_write': + elif action_rule.trigger == 'on_create_or_write': patch(Model, 'create', make_create()) patch(Model, '_write', make_write()) - elif action_rule.kind == 'on_write': + elif action_rule.trigger == 'on_write': patch(Model, '_write', make_write()) - elif action_rule.kind == 'on_unlink': + elif action_rule.trigger == 'on_unlink': patch(Model, 'unlink', make_unlink()) - elif action_rule.kind == 'on_change': + elif action_rule.trigger == 'on_change': # register an onchange method for the action_rule method = make_onchange(action_rule.id) for field_name in action_rule.on_change_fields.split(","): @@ -350,7 +339,7 @@ class BaseAutomation(models.Model): # retrieve all the action rules to run based on a timed condition eval_context = self._get_eval_context() - for action in self.with_context(active_test=True).search([('kind', '=', 'on_time')]): + for action in self.with_context(active_test=True).search([('trigger', '=', 'on_time')]): last_run = fields.Datetime.from_string(action.last_run) or datetime.datetime.utcfromtimestamp(0) # retrieve all the records that satisfy the action's condition @@ -367,7 +356,7 @@ class BaseAutomation(models.Model): filter_meta = action.filter_id.get_metadata()[0] user_id = (filter_meta['write_uid'] or filter_meta['create_uid'])[0] context['lang'] = self.env['res.users'].browse(user_id).lang - records = self.env[action.model].with_context(context).search(domain) + records = self.env[action.model_name].with_context(context).search(domain) # determine when action should occur for the records if action.trg_date_id.name == 'date_action_last' and 'create_date' in records._fields: diff --git a/addons/base_automation/models/ir_actions.py b/addons/base_automation/models/ir_actions.py new file mode 100644 index 00000000000..67e75872873 --- /dev/null +++ b/addons/base_automation/models/ir_actions.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class ServerAction(models.Model): + _inherit = "ir.actions.server" + + usage = fields.Selection(selection_add=[('base_automation', 'Automated Action')]) diff --git a/addons/base_automation/security/ir.model.access.csv b/addons/base_automation/security/ir.model.access.csv index 68396051573..2dc7103a91b 100644 --- a/addons/base_automation/security/ir.model.access.csv +++ b/addons/base_automation/security/ir.model.access.csv @@ -2,4 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_base_automation,base.automation,model_base_automation,,1,0,0,0 access_base_automation_config,base.automation config,model_base_automation,base.group_system,1,1,1,1 access_base_automation_lead_test,access_base_automation_lead_test,model_base_automation_lead_test,base.group_system,1,1,1,1 -access_base_automation_line_test,access_base_automation_line_test,model_base_automation_line_test,base.group_system,1,1,1,1 +access_base_automation_line_test,access_base_automation_line_test,model_base_automation_line_test,base.group_system,1,1,1,1 \ No newline at end of file diff --git a/addons/base_automation/views/base_automation_view.xml b/addons/base_automation/views/base_automation_view.xml index 212443c0dfd..838b1a62ccb 100644 --- a/addons/base_automation/views/base_automation_view.xml +++ b/addons/base_automation/views/base_automation_view.xml @@ -1,118 +1,78 @@ - + + - base.automation.form + Automations base.automation + primary + -
- - -
+ +