2013-10-23 12:53:43 +00:00
2006-12-07 13:41:40 +00:00
##############################################################################
2010-06-18 10:16:41 +00:00
#
2009-10-14 12:32:15 +00:00
# OpenERP, Open Source Management Solution
2014-01-09 09:50:17 +00:00
# Copyright (C) 2004-2014 OpenERP S.A. (<http://openerp.com).
2008-06-16 07:24:04 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 12:32:15 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-10-14 12:32:15 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 12:32:15 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-06-18 10:16:41 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
##############################################################################
2006-12-07 13:41:40 +00:00
#
# TODO:
2008-12-09 12:37:22 +00:00
# cr.execute('delete from wkf_triggers where model=%s and res_id=%s', (res_type,res_id))
2006-12-07 13:41:40 +00:00
#
2013-03-19 16:31:33 +00:00
import logging
2006-12-07 13:41:40 +00:00
import instance
2013-10-23 13:07:05 +00:00
from openerp . workflow . helpers import Session
from openerp . workflow . helpers import Record
2013-10-23 13:58:38 +00:00
from openerp . workflow . helpers import WorkflowActivity
2013-03-19 16:31:33 +00:00
logger = logging . getLogger ( __name__ )
2006-12-07 13:41:40 +00:00
2013-10-23 13:07:05 +00:00
import openerp
from openerp . tools . safe_eval import safe_eval as eval
2013-10-23 13:58:38 +00:00
class Environment ( dict ) :
2013-10-23 13:07:05 +00:00
"""
Dictionary class used as an environment to evaluate workflow code ( such as
the condition on transitions ) .
This environment provides sybmols for cr , uid , id , model name , model
instance , column names , and all the record ( the one obtained by browsing
the provided ID ) attributes .
"""
2013-10-23 13:58:38 +00:00
def __init__ ( self , session , record ) :
self . cr = session . cr
self . uid = session . uid
self . model = record . model
self . id = record . id
self . ids = [ record . id ]
self . obj = openerp . registry ( self . cr . dbname ) [ self . model ]
2013-10-23 13:07:05 +00:00
def __getitem__ ( self , key ) :
2014-07-06 14:44:26 +00:00
records = self . obj . browse ( self . cr , self . uid , self . ids )
if hasattr ( records , key ) :
return getattr ( records , key )
2013-10-23 13:07:05 +00:00
else :
2013-10-23 13:58:38 +00:00
return super ( Environment , self ) . __getitem__ ( key )
2013-10-23 13:07:05 +00:00
2013-10-23 13:58:38 +00:00
class WorkflowItem ( object ) :
2013-10-24 07:57:07 +00:00
def __init__ ( self , session , record , work_item_values ) :
2013-10-23 13:58:38 +00:00
assert isinstance ( session , Session )
assert isinstance ( record , Record )
self . session = session
self . record = record
2013-10-24 07:57:07 +00:00
if not work_item_values :
work_item_values = { }
assert isinstance ( work_item_values , dict )
self . workitem = work_item_values
@classmethod
def create ( cls , session , record , activity , instance_id , stack ) :
assert isinstance ( session , Session )
assert isinstance ( record , Record )
assert isinstance ( activity , dict )
2013-10-23 13:58:38 +00:00
assert isinstance ( instance_id , ( long , int ) )
assert isinstance ( stack , list )
2013-10-24 07:57:07 +00:00
cr = session . cr
2008-07-22 14:24:36 +00:00
cr . execute ( " select nextval( ' wkf_workitem_id_seq ' ) " )
id_new = cr . fetchone ( ) [ 0 ]
2013-10-24 07:57:07 +00:00
cr . execute ( " insert into wkf_workitem (id,act_id,inst_id,state) values ( %s , %s , %s , ' active ' ) " , ( id_new , activity [ ' id ' ] , instance_id ) )
2008-12-09 12:37:22 +00:00
cr . execute ( ' select * from wkf_workitem where id= %s ' , ( id_new , ) )
2013-10-24 07:57:07 +00:00
work_item_values = cr . dictfetchone ( )
2013-03-19 16:31:33 +00:00
logger . info ( ' Created workflow item in activity %s ' ,
2013-10-24 07:57:07 +00:00
activity [ ' id ' ] ,
extra = { ' ident ' : ( session . uid , record . model , record . id ) } )
2006-12-07 13:41:40 +00:00
2013-10-24 07:57:07 +00:00
workflow_item = WorkflowItem ( session , record , work_item_values )
workflow_item . process ( stack = stack )
2013-03-26 14:39:37 +00:00
2013-10-24 07:57:07 +00:00
@classmethod
def create_all ( cls , session , record , activities , instance_id , stack ) :
assert isinstance ( activities , list )
2008-07-22 14:24:36 +00:00
2013-10-24 07:57:07 +00:00
for activity in activities :
cls . create ( session , record , activity , instance_id , stack )
2013-10-23 13:58:38 +00:00
2013-10-24 07:57:07 +00:00
def process ( self , signal = None , force_running = False , stack = None ) :
assert isinstance ( force_running , bool )
2013-10-23 13:58:38 +00:00
assert stack is not None
cr = self . session . cr
2013-10-24 07:57:07 +00:00
cr . execute ( ' select * from wkf_activity where id= %s ' , ( self . workitem [ ' act_id ' ] , ) )
2013-10-23 13:58:38 +00:00
activity = cr . dictfetchone ( )
triggers = False
2013-10-24 07:57:07 +00:00
if self . workitem [ ' state ' ] == ' active ' :
2013-10-23 13:58:38 +00:00
triggers = True
2013-10-24 07:57:07 +00:00
if not self . _execute ( activity , stack ) :
2013-10-23 13:58:38 +00:00
return False
2013-10-24 07:57:07 +00:00
if force_running or self . workitem [ ' state ' ] == ' complete ' :
ok = self . _split_test ( activity [ ' split_mode ' ] , signal , stack )
2013-10-23 13:58:38 +00:00
triggers = triggers and not ok
if triggers :
2014-08-04 14:07:25 +00:00
cr . execute ( ' select * from wkf_transition where act_from= %s ORDER BY sequence,id ' , ( self . workitem [ ' act_id ' ] , ) )
2013-10-23 13:58:38 +00:00
for trans in cr . dictfetchall ( ) :
if trans [ ' trigger_model ' ] :
2013-10-24 07:57:07 +00:00
ids = self . wkf_expr_eval_expr ( trans [ ' trigger_expr_id ' ] )
2013-10-23 13:58:38 +00:00
for res_id in ids :
cr . execute ( ' select nextval( \' wkf_triggers_id_seq \' ) ' )
id = cr . fetchone ( ) [ 0 ]
2013-10-24 07:57:07 +00:00
cr . execute ( ' insert into wkf_triggers (model,res_id,instance_id,workitem_id,id) values ( %s , %s , %s , %s , %s ) ' , ( trans [ ' trigger_model ' ] , res_id , self . workitem [ ' inst_id ' ] , self . workitem [ ' id ' ] , id ) )
2008-07-22 14:24:36 +00:00
return True
2013-10-24 07:57:07 +00:00
def _execute ( self , activity , stack ) :
2013-10-23 13:58:38 +00:00
""" Send a signal to parenrt workflow (signal: subflow.signal_name) """
result = True
cr = self . session . cr
signal_todo = [ ]
2013-10-24 07:57:07 +00:00
if ( self . workitem [ ' state ' ] == ' active ' ) and activity [ ' signal_send ' ] :
2013-10-23 13:58:38 +00:00
# signal_send']:
2013-10-24 07:57:07 +00:00
cr . execute ( " select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id IN (select inst_id from wkf_workitem where subflow_id= %s ) " , ( self . workitem [ ' inst_id ' ] , ) )
2013-10-23 13:58:38 +00:00
for instance_id , model_name , record_id in cr . fetchall ( ) :
record = Record ( model_name , record_id )
signal_todo . append ( ( instance_id , record , activity [ ' signal_send ' ] ) )
if activity [ ' kind ' ] == WorkflowActivity . KIND_DUMMY :
2013-10-24 07:57:07 +00:00
if self . workitem [ ' state ' ] == ' active ' :
self . _state_set ( activity , ' complete ' )
2013-10-23 13:58:38 +00:00
if activity [ ' action_id ' ] :
2013-10-24 07:57:07 +00:00
res2 = self . wkf_expr_execute_action ( activity )
2013-10-23 13:58:38 +00:00
if res2 :
stack . append ( res2 )
result = res2
elif activity [ ' kind ' ] == WorkflowActivity . KIND_FUNCTION :
2013-10-24 07:57:07 +00:00
if self . workitem [ ' state ' ] == ' active ' :
self . _state_set ( activity , ' running ' )
returned_action = self . wkf_expr_execute ( activity )
2013-10-23 13:58:38 +00:00
if type ( returned_action ) in ( dict , ) :
stack . append ( returned_action )
if activity [ ' action_id ' ] :
2013-10-24 07:57:07 +00:00
res2 = self . wkf_expr_execute_action ( activity )
2013-10-23 13:58:38 +00:00
# A client action has been returned
if res2 :
stack . append ( res2 )
result = res2
2013-10-24 07:57:07 +00:00
self . _state_set ( activity , ' complete ' )
2013-10-23 13:58:38 +00:00
elif activity [ ' kind ' ] == WorkflowActivity . KIND_STOPALL :
2013-10-24 07:57:07 +00:00
if self . workitem [ ' state ' ] == ' active ' :
self . _state_set ( activity , ' running ' )
cr . execute ( ' delete from wkf_workitem where inst_id= %s and id<> %s ' , ( self . workitem [ ' inst_id ' ] , self . workitem [ ' id ' ] ) )
2013-10-23 13:58:38 +00:00
if activity [ ' action ' ] :
2013-10-24 07:57:07 +00:00
self . wkf_expr_execute ( activity )
self . _state_set ( activity , ' complete ' )
2013-10-23 13:58:38 +00:00
elif activity [ ' kind ' ] == WorkflowActivity . KIND_SUBFLOW :
2013-10-24 07:57:07 +00:00
if self . workitem [ ' state ' ] == ' active ' :
2013-10-23 13:58:38 +00:00
2013-10-24 07:57:07 +00:00
self . _state_set ( activity , ' running ' )
2013-10-23 13:58:38 +00:00
if activity . get ( ' action ' , False ) :
2013-10-24 07:57:07 +00:00
id_new = self . wkf_expr_execute ( activity )
2013-10-23 13:58:38 +00:00
if not id_new :
2013-10-24 07:57:07 +00:00
cr . execute ( ' delete from wkf_workitem where id= %s ' , ( self . workitem [ ' id ' ] , ) )
2013-10-23 13:58:38 +00:00
return False
assert type ( id_new ) == type ( 1 ) or type ( id_new ) == type ( 1 L ) , ' Wrong return value: ' + str ( id_new ) + ' ' + str ( type ( id_new ) )
2013-10-24 07:57:07 +00:00
cr . execute ( ' select id from wkf_instance where res_id= %s and wkf_id= %s ' , ( id_new , activity [ ' subflow_id ' ] ) )
2013-10-23 13:58:38 +00:00
id_new = cr . fetchone ( ) [ 0 ]
else :
2013-10-24 07:57:07 +00:00
inst = instance . WorkflowInstance ( self . session , self . record )
id_new = inst . create ( activity [ ' subflow_id ' ] )
cr . execute ( ' update wkf_workitem set subflow_id= %s where id= %s ' , ( id_new , self . workitem [ ' id ' ] ) )
self . workitem [ ' subflow_id ' ] = id_new
2013-10-23 13:58:38 +00:00
2013-10-24 07:57:07 +00:00
if self . workitem [ ' state ' ] == ' running ' :
cr . execute ( " select state from wkf_instance where id= %s " , ( self . workitem [ ' subflow_id ' ] , ) )
state = cr . fetchone ( ) [ 0 ]
2013-10-23 13:58:38 +00:00
if state == ' complete ' :
2013-10-24 07:57:07 +00:00
self . _state_set ( activity , ' complete ' )
2013-10-23 13:58:38 +00:00
for instance_id , record , signal_send in signal_todo :
2013-10-24 07:57:07 +00:00
wi = instance . WorkflowInstance ( self . session , record , { ' id ' : instance_id } )
wi . validate ( signal_send , force_running = True )
2013-10-23 13:58:38 +00:00
return result
2013-10-24 07:57:07 +00:00
def _state_set ( self , activity , state ) :
self . session . cr . execute ( ' update wkf_workitem set state= %s where id= %s ' , ( state , self . workitem [ ' id ' ] ) )
self . workitem [ ' state ' ] = state
2013-10-23 13:58:38 +00:00
logger . info ( ' Changed state of work item %s to " %s " in activity %s ' ,
2013-10-24 07:57:07 +00:00
self . workitem [ ' id ' ] , state , activity [ ' id ' ] ,
2013-10-23 13:58:38 +00:00
extra = { ' ident ' : ( self . session . uid , self . record . model , self . record . id ) } )
2013-10-24 07:57:07 +00:00
def _split_test ( self , split_mode , signal , stack ) :
2013-10-23 13:58:38 +00:00
cr = self . session . cr
2014-08-04 14:07:25 +00:00
cr . execute ( ' select * from wkf_transition where act_from= %s ORDER BY sequence,id ' , ( self . workitem [ ' act_id ' ] , ) )
2013-10-23 13:58:38 +00:00
test = False
transitions = [ ]
alltrans = cr . dictfetchall ( )
if split_mode in ( ' XOR ' , ' OR ' ) :
for transition in alltrans :
2013-10-24 07:57:07 +00:00
if self . wkf_expr_check ( transition , signal ) :
2013-10-23 13:58:38 +00:00
test = True
2013-10-24 07:57:07 +00:00
transitions . append ( ( transition [ ' id ' ] , self . workitem [ ' inst_id ' ] ) )
2013-10-23 13:58:38 +00:00
if split_mode == ' XOR ' :
break
else :
test = True
for transition in alltrans :
2013-10-24 07:57:07 +00:00
if not self . wkf_expr_check ( transition , signal ) :
2013-10-23 13:58:38 +00:00
test = False
2008-07-22 14:24:36 +00:00
break
2013-10-24 07:57:07 +00:00
cr . execute ( ' select count(*) from wkf_witm_trans where trans_id= %s and inst_id= %s ' , ( transition [ ' id ' ] , self . workitem [ ' inst_id ' ] ) )
2013-10-23 13:58:38 +00:00
if not cr . fetchone ( ) [ 0 ] :
2013-10-24 07:57:07 +00:00
transitions . append ( ( transition [ ' id ' ] , self . workitem [ ' inst_id ' ] ) )
2013-10-23 13:58:38 +00:00
if test and transitions :
cr . executemany ( ' insert into wkf_witm_trans (trans_id,inst_id) values ( %s , %s ) ' , transitions )
2013-10-24 07:57:07 +00:00
cr . execute ( ' delete from wkf_workitem where id= %s ' , ( self . workitem [ ' id ' ] , ) )
2013-10-23 13:58:38 +00:00
for t in transitions :
self . _join_test ( t [ 0 ] , t [ 1 ] , stack )
return True
return False
def _join_test ( self , trans_id , inst_id , stack ) :
cr = self . session . cr
cr . execute ( ' select * from wkf_activity where id=(select act_to from wkf_transition where id= %s ) ' , ( trans_id , ) )
activity = cr . dictfetchone ( )
if activity [ ' join_mode ' ] == ' XOR ' :
2013-10-24 07:57:07 +00:00
WorkflowItem . create ( self . session , self . record , activity , inst_id , stack = stack )
2013-10-23 13:58:38 +00:00
cr . execute ( ' delete from wkf_witm_trans where inst_id= %s and trans_id= %s ' , ( inst_id , trans_id ) )
else :
2014-08-04 14:07:25 +00:00
cr . execute ( ' select id from wkf_transition where act_to= %s ORDER BY sequence,id ' , ( activity [ ' id ' ] , ) )
2013-10-23 13:58:38 +00:00
trans_ids = cr . fetchall ( )
ok = True
2008-07-22 14:24:36 +00:00
for ( id , ) in trans_ids :
2013-10-23 13:58:38 +00:00
cr . execute ( ' select count(*) from wkf_witm_trans where trans_id= %s and inst_id= %s ' , ( id , inst_id ) )
res = cr . fetchone ( ) [ 0 ]
if not res :
ok = False
break
if ok :
for ( id , ) in trans_ids :
cr . execute ( ' delete from wkf_witm_trans where trans_id= %s and inst_id= %s ' , ( id , inst_id ) )
2013-10-24 07:57:07 +00:00
WorkflowItem . create ( self . session , self . record , activity , inst_id , stack = stack )
def wkf_expr_eval_expr ( self , lines ) :
"""
Evaluate each line of ` ` lines ` ` with the ` ` Environment ` ` environment , returning
the value of the last line .
"""
assert lines , ' You used a NULL action in a workflow, use dummy node instead. '
result = False
for line in lines . split ( ' \n ' ) :
line = line . strip ( )
if not line :
continue
if line == ' True ' :
result = True
elif line == ' False ' :
result = False
else :
env = Environment ( self . session , self . record )
result = eval ( line , env , nocopy = True )
return result
def wkf_expr_execute_action ( self , activity ) :
"""
Evaluate the ir . actions . server action specified in the activity .
"""
context = {
' active_model ' : self . record . model ,
' active_id ' : self . record . id ,
' active_ids ' : [ self . record . id ]
}
ir_actions_server = openerp . registry ( self . session . cr . dbname ) [ ' ir.actions.server ' ]
result = ir_actions_server . run ( self . session . cr , self . session . uid , [ activity [ ' action_id ' ] ] , context )
return result
def wkf_expr_execute ( self , activity ) :
"""
Evaluate the action specified in the activity .
"""
return self . wkf_expr_eval_expr ( activity [ ' action ' ] )
def wkf_expr_check ( self , transition , signal ) :
"""
Test if a transition can be taken . The transition can be taken if :
- the signal name matches ,
- the uid is SUPERUSER_ID or the user groups contains the transition ' s
group ,
- the condition evaluates to a truish value .
"""
if transition [ ' signal ' ] and signal != transition [ ' signal ' ] :
return False
if self . session . uid != openerp . SUPERUSER_ID and transition [ ' group_id ' ] :
registry = openerp . registry ( self . session . cr . dbname )
user_groups = registry [ ' res.users ' ] . read ( self . session . cr , self . session . uid , [ self . session . uid ] , [ ' groups_id ' ] ) [ 0 ] [ ' groups_id ' ]
if transition [ ' group_id ' ] not in user_groups :
return False
return self . wkf_expr_eval_expr ( transition [ ' condition ' ] )
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: