diff --git a/addons/base_action_rule/__openerp__.py b/addons/base_action_rule/__openerp__.py index fd2458c68eb..9a7e8c12ecb 100644 --- a/addons/base_action_rule/__openerp__.py +++ b/addons/base_action_rule/__openerp__.py @@ -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, } diff --git a/addons/base_action_rule/data/base_action_rule_data.xml b/addons/base_action_rule/data/base_action_rule_data.xml index 78988554e86..afcc01447a4 100644 --- a/addons/base_action_rule/data/base_action_rule_data.xml +++ b/addons/base_action_rule/data/base_action_rule_data.xml @@ -1,5 +1,5 @@ - + @@ -7,12 +7,12 @@ 4 hours -1 - - - - + + + + - + diff --git a/addons/base_action_rule/data/base_action_rule_demo.xml b/addons/base_action_rule/data/base_action_rule_demo.xml index c626cd41a3a..d59b7ba45ff 100644 --- a/addons/base_action_rule/data/base_action_rule_demo.xml +++ b/addons/base_action_rule/data/base_action_rule_demo.xml @@ -1,6 +1,5 @@ - - + Test lead in state 'draft' base.action.rule.lead.test @@ -50,5 +49,4 @@ - - + diff --git a/addons/base_action_rule/models/base_action_rule.py b/addons/base_action_rule/models/base_action_rule.py index 39dd7b1f3e3..9d731493e64 100644 --- a/addons/base_action_rule/models/base_action_rule.py +++ b/addons/base_action_rule/models/base_action_rule.py @@ -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() diff --git a/addons/base_action_rule/tests/test_models.py b/addons/base_action_rule/tests/test_models.py index 2bfb568d762..f88d61ccd2d 100644 --- a/addons/base_action_rule/tests/test_models.py +++ b/addons/base_action_rule/tests/test_models.py @@ -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') diff --git a/addons/base_action_rule/views/base_action_rule_view.xml b/addons/base_action_rule/views/base_action_rule_view.xml index 42c3cda41d2..41a1c264a3b 100644 --- a/addons/base_action_rule/views/base_action_rule_view.xml +++ b/addons/base_action_rule/views/base_action_rule_view.xml @@ -1,6 +1,5 @@ - - + base.action.rule.form @@ -12,7 +11,7 @@

- + @@ -26,25 +25,23 @@

Please choose the document type before setting the conditions.

- +
@@ -127,5 +124,4 @@ - - +