[MIG] base_action_rule: Migrated to new API

This commit provides some code simplifications, the complete
migration into new api.

Thanks to rco-odoo for the review, and code improvements.
This commit is contained in:
vbh-odoo
2016-04-21 14:52:50 +02:00
committed by Jérome Maes
parent 245f2f9101
commit e658fd6e1d
6 changed files with 207 additions and 300 deletions
+1 -3
View File
@@ -17,13 +17,11 @@ trigger an automatic reminder email.
""",
'depends': ['base', 'resource', 'mail'],
'data': [
'security/ir.model.access.csv',
'data/base_action_rule_data.xml',
'views/base_action_rule_view.xml',
'security/ir.model.access.csv',
],
'demo': [
'data/base_action_rule_demo.xml',
],
'installable': True,
'auto_install': False,
}
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<odoo>
<data noupdate="1">
<record id="ir_cron_crm_action" model="ir.cron">
@@ -7,12 +7,12 @@
<field name="interval_number">4</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'base.action.rule'" name="model"/>
<field eval="'_check'" name="function"/>
<field eval="'(True,)'" name="args"/>
<field name="doall" eval="False"/>
<field name="model" eval="'base.action.rule'"/>
<field name="function" eval="'_check'"/>
<field name="args" eval="'(True,)'"/>
<field name="active" eval="False" />
</record>
</data>
</openerp>
</odoo>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<record id="test_filter_draft" model="ir.filters">
<field name="name">Test lead in state 'draft'</field>
<field name="model_id">base.action.rule.lead.test</field>
@@ -50,5 +49,4 @@
<field name="model_id" ref="base_action_rule.model_base_action_rule_line_test"/>
<field name="act_user_id" ref="base.user_demo"/>
</record>
</data>
</openerp>
</odoo>
+168 -231
View File
@@ -1,20 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from datetime import datetime
from dateutil.relativedelta import relativedelta
import datetime as DT
import dateutil
import time
import datetime
import logging
import time
import traceback
from collections import defaultdict
import openerp
from openerp import SUPERUSER_ID
from openerp.modules.registry import RegistryManager
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval as eval
import dateutil
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, SUPERUSER_ID
from odoo.modules.registry import RegistryManager
from odoo.tools.safe_eval import safe_eval as eval
_logger = logging.getLogger(__name__)
@@ -26,103 +24,116 @@ DATE_RANGE_FUNCTION = {
False: lambda interval: relativedelta(0),
}
def get_datetime(date_str):
'''Return a datetime from a date string or a datetime string'''
# complete date time if date_str contains only a date
if ' ' not in date_str:
date_str = date_str + " 00:00:00"
return datetime.strptime(date_str, DEFAULT_SERVER_DATETIME_FORMAT)
class base_action_rule(osv.osv):
class BaseActionRule(models.Model):
""" Base Action Rules """
_name = 'base.action.rule'
_description = 'Action Rules'
_order = 'sequence'
_columns = {
'name': fields.char('Rule Name', required=True),
'model_id': fields.many2one('ir.model', 'Related Document Model',
required=True, domain=[('transient', '=', False)]),
'model': fields.related('model_id', 'model', type="char", string='Model'),
'create_date': fields.datetime('Create Date', readonly=1),
'active': fields.boolean('Active',
help="When unchecked, the rule is hidden and will not be executed."),
'sequence': fields.integer('Sequence',
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'),
'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.",
domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]"),
'trg_date_range': fields.integer('Delay after trigger date',
help="Delay after the trigger date." \
"You can put a negative number if you need a delay before the" \
"trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'),
('day', 'Days'), ('month', 'Months')], 'Delay type'),
'trg_date_calendar_id': fields.many2one(
'resource.calendar', '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.',
ondelete='set null',
),
'act_user_id': fields.many2one('res.users', '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)]",
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)]",
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('Last Run', readonly=1, copy=False),
'on_change_fields': fields.char(string="On Change Fields Trigger",
help="Comma-separated list of field names that triggers the onchange."),
}
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)
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)
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.""",
domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]")
trg_date_range = fields.Integer(string='Delay after trigger date',
help="""Delay after the trigger date.
You can put a negative number if you need a delay before the
trigger date, like sending a reminder 15 minutes before a meeting.""")
trg_date_range_type = fields.Selection([('minutes', 'Minutes'), ('hour', 'Hours'), ('day', 'Days'), ('month', 'Months')],
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)]",
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)]",
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']
_defaults = {
'active': True,
'trg_date_range_type': 'day',
}
@api.onchange('model_id')
def onchange_model_id(self):
self.filter_pre_id = self.filter_id = False
def onchange_kind(self, cr, uid, ids, kind, context=None):
clear_fields = []
if kind in ['on_create', 'on_create_or_write', 'on_unlink']:
clear_fields = ['filter_pre_id', 'filter_pre_domain', 'trg_date_id', 'trg_date_range', 'trg_date_range_type']
elif kind in ['on_write', 'on_create_or_write']:
clear_fields = ['trg_date_id', 'trg_date_range', 'trg_date_range_type']
elif kind == 'on_time':
clear_fields = ['filter_pre_id', 'filter_pre_domain']
return {'value': dict.fromkeys(clear_fields, False)}
@api.onchange('kind')
def onchange_kind(self):
if self.kind 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']:
self.trg_date_id = self.trg_date_range = self.trg_date_range_type = False
elif self.kind == 'on_time':
self.filter_pre_id = self.filter_pre_domain = False
def onchange_filter_pre_id(self, cr, uid, ids, filter_pre_id, context=None):
ir_filter = self.pool['ir.filters'].browse(cr, uid, filter_pre_id, context=context)
return {'value': {'filter_pre_domain': ir_filter.domain}}
@api.onchange('filter_pre_id')
def onchange_filter_pre_id(self):
self.filter_pre_domain = self.filter_pre_id.domain
def onchange_filter_id(self, cr, uid, ids, filter_id, context=None):
ir_filter = self.pool['ir.filters'].browse(cr, uid, filter_id, context=context)
return {'value': {'filter_domain': ir_filter.domain}}
@api.onchange('filter_id')
def onchange_filter_id(self):
self.filter_domain = self.filter_id.domain
@api.model
def create(self, vals):
base_action_rule = super(BaseActionRule, self).create(vals)
self._update_cron()
self._update_registry()
return base_action_rule
@api.multi
def write(self, vals):
res = super(BaseActionRule, self).write(vals)
if set(vals).intersection(self.CRITICAL_FIELDS):
self._update_cron()
self._update_registry()
return res
@api.multi
def unlink(self):
res = super(BaseActionRule, self).unlink()
self._update_cron()
self._update_registry()
return res
def _update_cron(self):
""" Activate the cron job depending on whether there exists action rules
based on time conditions.
"""
cron = self.env.ref('base_action_rule.ir_cron_crm_action', raise_if_not_found=False)
return cron and cron.toggle(model=self._name, domain=[('kind', '=', 'on_time')])
def _update_registry(self):
""" Update the registry after a modification on action rules. """
if self.env.registry.ready:
# for the sake of simplicity, simply force the registry to reload
self._cr.commit()
self.env.reset()
RegistryManager.new(self._cr.dbname)
RegistryManager.signal_registry_change(self._cr.dbname)
@openerp.api.model
def _get_actions(self, records, kinds):
""" Return the actions of the given kinds for records' model. The
returned actions' context contain an object to manage processing.
returned actions' context contain an object to manage processing.
"""
if '__action_done' not in self._context:
self = self.with_context(__action_done={})
@@ -130,49 +141,42 @@ class base_action_rule(osv.osv):
actions = self.with_context(active_test=True).search(domain)
return actions.with_env(self.env)
@openerp.api.model
def _get_eval_context(self):
""" Prepare the context used when evaluating python code
:returns: dict -- evaluation context given to (safe_)eval """
:returns: dict -- evaluation context given to (safe_)eval
"""
return {
'datetime': DT,
'datetime': datetime,
'dateutil': dateutil,
'time': time,
'uid': self.env.uid,
'user': self.env.user,
}
@openerp.api.model
def _filter_pre(self, records):
""" Filter the records that satisfy the precondition of action ``self``. """
if self.filter_pre_id and records:
eval_context = self._get_eval_context()
domain = [('id', 'in', records.ids)] + eval(self.filter_pre_id.domain, eval_context)
domain = [('id', 'in', records.ids)] + eval(self.filter_pre_id.domain, self._get_eval_context())
ctx = eval(self.filter_pre_id.context)
return records.with_context(**ctx).search(domain).with_env(records.env)
elif self.filter_pre_domain and records:
eval_context = self._get_eval_context()
domain = [('id', 'in', records.ids)] + eval(self.filter_pre_domain, eval_context)
domain = [('id', 'in', records.ids)] + eval(self.filter_pre_domain, self._get_eval_context())
return records.search(domain)
else:
return records
@openerp.api.model
def _filter_post(self, records):
""" Filter the records that satisfy the postcondition of action ``self``. """
if self.filter_id and records:
eval_context = self._get_eval_context()
domain = [('id', 'in', records.ids)] + eval(self.filter_id.domain, eval_context)
domain = [('id', 'in', records.ids)] + eval(self.filter_id.domain, self._get_eval_context())
ctx = eval(self.filter_id.context)
return records.with_context(**ctx).search(domain).with_env(records.env)
elif self.filter_domain and records:
eval_context = self._get_eval_context()
domain = [('id', 'in', records.ids)] + eval(self.filter_domain, eval_context)
domain = [('id', 'in', records.ids)] + eval(self.filter_domain, self._get_eval_context())
return records.search(domain)
else:
return records
@openerp.api.multi
def _process(self, records):
""" Process action ``self`` on the ``records`` that have not been done yet. """
# filter out the records on which self has already been done, then mark
@@ -186,7 +190,7 @@ class base_action_rule(osv.osv):
# modify records
values = {}
if 'date_action_last' in records._fields:
values['date_action_last'] = openerp.fields.Datetime.now()
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:
@@ -202,9 +206,10 @@ class base_action_rule(osv.osv):
ctx = {'active_model': record._name, 'active_ids': record.ids, 'active_id': record.id}
self.server_action_ids.with_context(**ctx).run()
def _register_hook(self, cr):
@api.model_cr
def _register_hook(self):
""" Patch models that should trigger action rules based on creation,
modification, deletion of records and form onchanges.
modification, deletion of records and form onchanges.
"""
#
# Note: the patched methods must be defined inside another function,
@@ -217,18 +222,15 @@ class base_action_rule(osv.osv):
def make_create():
""" Instanciate a create method that processes action rules. """
@openerp.api.model
@api.model
def create(self, vals, **kw):
# retrieve the action rules to possibly execute
actions = self.env['base.action.rule']._get_actions(self, ['on_create', 'on_create_or_write'])
# call original method
record = create.origin(self.with_env(actions.env), vals, **kw)
# check postconditions, and execute actions on the records that satisfy them
for action in actions.with_context(old_values=None):
action._process(action._filter_post(record))
return record.with_env(self.env)
return create
@@ -239,24 +241,20 @@ class base_action_rule(osv.osv):
# Note: we patch method _write() instead of write() in order to
# catch updates made by field recomputations.
#
@openerp.api.multi
@api.multi
def _write(self, vals, **kw):
# retrieve the action rules to possibly execute
actions = self.env['base.action.rule']._get_actions(self, ['on_write', 'on_create_or_write'])
records = self.with_env(actions.env)
# check preconditions on records
pre = {action: action._filter_pre(records) for action in actions}
# read old values before the update
old_values = {
old_vals.pop('id'): old_vals
for old_vals in records.read(list(vals))
}
# call original method
_write.origin(records, vals, **kw)
# check postconditions, and execute actions on the records that satisfy them
for action in actions.with_context(old_values=old_values):
action._process(action._filter_post(pre[action]))
@@ -266,16 +264,14 @@ class base_action_rule(osv.osv):
def make_unlink():
""" Instanciate an unlink method that processes action rules. """
@openerp.api.multi
@api.multi
def unlink(self, **kwargs):
# retrieve the action rules to possibly execute
actions = self.env['base.action.rule']._get_actions(self, ['on_unlink'])
records = self.with_env(actions.env)
# check conditions, and execute actions on the records that satisfy them
for action in actions:
action._process(action._filter_post(pre[action]))
action._process(action._filter_post(records))
# call original method
return unlink.origin(self, **kwargs)
@@ -285,17 +281,17 @@ class base_action_rule(osv.osv):
""" Instanciate an onchange method for the given action rule. """
def base_action_rule_onchange(self):
action_rule = self.env['base.action.rule'].browse(action_rule_id)
server_actions = action_rule.server_action_ids.with_context(active_model=self._name, onchange_self=self)
result = {}
for server_action in server_actions:
for server_action in action_rule.server_action_ids.with_context(active_model=self._name, onchange_self=self):
res = server_action.run()
if res and 'value' in res:
res['value'].pop('id', None)
self.update(self._convert_to_cache(res['value'], validate=False))
if res and 'domain' in res:
result.setdefault('domain', {}).update(res['domain'])
if res and 'warning' in res:
result['warning'] = res['warning']
if res:
if 'value' in res:
res['value'].pop('id', None)
self.update(self._convert_to_cache(res['value'], validate=False))
if 'domain' in res:
result.setdefault('domain', {}).update(res['domain'])
if 'warning' in res:
result['warning'] = res['warning']
return result
return base_action_rule_onchange
@@ -308,146 +304,87 @@ class base_action_rule(osv.osv):
model._patch_method(name, method)
# retrieve all actions, and patch their corresponding model
ids = self.search(cr, SUPERUSER_ID, [])
for action_rule in self.browse(cr, SUPERUSER_ID, ids):
model = action_rule.model_id.model
model_obj = self.pool.get(model)
if not model_obj:
continue
for action_rule in self.with_context({}).search([]):
Model = self.env[action_rule.model]
if action_rule.kind == 'on_create':
patch(model_obj, 'create', make_create())
patch(Model, 'create', make_create())
elif action_rule.kind == 'on_create_or_write':
patch(model_obj, 'create', make_create())
patch(model_obj, '_write', make_write())
patch(Model, 'create', make_create())
patch(Model, '_write', make_write())
elif action_rule.kind == 'on_write':
patch(model_obj, '_write', make_write())
patch(Model, '_write', make_write())
elif action_rule.kind == 'on_unlink':
patch(model_obj, 'unlink', make_unlink())
patch(Model, 'unlink', make_unlink())
elif action_rule.kind == '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(","):
field_name = field_name.strip()
model_obj._onchange_methods[field_name].append(method)
Model._onchange_methods[field_name.strip()].append(method)
def _update_cron(self, cr, uid, context=None):
""" Activate the cron job depending on whether there exists action rules
based on time conditions. """
try:
cron = self.pool['ir.model.data'].get_object(
cr, uid, 'base_action_rule', 'ir_cron_crm_action', context=context)
except ValueError:
return False
return cron.toggle(model=self._name, domain=[('kind', '=', 'on_time')])
def _update_registry(self, cr, uid, context=None):
""" Update the registry after a modification on action rules. """
if self.pool.ready:
# for the sake of simplicity, simply force the registry to reload
cr.commit()
openerp.api.Environment.reset()
RegistryManager.new(cr.dbname)
RegistryManager.signal_registry_change(cr.dbname)
def create(self, cr, uid, vals, context=None):
res_id = super(base_action_rule, self).create(cr, uid, vals, context=context)
self._update_cron(cr, uid, context=context)
self._update_registry(cr, uid, context=context)
return res_id
def write(self, cr, uid, ids, vals, context=None):
super(base_action_rule, self).write(cr, uid, ids, vals, context=context)
if set(vals) & set(self.CRITICAL_FIELDS):
self._update_cron(cr, uid, context=context)
self._update_registry(cr, uid, context=context)
return True
def unlink(self, cr, uid, ids, context=None):
res = super(base_action_rule, self).unlink(cr, uid, ids, context=context)
self._update_cron(cr, uid, context=context)
self._update_registry(cr, uid, context=context)
return res
def onchange_model_id(self, cr, uid, ids, model_id, context=None):
data = {'model': False, 'filter_pre_id': False, 'filter_id': False}
if model_id:
model = self.pool.get('ir.model').browse(cr, uid, model_id, context=context)
data.update({'model': model.model})
return {'value': data}
def _check_delay(self, cr, uid, action, record, record_dt, context=None):
@api.model
def _check_delay(self, action, record, record_dt):
if action.trg_date_calendar_id and action.trg_date_range_type == 'day':
start_dt = get_datetime(record_dt)
action_dt = self.pool['resource.calendar'].schedule_days_get_date(
cr, uid, action.trg_date_calendar_id.id, action.trg_date_range,
day_date=start_dt, compute_leaves=True, context=context
)
return action.trg_date_calendar_id.schedule_days_get_date(
action.trg_date_range,
day_date=fields.Datetime.from_string(record_dt),
compute_leaves=True,
)[0]
else:
delay = DATE_RANGE_FUNCTION[action.trg_date_range_type](action.trg_date_range)
action_dt = get_datetime(record_dt) + delay
return action_dt
return fields.Datetime.from_string(record_dt) + delay
def _check(self, cr, uid, automatic=False, use_new_cursor=False, context=None):
@api.model
def _check(self, automatic=False, use_new_cursor=False):
""" This Function is called by scheduler. """
context = context or {}
if '__action_done' not in context:
context = dict(context, __action_done={})
if '__action_done' not in self._context:
self = self.with_context(__action_done={})
# retrieve all the action rules to run based on a timed condition
action_dom = [('kind', '=', 'on_time')]
action_ids = self.search(cr, uid, action_dom, context=dict(context, active_test=True))
eval_context = self._get_eval_context(cr, uid, context=context)
for action in self.browse(cr, uid, action_ids, context=context):
now = datetime.now()
if action.last_run:
last_run = get_datetime(action.last_run)
else:
last_run = datetime.utcfromtimestamp(0)
eval_context = self._get_eval_context()
for action in self.with_context(active_test=True).search([('kind', '=', '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
model = self.pool[action.model_id.model]
domain = []
ctx = dict(context)
if action.filter_domain is not False:
context = dict(self._context)
if action.filter_domain:
domain = eval(action.filter_domain, eval_context)
elif action.filter_id:
domain = eval(action.filter_id.domain, eval_context)
ctx.update(eval(action.filter_id.context))
if 'lang' not in ctx:
context.update(eval(action.filter_id.context))
if 'lang' not in context:
# Filters might be language-sensitive, attempt to reuse creator lang
# as we are usually running this as super-user in background
[filter_meta] = action.filter_id.get_metadata()
user_id = filter_meta['write_uid'] and filter_meta['write_uid'][0] or \
filter_meta['create_uid'][0]
ctx['lang'] = self.pool['res.users'].browse(cr, uid, user_id).lang
record_ids = model.search(cr, uid, domain, context=ctx)
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)
# determine when action should occur for the records
date_field = action.trg_date_id.name
if date_field == 'date_action_last' and 'create_date' in model._fields:
get_record_dt = lambda record: record[date_field] or record.create_date
if action.trg_date_id.name == 'date_action_last' and 'create_date' in records._fields:
get_record_dt = lambda record: record[action.trg_date_id.name] or record.create_date
else:
get_record_dt = lambda record: record[date_field]
get_record_dt = lambda record: record[action.trg_date_id.name]
# process action on the records that should be executed
for record in model.browse(cr, uid, record_ids, context=context):
now = datetime.datetime.now()
for record in records:
record_dt = get_record_dt(record)
if not record_dt:
continue
action_dt = self._check_delay(cr, uid, action, record, record_dt, context=context)
action_dt = self._check_delay(action, record, record_dt)
if last_run <= action_dt < now:
try:
action._process(record)
except Exception:
import traceback
_logger.error(traceback.format_exc())
action.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)})
action.write({'last_run': fields.Datetime.now()})
if automatic:
# auto-commit for batch processing
cr.commit()
self._cr.commit()
+16 -38
View File
@@ -1,51 +1,29 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp
from openerp.osv import fields, osv
from openerp import api
from odoo import fields, models
AVAILABLE_STATES = [
('draft', 'New'),
('cancel', 'Cancelled'),
('open', 'In Progress'),
('pending', 'Pending'),
('done', 'Closed')
]
class lead_test(osv.Model):
class LeadTest(models.Model):
_name = "base.action.rule.lead.test"
_description = "Action Rule Test"
_columns = {
'name': fields.char('Subject', required=True, select=1),
'user_id': fields.many2one('res.users', 'Responsible'),
'state': fields.selection(AVAILABLE_STATES, string="Status", readonly=True),
'active': fields.boolean('Active', required=False),
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null'),
'date_action_last': fields.datetime('Last Action', readonly=1),
'line_ids': fields.one2many('base.action.rule.line.test', 'lead_id'),
}
_defaults = {
'state' : 'draft',
'active' : True,
}
customer = openerp.fields.Boolean(related='partner_id.customer', readonly=True, store=True)
@api.cr_uid_ids_context
def message_post(self, cr, uid, thread_id, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
pass
def message_subscribe(self, cr, uid, ids, partner_ids=None, channel_ids=None, subtype_ids=None, force=True, context=None):
pass
name = fields.Char(string='Subject', required=True, index=True)
user_id = fields.Many2one('res.users', string='Responsible')
state = fields.Selection([('draft', 'New'), ('cancel', 'Cancelled'), ('open', 'In Progress'),
('pending', 'Pending'), ('done', 'Closed')],
string="Status", readonly=True, default='draft')
active = fields.Boolean(default=True)
partner_id = fields.Many2one('res.partner', string='Partner')
date_action_last = fields.Datetime(string='Last Action', readonly=True)
customer = fields.Boolean(related='partner_id.customer', readonly=True, store=True)
line_ids = fields.One2many('base.action.rule.line.test', 'lead_id')
class line_test(osv.Model):
class LineTest(models.Model):
_name = "base.action.rule.line.test"
_description = "Action Rule Line Test"
name = openerp.fields.Char()
lead_id = openerp.fields.Many2one('base.action.rule.lead.test', ondelete='cascade')
user_id = openerp.fields.Many2one('res.users')
name = fields.Char()
lead_id = fields.Many2one('base.action.rule.lead.test', ondelete='cascade')
user_id = fields.Many2one('res.users')
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<!-- Action Rule Form View -->
<record id="view_base_action_rule_form" model="ir.ui.view">
<field name="name">base.action.rule.form</field>
@@ -12,7 +11,7 @@
<h1><field name="name"/></h1>
<group>
<group>
<field name="model_id" on_change="onchange_model_id(model_id, context)"/>
<field name="model_id"/>
<field name="model" invisible="1"/>
</group>
<group>
@@ -26,25 +25,23 @@
<p><b>Please choose the document type before setting the conditions.</b></p>
</group>
<group attrs="{'invisible': [('model', '=', False)]}">
<field name="kind" required="1"
on_change="onchange_kind(kind)"/>
<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="char_domain" options="{'model_field': 'model'}"
class="oe_inline"/>
<div class="oe_edit_only oe_inline">Set selection based on a search filter:
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="oe_inline" context="{'default_model_id': model, 'default_active': False, 'active_test': False}"
on_change="onchange_filter_pre_id(filter_pre_id, context)"/></div>
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="char_domain" options="{'model_field': 'model'}"
class="oe_inline"/>
<div class="oe_edit_only oe_inline">Set selection based on a search filter:
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="oe_inline" context="{'default_model_id': model, 'default_active': False, 'active_test': False}"
on_change="onchange_filter_id(filter_id, context)"/></div>
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')]}"/>
@@ -53,10 +50,10 @@
<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="oe_inline"/>
<field name="trg_date_range_type" class="oe_inline" attrs="{'required': [('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="oe_inline"
<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)]}">
@@ -127,5 +124,4 @@
<menuitem id="menu_base_action_rule_form"
parent="base.menu_automation" action="base_action_rule_act" sequence="1"/>
</data>
</openerp>
</odoo>