[IMP] Base Action Rule (only filter conditions)

bzr revid: api@openerp.com-20121128092928-7kbs1d9g8ung4oul
This commit is contained in:
Arnaud Pineux 2012-11-28 10:29:28 +01:00
parent a8f3510297
commit 3df8c5fe9a
9 changed files with 228 additions and 135 deletions

View File

@ -20,5 +20,6 @@
##############################################################################
import base_action_rule
import test_models
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -97,7 +97,8 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
\ne.g.: 'urgent.*' will search for records having name starting with the string 'urgent'\
\nNote: This is case sensitive search."),
'server_action_ids': fields.one2many('ir.actions.server', 'action_rule_id', 'Server Action', help="Define Server actions.\neg:Email Reminders, Call Object Service, etc.."), #TODO: set domain [('model_id','=',model_id)]
'filter_id':fields.many2one('ir.filters', 'Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)]
'filter_id':fields.many2one('ir.filters', 'Precondition Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)]
'filter_post_id': fields.many2one('ir.filters', 'Postcondition Filter', required=False),
'last_run': fields.datetime('Last Run', readonly=1),
}
@ -110,7 +111,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
_order = 'sequence'
def post_action(self, cr, uid, ids, model, old_records=None, context=None):
def post_action(self, cr, uid, ids, model, precondition_ok=None, context=None):
# Searching for action rules
cr.execute("SELECT model.model, rule.id FROM base_action_rule rule \
LEFT JOIN ir_model model on (model.id = rule.model_id) \
@ -122,7 +123,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
# If the rule doesn't involve a time condition, run it immediately
# Otherwise we let the scheduler run the action
if self.browse(cr, uid, rule_id, context=context).trg_date_type == 'none':
self._action(cr, uid, [rule_id], model_pool.browse(cr, uid, ids, context=context), old_records=old_records, context=context)
self._action(cr, uid, [rule_id], model_pool.browse(cr, uid, ids, context=context), precondition_ok=precondition_ok, context=context)
return True
def _create(self, old_create, model, context=None):
@ -134,8 +135,17 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
if context is None:
context = {}
new_id = old_create(cr, uid, vals, context=context)
#As it is a new record, we can assume that the precondition is true for every filter.
#(There is nothing before the create so no condition)
precondition_ok = {}
precondition_ok[new_id] = {}
for action in self.browse(cr, uid, self.search(cr, uid, [], context=context), context=context):
if action.filter_id:
precondition_ok[new_id][action.id] = False
else:
precondition_ok[new_id][action.id] = True
if not context.get('action'):
self.post_action(cr, uid, [new_id], model, context=context)
self.post_action(cr, uid, [new_id], model, precondition_ok=precondition_ok, context=context)
return new_id
return wrapper
@ -151,15 +161,22 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
if isinstance(ids, (str, int, long)):
ids = [ids]
model_pool = self.pool.get(model)
# get the old records (before the write)
if model and ids:
old_records = model_pool.browse(cr,uid,ids,context=context)
# look at records' states to fill the record cache
for record in old_records:
record.state
# We check for the pre-filter. We must apply it before the write
precondition_ok = {}
for id in ids:
precondition_ok[id] = {}
for action in self.browse(cr, uid, self.search(cr, uid, [], context=context), context=context):
precondition_ok[id][action.id] = True
if action.filter_id and action.model_id.model == action.filter_id.model_id:
ctx = dict(context)
ctx.update(eval(action.filter_id.context))
obj_ids = []
if self.pool.get(action.model_id.model)!=None:
obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_id.domain), context=ctx)
precondition_ok[id][action.id] = id in obj_ids
old_write(cr, uid, ids, vals, context=context)
if not context.get('action'):
self.post_action(cr, uid, ids, model, old_records=old_records, context=context)
self.post_action(cr, uid, ids, model, precondition_ok=precondition_ok, context=context)
return True
return wrapper
@ -263,15 +280,15 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
def do_check(self, cr, uid, action, obj, old_records=None, context=None):
def do_check(self, cr, uid, action, obj, precondition_ok=True, context=None):
""" check Action """
if context is None:
context = {}
ok = True
if action.filter_id and action.model_id.model == action.filter_id.model_id:
ok = precondition_ok
if action.filter_post_id and action.model_id.model == action.filter_post_id.model_id:
ctx = dict(context)
ctx.update(eval(action.filter_id.context))
obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_id.domain), context=ctx)
ctx.update(eval(action.filter_post_id.context))
obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_post_id.domain), context=ctx)
ok = ok and obj.id in obj_ids
if getattr(obj, 'user_id', False):
ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==obj.user_id.id)
@ -284,22 +301,6 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
(action.trg_partner_categ_id.id in map(lambda x: x.id, obj.partner_id.category_id or []))
)
)
#state_to = the state after a write or a create
state_to = getattr(obj, 'state', False)
#state_from = nothing or the state of old_records
state_from = ""
if old_records != None:
for record in old_records:
state_from = record.state
else:
state_from = "na" #it means that there was no state before (creation for example)
#if we have an action that check the status
if action.trg_state_to:
if action.trg_state_from:
ok = ok and action.trg_state_from==state_from
else:
ok = state_from!=state_to
ok = ok and action.trg_state_to==state_to
reg_name = action.regex_name
result_name = True
if reg_name:
@ -341,7 +342,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
model_obj.message_subscribe(cr, uid, [obj.id], new_followers, context=context)
return True
def _action(self, cr, uid, ids, objects, scrit=None, old_records=None, context=None):
def _action(self, cr, uid, ids, objects, scrit=None, precondition_ok=None, context=None):
""" Do Action """
if context is None:
context = {}
@ -350,9 +351,11 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
objects = [objects]
for action in self.browse(cr, uid, ids, context=context):
for obj in objects:
if self.do_check(cr, uid, action, obj, old_records=old_records, context=context):
ok = True
if precondition_ok!=None:
ok = precondition_ok[obj.id][action.id]
if self.do_check(cr, uid, action, obj, precondition_ok=ok, context=context):
self.do_action(cr, uid, action, obj, context=context)
context.update({'action': False})
return True

View File

@ -15,36 +15,31 @@
<sheet>
<group col="4">
<field name="name"/>
<field name="model_id"/>
<field name="filter_id" domain="[('model_id','=',model), ('user_id', '=', False)]" context="{'default_model_id': model}"/>
<field name="sequence"/>
<field name="active"/>
<field name="model_id"/>
<field name="model" invisible="1"/>
<field name="sequence" invisible="1"/>
</group>
<notebook>
<page string="Conditions">
<p class="oe_grey">
Select a filter or a timer as condition.<br/>
To create a new filter,<br/>
- Go to your Model's page and set the filter parameters in the "Search" view<br/>
- In this same "Search" view, select the menu "Save Current Filter", enter the name and add the option "Share with all users"<br/>
The filter must therefore be available in this page.
</p>
<group>
<group name="model" string="Conditions on Model Fields">
<field name="regex_name"/>
<field name="trg_user_id"/>
<group name="filter" string="Filter Condition">
<field name="filter_id" domain="[('model_id','=',model), ('user_id', '=', False)]" context="{'default_model_id': model}"/>
<field name="filter_post_id" domain="[('model_id','=',model), ('user_id', '=', False)]" context="{'default_model_id': model}"/>
</group>
<group name="partner" string="Conditions on Model Partner">
<field name="trg_partner_id"/>
<field name="trg_partner_categ_id"/>
</group>
<group name="state" string="Conditions on Status">
<field name="trg_state_to"/>
<field name="trg_state_from" attrs="{'invisible': ['|',('trg_state_to', '=', ' '),('trg_state_to', '=', '')]}"/>
</group>
<group name="timing" string="Conditions on Timing">
<group name="timing" string="Timer">
<field name="trg_date_type"/>
<field name="trg_date_range" string="Delay After Trigger Date" attrs="{'invisible': [('trg_date_type', '=', 'none')]}"/>
<field name="trg_date_range_type" attrs="{'invisible': [('trg_date_type', '=', 'none')]}"/>
</group>
</group>
<group string="Note">
<label string="The rule uses the AND operator. The model must match all non-empty fields so that the rule executes the action described in the 'Actions' tab." />
</group>
</page>
<page string="Actions">
<group name="action_followers">
@ -63,7 +58,6 @@
</tree>
</field>
</group>
</page>
</notebook>
</sheet>

View File

@ -0,0 +1,32 @@
from osv import osv, fields
AVAILABLE_STATES = [
('draft', 'New'),
('cancel', 'Cancelled'),
('open', 'In Progress'),
('pending', 'Pending'),
('done', 'Closed')
]
class lead_test(osv.Model):
_name = "base.action.rule.lead.test"
_columns = {
'name': fields.char('Subject', size=64, 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),
}
_defaults = {
'state' : 'draft',
'active' : True,
}
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
pass
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
pass

View File

@ -0,0 +1,142 @@
import tools
from openerp.tests import common
from .. import test_models
class base_action_rule_test(common.TransactionCase):
def setUp(self):
"""*****setUp*****"""
super(base_action_rule_test, self).setUp()
cr, uid = self.cr, self.uid
self.demo_user = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'user_demo')
self.admin_user = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'user_admin')
def create_filter_done(self, cr, uid, context=None):
filter_pool = self.registry('ir.filters')
return filter_pool.create(cr, uid, {
'name': "Lead is in done state",
'is_default': False,
'model_id': 'base.action.rule.lead.test',
'domain' : "[('state','=','done')]",
}, context=context)
def create_filter_draft(self, cr, uid, context=None):
filter_pool = self.registry('ir.filters')
return filter_pool.create(cr, uid, {
'name': "Lead is in draft state",
'is_default': False,
'model_id': "base.action.rule.lead.test",
'domain' : "[('state','=','draft')]",
}, context=context)
def create_lead_test_1(self, cr, uid, context=None):
"""
Create a new lead_test
"""
lead_pool = self.registry('base.action.rule.lead.test')
return lead_pool.create(cr, uid, {
'name': "Lead Test 1",
'user_id': self.admin_user[1],
}, context=context)
def create_rule(self, cr, uid, filter_id=None, filter_post_id=None, context=None):
"""
The "Rule 1" says that when a lead goes to the 'draft' state, the responsible for that lead changes to "demo_user"
"""
self.action_pool = self.registry('base.action.rule')
return self.action_pool.create(cr,uid,{
'name' : "Rule 1",
'model_id': self.registry('ir.model').search(cr, uid, [('model','=','base.action.rule.lead.test')], context=context)[0],
'active' : 1,
'trg_date_type' : 'none',
'filter_post_id' : filter_post_id,
'filter_id' : filter_id,
'act_user_id': self.demo_user[1],
}, context=context)
def test_00_check_to_state_draft_pre(self):
"""
Check that a new record (with state = draft) doesn't change its responsible when there is a precondition filter which check that the state is draft.
"""
cr, uid = self.cr, self.uid
context = {}
filter_draft = self.create_filter_draft(cr, uid, context=context)
rule_1_id = self.create_rule(cr, uid, filter_id=filter_draft, context=context)
new_lead_id = self.create_lead_test_1(cr,uid,context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='draft')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context))
def test_01_check_to_state_draft_post(self):
"""
Check that a new record (with state = draft) changes its responsible when there is a postcondition filter which check that the state is draft.
"""
cr, uid = self.cr, self.uid
context = {}
filter_draft = self.create_filter_draft(cr, uid, context=context)
rule_1_id = self.create_rule(cr, uid, filter_post_id=filter_draft, context=context)
new_lead_id = self.create_lead_test_1(cr,uid,context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='draft')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo_user[1], context=context))
def test_02_check_from_draft_to_done_with_steps(self):
"""
A new record will be created and will goes from draft to done state via the other states (open, pending and cancel)
We will create a rule that says in precondition that the record must be in the "draft" state while a postcondition filter says
that the record will be done. If the state goes from 'draft' to 'done' the responsible will change. If those two conditions aren't
verified, the responsible will stay the same
The responsible in that test will never change
"""
cr, uid = self.cr, self.uid
context = {}
filter_draft = self.create_filter_draft(cr, uid, context=context)
filter_done = self.create_filter_done(cr, uid, context=context)
self.create_rule(cr, uid, filter_id=filter_draft, filter_post_id=filter_done, context=context)
new_lead_id = self.create_lead_test_1(cr,uid,context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='draft')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context))
""" change the state of new_lead to open and check that responsible doen't change"""
new_lead.write({'state': 'open'}, context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='open')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context))
""" change the state of new_lead to pending and check that responsible doen't change"""
new_lead.write({'state': 'pending'}, context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='pending')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context))
""" change the state of new_lead to cancel and check that responsible doen't change"""
new_lead.write({'state': 'cancel'}, context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='cancel')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context))
""" change the state of new_lead to done and check that responsible doen't change """
new_lead.write({'state': 'done'}, context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='done')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context))
def test_02_check_from_draft_to_done_without_steps(self):
"""
A new record will be created and will goes from draft to done in one operation
We will create a rule that says in precondition that the record must be in the "draft" state while a postcondition filter says
that the record will be done. If the state goes from 'draft' to 'done' the responsible will change. If those two conditions aren't
verified, the responsible will stay the same
The responsible in that test will change to "demo_user"
"""
cr, uid = self.cr, self.uid
context = {}
filter_draft = self.create_filter_draft(cr, uid, context=context)
filter_done = self.create_filter_done(cr, uid, context=context)
self.create_rule(cr, uid, filter_id=filter_draft, filter_post_id=filter_done, context=context)
new_lead_id = self.create_lead_test_1(cr,uid,context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='draft')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context))
""" change the state of new_lead to done and check that responsible change to Demo_user"""
new_lead.write({'state': 'done'}, context=context)
new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context)
self.assertTrue(new_lead.state=='done')
self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo_user[1], context=context))

View File

@ -43,8 +43,8 @@ class base_action_rule(osv.osv):
'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'),
}
def do_check(self, cr, uid, action, obj, old_records=None, context=None):
ok = super(base_action_rule, self).do_check(cr, uid, action, obj, old_records=old_records, context=context)
def do_check(self, cr, uid, action, obj, precondition_ok=True, context=None):
ok = super(base_action_rule, self).do_check(cr, uid, action, obj, precondition_ok=precondition_ok, context=context)
if hasattr(obj, 'section_id'):
ok = ok and (not action.trg_section_id or action.trg_section_id.id == obj.section_id.id)

View File

@ -7,16 +7,6 @@
<field name="model">base.action.rule</field>
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='partner']" position="after">
<group name="case" string="Conditions on Case Fields">
<field name="trg_section_id" widget="selection"/>
<field name="trg_categ_id"/>
</group>
<group name="communication" string="Conditions on Communication History">
<field name="regex_history"/>
<field name="trg_max_history"/>
</group>
</xpath>
<xpath expr="//field[@name='act_user_id']" position="after">
<field name="act_section_id"/>
<field name="act_categ_id"/>

View File

@ -1,69 +0,0 @@
import tools
from openerp.tests import common
class base_action_rule_test(common.TransactionCase):
def setUp(self):
"""*****setUp*****"""
super(base_action_rule_test, self).setUp()
cr, uid = self.cr, self.uid
"""*****TeamCreation******"""
self.team_pool = self.registry('crm.case.section')
self.team_1_id = self.team_pool.create(cr, uid, {
'name' : "Team 1",
'active' : 1,
'user_id' : uid,
'complete_name' : "Test / Team 1",
}, context=None)
self.team_2_id = self.team_pool.create(cr, uid, {
'name' : "Team 2",
'active' : 1,
'user_id' : uid,
'complete_name' : "Test / Team 2",
}, context=None)
def create_rule_1(self, cr, uid, context=None):
"""
The "Rule 1" says that when a lead goes to the 'open' state, the team responsible for that lead changes to "Team 1"
"""
self.action_pool = self.registry('base.action.rule')
self.rule_1_id = self.action_pool.create(cr,uid,{
'name' : "Rule 1",
'model_id' : self.registry('ir.model').search(cr, uid, [('model','=','crm.lead')], context=context)[0],
'active' : 1,
'trg_state_to' : 'open',
'trg_date_type' : 'none',
'act_section_id' : self.team_1_id,
}, context=context)
def create_rule_2(self, cr, uid, context=None):
"""
The "Rule 2" says that when a lead goes from 'open' state to 'done' state, the team responsible for that lead changes to "Team 2"
"""
self.action_pool = self.registry('base.action.rule')
self.rule_2_id = self.action_pool.create(cr,uid,{
'name' : "Rule 2",
'model_id' : self.registry('ir.model').search(cr, uid, [('model','=','crm.lead')], context=context)[0],
'active' : 1,
'trg_state_to' : 'done',
'trg_state_from' : 'open',
'trg_date_type' : 'none',
'act_section_id' : self.team_2_id,
}, context=context)
def test_00_check_to_state_draft(self):
"""
This test check that the team change when the state is open but doesn't change when the state is different
"""
cr, uid = self.cr, self.uid
context = {}
#First we need to create the Rule 1
self.create_rule_1(cr, uid, context)
#Once it is done, we can create a new lead (with a state = 'draft')
#first we get our team 1
self.team_1 = self.team_pool.browse(cr,uid,self.team_1_id,context=context)
#We get a lead
self.lead_1 = self.registry('ir.model.data').get_object_reference(cr, uid, 'crm', 'crm_case_1')
#We change the team of crm_case_1