diff options
| -rw-r--r-- | addons/base_automation/data/base_automation_demo.xml | 36 | ||||
| -rw-r--r-- | addons/base_automation/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/base_automation/models/base_automation.py | 107 | ||||
| -rw-r--r-- | addons/base_automation/models/ir_actions.py | 10 | ||||
| -rw-r--r-- | addons/base_automation/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | addons/base_automation/views/base_automation_view.xml | 158 |
6 files changed, 143 insertions, 171 deletions
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 @@ </record> <record id="test_rule_on_create" model="base.automation"> - <field name="name">Test rule on create</field> - <field name="kind">on_create</field> + <field name="name">Base Automation: test rule on create</field> + <field name="model_id" ref="base_automation.model_base_automation_lead_test"/> + <field name="state">code</field> + <field name="code" eval="'records.write({\'user_id\': %s})' % ref('base.user_demo')"/> + <field name="trigger">on_create</field> + <field name="active" eval="True"/> <field name="model_id" ref="base_automation.model_base_automation_lead_test"/> - <field name="act_user_id" ref="base.user_demo"/> <field name="filter_id" ref="test_filter_draft"/> </record> <record id="test_rule_on_write" model="base.automation"> - <field name="name">Test rule on write</field> - <field name="kind">on_write</field> + <field name="name">Base Automation: test rule on write</field> + <field name="model_id" ref="base_automation.model_base_automation_lead_test"/> + <field name="state">code</field> + <field name="code" eval="'records.write({\'user_id\': %s})' % ref('base.user_demo')"/> + <field name="trigger">on_write</field> + <field name="active" eval="True"/> <field name="model_id" ref="base_automation.model_base_automation_lead_test"/> - <field name="act_user_id" ref="base.user_demo"/> <field name="filter_pre_id" ref="test_filter_open"/> <field name="filter_id" ref="test_filter_done"/> </record> <record id="test_rule_on_recompute" model="base.automation"> - <field name="name">Test rule on recompute</field> - <field name="kind">on_write</field> + <field name="name">Base Automation: test rule on recompute</field> + <field name="model_id" ref="base_automation.model_base_automation_lead_test"/> + <field name="state">code</field> + <field name="code" eval="'records.write({\'user_id\': %s})' % ref('base.user_demo')"/> + <field name="trigger">on_write</field> + <field name="active" eval="True"/> <field name="model_id" ref="base_automation.model_base_automation_lead_test"/> - <field name="act_user_id" ref="base.user_demo"/> <field name="filter_domain">[('customer', '=', True)]</field> </record> <record id="test_rule_on_line" model="base.automation"> - <field name="name">Test rule on secondary record</field> - <field name="kind">on_create</field> + <field name="name">Base Automation: test rule on secondary model</field> + <field name="model_id" ref="base_automation.model_base_automation_line_test"/> + <field name="state">code</field> + <field name="code" eval="'records.write({\'user_id\': %s})' % ref('base.user_demo')"/> + <field name="trigger">on_create</field> + <field name="active" eval="True"/> <field name="model_id" ref="base_automation.model_base_automation_line_test"/> - <field name="act_user_id" ref="base.user_demo"/> </record> </odoo> 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 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> - <!-- Action Rule Form View --> + + <!-- Automation Form View --> <record id="view_base_automation_form" model="ir.ui.view"> - <field name="name">base.automation.form</field> + <field name="name">Automations</field> <field name="model">base.automation</field> + <field name="mode">primary</field> + <field name="inherit_id" ref="base.view_server_action_form"/> <field name="arch" type="xml"> - <form string="Action Rule"> - <sheet> - <label for="name" class="oe_edit_only"/> - <h1><field name="name"/></h1> - <group> - <group> - <field name="model_id"/> - <field name="model" invisible="1"/> - </group> - <group> - <field name="active"/> - <field name="sequence"/> - </group> - </group> - <notebook> - <page string="Conditions"> - <group attrs="{'invisible': [('model', '!=', False)]}"> - <p><b>Please choose the document type before setting the conditions.</b></p> - </group> - <group attrs="{'invisible': [('model', '=', False)]}"> - <field name="kind"/> - <label for="filter_pre_id" attrs="{'invisible': [('kind', '!=', 'on_write')]}"/> - <div attrs="{'invisible': [('kind', '!=', 'on_write')]}"> - <field name="filter_pre_domain" widget="domain" options="{'model': 'model'}" - class="o_row"/> - <div class="oe_edit_only o_row">Set selection based on a search filter: - <field name="filter_pre_id" domain="[('model_id', '=', model), '|', ('user_id', '=', uid), ('user_id', '=', False)]" - class="o_row" context="{'default_model_id': model, 'default_active': False, 'active_test': False}"/></div> - </div> - <label for="filter_id"/> - <div> - <field name="filter_domain" widget="domain" options="{'model': 'model'}" - class="o_row"/> - <div class="oe_edit_only o_row">Set selection based on a search filter: - <field name="filter_id" domain="[('model_id', '=', model), '|', ('user_id', '=', uid), ('user_id', '=', False)]" - class="o_row" context="{'default_model_id': model, 'default_active': False, 'active_test': False}"/> - </div> - </div> - <field name="on_change_fields" - attrs="{'invisible': [('kind', '!=', 'on_change')], 'required': [('kind', '=', 'on_change')]}"/> - <field name="trg_date_id" - attrs="{'invisible': [('kind', '!=', 'on_time')], 'required': [('kind', '=', 'on_time')]}"/> - <label for="trg_date_range" string="Delay After Trigger Date" - attrs="{'invisible': [('trg_date_id','=',False)]}"/> - <div attrs="{'invisible': [('trg_date_id','=',False)]}"> - <field name="trg_date_range" class="o_row"/> - <field name="trg_date_range_type" class="o_row" attrs="{'required': [('trg_date_id','!=',False)]}"/> - </div> - <field name="trg_date_calendar_id" class="o_row" - attrs="{'invisible': ['|', ('trg_date_id','=',False), ('trg_date_range_type', '!=', 'day')]}"/> - </group> - <div attrs="{'invisible': [('model', '=', False)]}"> - <p>Select when the action must be run, and choose records and/or timing conditions.</p> - <p groups="base.group_no_one">You may also use filters instead of choosing records. In order to create a new filter:</p> - <ul groups="base.group_no_one"> - <li>Go to your "Related Document Model" page and set the filter parameters in the "Search" view (Example of filter based on Leads/Opportunities: Creation Date "is equal to" 01/01/2012)</li> - <li>In this same "Search" view, select the menu "Save Current Filter", enter the name (Ex: Create the 01/01/2012) and add the option "Share with all users"</li> - </ul> - </div> - </page> - <page string="Actions"> - <group name="action_field" col="4" string="Fields to Change"> - <field name="act_user_id"/> - <field name="act_followers" widget="many2many_tags"/> - </group> - <group name="action_server" string="Server actions to run"> - <field name="server_action_ids" domain="[('model_id','=', model_id)]" nolabel="1" context="{'default_model_id': model_id}"> - <tree string="Server Actions"> - <field name="sequence"/> - <field name="name"/> - <field name="state"/> - </tree> - </field> - </group> - </page> - </notebook> - </sheet> - </form> + <xpath expr="//button[@name='create_action']" position="replace"> + <button name="create_action_for_menu" 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."/> + </xpath> + <xpath expr="//button[@name='unlink_action']" position="replace"> + <button name="unlink_action_for_menu" string="Remove Action" type="object" + attrs="{'invisible':[('menu_ir_values_id','=',False)]}" + help="Remove the contextual action related to this server action"/> + </xpath> + <xpath expr="//group[@name='action_wrapper']" position="inside"> + <group> + <field name="trigger"/> + + <label for="filter_pre_id" attrs="{'invisible': [('trigger', 'not in', ['on_write','on_create_or_write'])]}"/> + <div attrs="{'invisible': [('trigger', 'not in', ['on_write','on_create_or_write'])]}"> + <field name="filter_pre_domain" widget="domain" options="{'model': 'model_name'}" + class="o_row"/> + <div class="oe_edit_only o_row">Set selection based on a search filter: + <field name="filter_pre_id" domain="[('model_id', '=', model_name), '|', ('user_id', '=', uid), ('user_id', '=', False)]" + class="o_row" context="{'default_model_id': model_name, 'default_active': False, 'active_test': False}"/></div> + </div> + + <label for="filter_id"/> + <div> + <field name="filter_domain" widget="domain" options="{'model': 'model_name'}" + class="o_row"/> + <div class="oe_edit_only o_row">Set selection based on a search filter: + <field name="filter_id" domain="[('model_id', '=', model_name), '|', ('user_id', '=', uid), ('user_id', '=', False)]" + class="o_row" context="{'default_model_id': model_name, 'default_active': False, 'active_test': False}"/> + </div> + </div> + <field name="on_change_fields" + attrs="{'invisible': [('trigger', '!=', 'on_change')], 'required': [('trigger', '=', 'on_change')]}"/> + <field name="trg_date_id" + attrs="{'invisible': [('trigger', '!=', 'on_time')], 'required': [('trigger', '=', 'on_time')]}"/> + <label for="trg_date_range" + attrs="{'invisible': [('trigger', '!=', 'on_time')]}"/> + <div class="o_row" attrs="{'invisible': [('trigger', '!=', 'on_time')]}"> + <field name="trg_date_range" attrs="{'required': [('trigger','=','on_time')]}"/> + <field name="trg_date_range_type" attrs="{'required': [('trigger','=','on_time')]}"/> + </div> + <field name="trg_date_calendar_id" + attrs="{'invisible': ['|', ('trg_date_id','=',False), ('trg_date_range_type', '!=', 'day')]}"/> + </group> + </xpath> </field> </record> - <!-- Action Rule Tree View --> + <!-- automation Tree View --> <record id="view_base_automation_tree" model="ir.ui.view"> <field name="name">base.automation.tree</field> <field name="model">base.automation</field> <field name="arch" type="xml"> - <tree string="Action Rule"> - <field name="sequence"/> + <tree string="Automation"> + <field name="sequence" widget="handle"/> <field name="name"/> - <field name="kind"/> + <field name="trigger"/> <field name="model_id"/> - <field name="filter_id"/> </tree> </field> </record> - <record id="view_base_automation_search" model="ir.ui.view"> - <field name="name">base.automation.search</field> - <field name="model">base.automation</field> - <field name="arch" type="xml"> - <search> - <field name="name"/> - <field name="model_id"/> - </search> - </field> - </record> - - <!-- Action Rule Action --> + <!-- automation Action --> <record id="base_automation_act" model="ir.actions.act_window"> <field name="name">Automated Actions</field> <field name="res_model">base.automation</field> @@ -121,7 +81,7 @@ <field name="view_id" ref="view_base_automation_tree"/> <field name="help" type="html"> <p class="oe_view_nocontent_create"> - Click to setup a new automated action rule. + Click to setup a new automated automation. </p><p> Use automated actions to automatically trigger actions for various screens. Example: a lead created by a specific user may @@ -135,4 +95,4 @@ <menuitem id="menu_base_automation_form" parent="base.menu_automation" action="base_automation_act" sequence="1"/> -</odoo> +</odoo>
\ No newline at end of file |
