[MERGE] merge with lp:openobject-server
[odoo/odoo.git] / openerp / workflow / workitem.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 #
23 # TODO:
24 # cr.execute('delete from wkf_triggers where model=%s and res_id=%s', (res_type,res_id))
25 #
26
27 import openerp.netsvc as netsvc
28 import instance
29
30 import wkf_expr
31 import wkf_logs
32
33 def create(cr, act_datas, inst_id, ident, stack):
34     for act in act_datas:
35         cr.execute("select nextval('wkf_workitem_id_seq')")
36         id_new = cr.fetchone()[0]
37         cr.execute("insert into wkf_workitem (id,act_id,inst_id,state) values (%s,%s,%s,'active')", (id_new, act['id'], inst_id))
38         cr.execute('select * from wkf_workitem where id=%s',(id_new,))
39         res = cr.dictfetchone()
40         wkf_logs.log(cr,ident,act['id'],'active')
41         process(cr, res, ident, stack=stack)
42
43 def process(cr, workitem, ident, signal=None, force_running=False, stack=None):
44     if stack is None:
45         raise 'Error !!!'
46     result = True
47     cr.execute('select * from wkf_activity where id=%s', (workitem['act_id'],))
48     activity = cr.dictfetchone()
49
50     triggers = False
51     if workitem['state']=='active':
52         triggers = True
53         result = _execute(cr, workitem, activity, ident, stack)
54         if not result:
55             return False
56
57     if workitem['state']=='running':
58         pass
59
60     if workitem['state']=='complete' or force_running:
61         ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack)
62         triggers = triggers and not ok
63
64     if triggers:
65         cr.execute('select * from wkf_transition where act_from=%s', (workitem['act_id'],))
66         alltrans = cr.dictfetchall()
67         for trans in alltrans:
68             if trans['trigger_model']:
69                 ids = wkf_expr._eval_expr(cr,ident,workitem,trans['trigger_expr_id'])
70                 for res_id in ids:
71                     cr.execute('select nextval(\'wkf_triggers_id_seq\')')
72                     id =cr.fetchone()[0]
73                     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,workitem['inst_id'], workitem['id'], id))
74
75     return result
76
77
78 # ---------------------- PRIVATE FUNCS --------------------------------
79
80 def _state_set(cr, workitem, activity, state, ident):
81     cr.execute('update wkf_workitem set state=%s where id=%s', (state,workitem['id']))
82     workitem['state'] = state
83     wkf_logs.log(cr,ident,activity['id'],state)
84
85 def _execute(cr, workitem, activity, ident, stack):
86     result = True
87     #
88     # send a signal to parent workflow (signal: subflow.signal_name)
89     #
90     signal_todo = []
91     if (workitem['state']=='active') and activity['signal_send']:
92         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)", (workitem['inst_id'],))
93         for i in cr.fetchall():
94             signal_todo.append((i[0], (ident[0],i[1],i[2]), activity['signal_send']))
95
96     if activity['kind']=='dummy':
97         if workitem['state']=='active':
98             _state_set(cr, workitem, activity, 'complete', ident)
99             if activity['action_id']:
100                 res2 = wkf_expr.execute_action(cr, ident, workitem, activity)
101                 if res2:
102                     stack.append(res2)
103                     result=res2
104     elif activity['kind']=='function':
105         if workitem['state']=='active':
106             _state_set(cr, workitem, activity, 'running', ident)
107             returned_action = wkf_expr.execute(cr, ident, workitem, activity)
108             if type(returned_action) in (dict,):
109                 stack.append(returned_action)
110             if activity['action_id']:
111                 res2 = wkf_expr.execute_action(cr, ident, workitem, activity)
112                 # A client action has been returned
113                 if res2:
114                     stack.append(res2)
115                     result=res2
116             _state_set(cr, workitem, activity, 'complete', ident)
117     elif activity['kind']=='stopall':
118         if workitem['state']=='active':
119             _state_set(cr, workitem, activity, 'running', ident)
120             cr.execute('delete from wkf_workitem where inst_id=%s and id<>%s', (workitem['inst_id'], workitem['id']))
121             if activity['action']:
122                 wkf_expr.execute(cr, ident, workitem, activity)
123             _state_set(cr, workitem, activity, 'complete', ident)
124     elif activity['kind']=='subflow':
125         if workitem['state']=='active':
126             _state_set(cr, workitem, activity, 'running', ident)
127             if activity.get('action', False):
128                 id_new = wkf_expr.execute(cr, ident, workitem, activity)
129                 if not (id_new):
130                     cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
131                     return False
132                 assert type(id_new)==type(1) or type(id_new)==type(1L), 'Wrong return value: '+str(id_new)+' '+str(type(id_new))
133                 cr.execute('select id from wkf_instance where res_id=%s and wkf_id=%s', (id_new,activity['subflow_id']))
134                 id_new = cr.fetchone()[0]
135             else:
136                 id_new = instance.create(cr, ident, activity['subflow_id'])
137             cr.execute('update wkf_workitem set subflow_id=%s where id=%s', (id_new, workitem['id']))
138             workitem['subflow_id'] = id_new
139         if workitem['state']=='running':
140             cr.execute("select state from wkf_instance where id=%s", (workitem['subflow_id'],))
141             state= cr.fetchone()[0]
142             if state=='complete':
143                 _state_set(cr, workitem, activity, 'complete', ident)
144     for t in signal_todo:
145         instance.validate(cr, t[0], t[1], t[2], force_running=True)
146
147     return result
148
149 def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None):
150     if stack is None:
151         raise 'Error !!!'
152     cr.execute('select * from wkf_transition where act_from=%s', (workitem['act_id'],))
153     test = False
154     transitions = []
155     alltrans = cr.dictfetchall()
156     if split_mode=='XOR' or split_mode=='OR':
157         for transition in alltrans:
158             if wkf_expr.check(cr, workitem, ident, transition,signal):
159                 test = True
160                 transitions.append((transition['id'], workitem['inst_id']))
161                 if split_mode=='XOR':
162                     break
163     else:
164         test = True
165         for transition in alltrans:
166             if not wkf_expr.check(cr, workitem, ident, transition,signal):
167                 test = False
168                 break
169             cr.execute('select count(*) from wkf_witm_trans where trans_id=%s and inst_id=%s', (transition['id'], workitem['inst_id']))
170             if not cr.fetchone()[0]:
171                 transitions.append((transition['id'], workitem['inst_id']))
172     if test and len(transitions):
173         cr.executemany('insert into wkf_witm_trans (trans_id,inst_id) values (%s,%s)', transitions)
174         cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
175         for t in transitions:
176             _join_test(cr, t[0], t[1], ident, stack)
177         return True
178     return False
179
180 def _join_test(cr, trans_id, inst_id, ident, stack):
181     cr.execute('select * from wkf_activity where id=(select act_to from wkf_transition where id=%s)', (trans_id,))
182     activity = cr.dictfetchone()
183     if activity['join_mode']=='XOR':
184         create(cr,[activity], inst_id, ident, stack)
185         cr.execute('delete from wkf_witm_trans where inst_id=%s and trans_id=%s', (inst_id,trans_id))
186     else:
187         cr.execute('select id from wkf_transition where act_to=%s', (activity['id'],))
188         trans_ids = cr.fetchall()
189         ok = True
190         for (id,) in trans_ids:
191             cr.execute('select count(*) from wkf_witm_trans where trans_id=%s and inst_id=%s', (id,inst_id))
192             res = cr.fetchone()[0]
193             if not res:
194                 ok = False
195                 break
196         if ok:
197             for (id,) in trans_ids:
198                 cr.execute('delete from wkf_witm_trans where trans_id=%s and inst_id=%s', (id,inst_id))
199             create(cr, [activity], inst_id, ident, stack)
200
201 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
202