summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/base_automation/data/base_automation_demo.xml36
-rw-r--r--addons/base_automation/models/__init__.py1
-rw-r--r--addons/base_automation/models/base_automation.py107
-rw-r--r--addons/base_automation/models/ir_actions.py10
-rw-r--r--addons/base_automation/security/ir.model.access.csv2
-rw-r--r--addons/base_automation/views/base_automation_view.xml158
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