← Back to team overview

openerp-community team mailing list archive

[Merge] lp:~openerp-community/openobject-server/context-in-workflows into lp:openobject-server

 

Raphaël Valyi - http://www.akretion.com has proposed merging lp:~openerp-community/openobject-server/context-in-workflows into lp:openobject-server.

Requested reviews:
  Raphaël Valyi - http://www.akretion.com (rvalyi)
  OpenERP Core Team (openerp)

For more details, see:
https://code.launchpad.net/~openerp-community/openobject-server/context-in-workflows/+merge/85518

Work in progress, seem my merge message.
A way to test the current limitation where a bug is caused because the workflow engine would pass some unexpected context param is just to create an order with manual invoicing. Validate it and then create the invoice from the sale order.

A bug like that will result:
[..]
  File "/home/rvalyi/DEV/openerp/openerp6.1/addons/sale/sale.py", line 457, in manual_invoice
[..]
  File "/home/rvalyi/DEV/openerp/openerp6.1/server/openerp/workflow/wkf_expr.py", line 85, in check
    return _eval_expr(cr, ident, workitem, transition['condition'], context)
  File "/home/rvalyi/DEV/openerp/openerp6.1/server/openerp/workflow/wkf_expr.py", line 59, in _eval_expr
    ret = eval(line, env, nocopy=True)
  File "/home/rvalyi/DEV/openerp/openerp6.1/server/openerp/tools/safe_eval.py", line 294, in safe_eval
    return eval(test_expr(expr,_SAFE_OPCODES, mode=mode), globals_dict, locals_dict)
  File "", line 1, in <module>
  File "/home/rvalyi/DEV/openerp/openerp6.1/server/openerp/osv/orm.py", line 396, in function_proxy
    return attr(self._cr, self._uid, [self._id], *args, **kwargs)
TypeError: test_state() got an unexpected keyword argument 'context'


Probably, to make it compatible we should inspect the method signature a bit like Albert did in it former "browse_executer" here:
https://code.launchpad.net/~albert-nan/openobject-server/workflow-context/+merge/12256
(in orm.py)
-- 
https://code.launchpad.net/~openerp-community/openobject-server/context-in-workflows/+merge/85518
Your team OpenERP Community is subscribed to branch lp:~openerp-community/openobject-server/context-in-workflows.
=== modified file 'openerp/osv/osv.py'
--- openerp/osv/osv.py	2011-10-01 01:22:10 +0000
+++ openerp/osv/osv.py	2011-12-13 17:01:27 +0000
@@ -180,16 +180,16 @@
             cr.close()
         return res
 
-    def exec_workflow_cr(self, cr, uid, obj, method, *args):
+    def exec_workflow_cr(self, cr, uid, obj, method, id, context=None):
         wf_service = netsvc.LocalService("workflow")
-        return wf_service.trg_validate(uid, obj, args[0], method, cr)
+        return wf_service.trg_validate(uid, obj, id, method, cr, context)
 
     @check
-    def exec_workflow(self, db, uid, obj, method, *args):
+    def exec_workflow(self, db, uid, obj, method, id, context=None):
         cr = pooler.get_db(db).cursor()
         try:
             try:
-                res = self.exec_workflow_cr(cr, uid, obj, method, *args)
+                res = self.exec_workflow_cr(cr, uid, obj, method, id, context)
                 cr.commit()
             except Exception:
                 cr.rollback()

=== modified file 'openerp/workflow/instance.py'
--- openerp/workflow/instance.py	2011-02-07 12:57:23 +0000
+++ openerp/workflow/instance.py	2011-12-13 17:01:27 +0000
@@ -25,14 +25,14 @@
 import openerp.netsvc as netsvc
 import openerp.pooler as pooler
 
-def create(cr, ident, wkf_id):
+def create(cr, ident, wkf_id, context=None):
     (uid,res_type,res_id) = ident
     cr.execute('insert into wkf_instance (res_type,res_id,uid,wkf_id) values (%s,%s,%s,%s) RETURNING id', (res_type,res_id,uid,wkf_id))
     id_new = cr.fetchone()[0]
     cr.execute('select * from wkf_activity where flow_start=True and wkf_id=%s', (wkf_id,))
     res = cr.dictfetchall()
     stack = []
-    workitem.create(cr, res, id_new, ident, stack=stack)
+    workitem.create(cr, res, id_new, ident, stack=stack, context=context)
     update(cr, id_new, ident)
     return id_new
 
@@ -40,24 +40,24 @@
     (uid,res_type,res_id) = ident
     cr.execute('delete from wkf_instance where res_id=%s and res_type=%s', (res_id,res_type))
 
-def validate(cr, inst_id, ident, signal, force_running=False):
+def validate(cr, inst_id, ident, signal, force_running=False, context=None):
     cr.execute("select * from wkf_workitem where inst_id=%s", (inst_id,))
     stack = []
     for witem in cr.dictfetchall():
         stack = []
-        workitem.process(cr, witem, ident, signal, force_running, stack=stack)
+        workitem.process(cr, witem, ident, signal, force_running, stack=stack, context=context)
         # An action is returned
-    _update_end(cr, inst_id, ident)
+    _update_end(cr, inst_id, ident, context)
     return stack and stack[0] or False
 
-def update(cr, inst_id, ident):
+def update(cr, inst_id, ident, context=None):
     cr.execute("select * from wkf_workitem where inst_id=%s", (inst_id,))
     for witem in cr.dictfetchall():
         stack = []
-        workitem.process(cr, witem, ident, stack=stack)
-    return _update_end(cr, inst_id, ident)
+        workitem.process(cr, witem, ident, stack=stack, context=context)
+    return _update_end(cr, inst_id, ident, context)
 
-def _update_end(cr, inst_id, ident):
+def _update_end(cr, inst_id, ident, context=None):
     cr.execute('select wkf_id from wkf_instance where id=%s', (inst_id,))
     wkf_id = cr.fetchone()[0]
     cr.execute('select state,flow_stop from wkf_workitem w left join wkf_activity a on (a.id=w.act_id) where w.inst_id=%s', (inst_id,))
@@ -74,7 +74,7 @@
         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)", (inst_id,))
         for i in cr.fetchall():
             for act_name in act_names:
-                validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0])
+                validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0], context)
     return ok
 
 

=== modified file 'openerp/workflow/wkf_expr.py'
--- openerp/workflow/wkf_expr.py	2011-09-14 10:25:05 +0000
+++ openerp/workflow/wkf_expr.py	2011-12-13 17:01:27 +0000
@@ -26,22 +26,23 @@
 from openerp.tools.safe_eval import safe_eval as eval
 
 class Env(dict):
-    def __init__(self, cr, uid, model, ids):
+    def __init__(self, cr, uid, model, ids, context=None):
         self.cr = cr
         self.uid = uid
         self.model = model
         self.ids = ids
+        self.context = context
         self.obj = pooler.get_pool(cr.dbname).get(model)
         self.columns = self.obj._columns.keys() + self.obj._inherit_fields.keys()
 
     def __getitem__(self, key):
         if (key in self.columns) or (key in dir(self.obj)):
-            res = self.obj.browse(self.cr, self.uid, self.ids[0])
+            res = self.obj.browse(self.cr, self.uid, self.ids[0], self.context)
             return res[key]
         else:
             return super(Env, self).__getitem__(key)
 
-def _eval_expr(cr, ident, workitem, action):
+def _eval_expr(cr, ident, workitem, action, context=None):
     ret=False
     assert action, 'You used a NULL action in a workflow, use dummy node instead.'
     for line in action.split('\n'):
@@ -54,20 +55,23 @@
         elif line =='False':
             ret=False
         else:
-            env = Env(cr, uid, model, ids)
+            env = Env(cr, uid, model, ids, context)
             ret = eval(line, env, nocopy=True)
     return ret
 
-def execute_action(cr, ident, workitem, activity):
+def execute_action(cr, ident, workitem, activity, context=None):
     obj = pooler.get_pool(cr.dbname).get('ir.actions.server')
+    ctx = {}
+    if context is not None:
+        ctx.update( context )
     ctx = {'active_model':ident[1], 'active_id':ident[2], 'active_ids':[ident[2]]}
     result = obj.run(cr, ident[0], [activity['action_id']], ctx)
     return result
 
-def execute(cr, ident, workitem, activity):
-    return _eval_expr(cr, ident, workitem, activity['action'])
+def execute(cr, ident, workitem, activity, context=None):
+    return _eval_expr(cr, ident, workitem, activity['action'], context)
 
-def check(cr, workitem, ident, transition, signal):
+def check(cr, workitem, ident, transition, signal, context=None):
     if transition['signal'] and signal != transition['signal']:
         return False
 
@@ -78,8 +82,7 @@
         if not transition['group_id'] in user_groups:
             return False
 
-    return _eval_expr(cr, ident, workitem, transition['condition'])
-
+    return _eval_expr(cr, ident, workitem, transition['condition'], context)
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 

=== modified file 'openerp/workflow/wkf_service.py'
--- openerp/workflow/wkf_service.py	2011-09-24 14:52:58 +0000
+++ openerp/workflow/wkf_service.py	2011-12-13 17:01:27 +0000
@@ -44,7 +44,10 @@
     def clear_cache(self, cr, uid):
         self.wkf_on_create_cache[cr.dbname]={}
 
-    def trg_write(self, uid, res_type, res_id, cr):
+    def trg_write(self, uid, res_type, res_id, cr, context=None):
+        print "WWWWWWWWWWWWW", context
+        import traceback
+        traceback.print_stack()
         """
         Reevaluates the specified workflow instance. Thus if any condition for
         a transition have been changed in the backend, then running ``trg_write``
@@ -57,9 +60,10 @@
         ident = (uid,res_type,res_id)
         cr.execute('select id from wkf_instance where res_id=%s and res_type=%s and state=%s', (res_id or None,res_type or None, 'active'))
         for (id,) in cr.fetchall():
-            instance.update(cr, id, ident)
+            instance.update(cr, id, ident, context)
 
-    def trg_trigger(self, uid, res_type, res_id, cr):
+    def trg_trigger(self, uid, res_type, res_id, cr, context=None):
+        print "TTTTTTTTTTTTTTT", context
         """
         Activate a trigger.
 
@@ -75,7 +79,7 @@
         for (instance_id,) in res:
             cr.execute('select %s,res_type,res_id from wkf_instance where id=%s', (uid, instance_id,))
             ident = cr.fetchone()
-            instance.update(cr, instance_id, ident)
+            instance.update(cr, instance_id, ident, context)
 
     def trg_delete(self, uid, res_type, res_id, cr):
         """
@@ -88,7 +92,7 @@
         ident = (uid,res_type,res_id)
         instance.delete(cr, ident)
 
-    def trg_create(self, uid, res_type, res_id, cr):
+    def trg_create(self, uid, res_type, res_id, cr, context=None):
         """
         Create a new workflow instance
 
@@ -105,9 +109,10 @@
             wkf_ids = cr.fetchall()
             self.wkf_on_create_cache[cr.dbname][res_type] = wkf_ids
         for (wkf_id,) in wkf_ids:
-            instance.create(cr, ident, wkf_id)
+            instance.create(cr, ident, wkf_id, context)
 
-    def trg_validate(self, uid, res_type, res_id, signal, cr):
+    def trg_validate(self, uid, res_type, res_id, signal, cr, context=None):
+        print "VVVVVVVVVVV", context
         """
         Fire a signal on a given workflow instance
 
@@ -121,7 +126,7 @@
         # ids of all active workflow instances for a corresponding resource (id, model_nam)
         cr.execute('select id from wkf_instance where res_id=%s and res_type=%s and state=%s', (res_id, res_type, 'active'))
         for (id,) in cr.fetchall():
-            res2 = instance.validate(cr, id, ident, signal)
+            res2 = instance.validate(cr, id, ident, signal, context=context)
             result = result or res2
         return result
 

=== modified file 'openerp/workflow/workitem.py'
--- openerp/workflow/workitem.py	2011-12-11 10:21:40 +0000
+++ openerp/workflow/workitem.py	2011-12-13 17:01:27 +0000
@@ -30,7 +30,7 @@
 import wkf_expr
 import wkf_logs
 
-def create(cr, act_datas, inst_id, ident, stack):
+def create(cr, act_datas, inst_id, ident, stack, context=None):
     for act in act_datas:
         cr.execute("select nextval('wkf_workitem_id_seq')")
         id_new = cr.fetchone()[0]
@@ -38,9 +38,9 @@
         cr.execute('select * from wkf_workitem where id=%s',(id_new,))
         res = cr.dictfetchone()
         wkf_logs.log(cr,ident,act['id'],'active')
-        process(cr, res, ident, stack=stack)
+        process(cr, res, ident, stack=stack, context=context)
 
-def process(cr, workitem, ident, signal=None, force_running=False, stack=None):
+def process(cr, workitem, ident, signal=None, force_running=False, stack=None, context=None):
     if stack is None:
         raise 'Error !!!'
     result = True
@@ -50,7 +50,7 @@
     triggers = False
     if workitem['state']=='active':
         triggers = True
-        result = _execute(cr, workitem, activity, ident, stack)
+        result = _execute(cr, workitem, activity, ident, stack, context)
         if not result:
             return False
 
@@ -58,7 +58,7 @@
         pass
 
     if workitem['state']=='complete' or force_running:
-        ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack)
+        ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack, context)
         triggers = triggers and not ok
 
     if triggers:
@@ -66,7 +66,7 @@
         alltrans = cr.dictfetchall()
         for trans in alltrans:
             if trans['trigger_model']:
-                ids = wkf_expr._eval_expr(cr,ident,workitem,trans['trigger_expr_id'])
+                ids = wkf_expr._eval_expr(cr,ident,workitem,trans['trigger_expr_id'], context)
                 for res_id in ids:
                     cr.execute('select nextval(\'wkf_triggers_id_seq\')')
                     id =cr.fetchone()[0]
@@ -82,7 +82,7 @@
     workitem['state'] = state
     wkf_logs.log(cr,ident,activity['id'],state)
 
-def _execute(cr, workitem, activity, ident, stack):
+def _execute(cr, workitem, activity, ident, stack, context=None):
     result = True
     #
     # send a signal to parent workflow (signal: subflow.signal_name)
@@ -97,18 +97,18 @@
         if workitem['state']=='active':
             _state_set(cr, workitem, activity, 'complete', ident)
             if activity['action_id']:
-                res2 = wkf_expr.execute_action(cr, ident, workitem, activity)
+                res2 = wkf_expr.execute_action(cr, ident, workitem, activity, context)
                 if res2:
                     stack.append(res2)
                     result=res2
     elif activity['kind']=='function':
         if workitem['state']=='active':
             _state_set(cr, workitem, activity, 'running', ident)
-            returned_action = wkf_expr.execute(cr, ident, workitem, activity)
+            returned_action = wkf_expr.execute(cr, ident, workitem, activity, context)
             if type(returned_action) in (dict,):
                 stack.append(returned_action)
             if activity['action_id']:
-                res2 = wkf_expr.execute_action(cr, ident, workitem, activity)
+                res2 = wkf_expr.execute_action(cr, ident, workitem, activity, context)
                 # A client action has been returned
                 if res2:
                     stack.append(res2)
@@ -119,13 +119,13 @@
             _state_set(cr, workitem, activity, 'running', ident)
             cr.execute('delete from wkf_workitem where inst_id=%s and id<>%s', (workitem['inst_id'], workitem['id']))
             if activity['action']:
-                wkf_expr.execute(cr, ident, workitem, activity)
+                wkf_expr.execute(cr, ident, workitem, activity, context)
             _state_set(cr, workitem, activity, 'complete', ident)
     elif activity['kind']=='subflow':
         if workitem['state']=='active':
             _state_set(cr, workitem, activity, 'running', ident)
             if activity.get('action', False):
-                id_new = wkf_expr.execute(cr, ident, workitem, activity)
+                id_new = wkf_expr.execute(cr, ident, workitem, activity, context)
                 if not (id_new):
                     cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
                     return False
@@ -133,7 +133,7 @@
                 cr.execute('select id from wkf_instance where res_id=%s and wkf_id=%s', (id_new,activity['subflow_id']))
                 id_new = cr.fetchone()[0]
             else:
-                id_new = instance.create(cr, ident, activity['subflow_id'])
+                id_new = instance.create(cr, ident, activity['subflow_id'], context)
             cr.execute('update wkf_workitem set subflow_id=%s where id=%s', (id_new, workitem['id']))
             workitem['subflow_id'] = id_new
         if workitem['state']=='running':
@@ -142,11 +142,11 @@
             if state=='complete':
                 _state_set(cr, workitem, activity, 'complete', ident)
     for t in signal_todo:
-        instance.validate(cr, t[0], t[1], t[2], force_running=True)
+        instance.validate(cr, t[0], t[1], t[2], force_running=True, context=context)
 
     return result
 
-def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None):
+def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None, context=None):
     if stack is None:
         raise 'Error !!!'
     cr.execute('select * from wkf_transition where act_from=%s', (workitem['act_id'],))
@@ -155,7 +155,7 @@
     alltrans = cr.dictfetchall()
     if split_mode=='XOR' or split_mode=='OR':
         for transition in alltrans:
-            if wkf_expr.check(cr, workitem, ident, transition,signal):
+            if wkf_expr.check(cr, workitem, ident, transition, signal, context):
                 test = True
                 transitions.append((transition['id'], workitem['inst_id']))
                 if split_mode=='XOR':
@@ -163,7 +163,7 @@
     else:
         test = True
         for transition in alltrans:
-            if not wkf_expr.check(cr, workitem, ident, transition,signal):
+            if not wkf_expr.check(cr, workitem, ident, transition, signal, context):
                 test = False
                 break
             cr.execute('select count(*) from wkf_witm_trans where trans_id=%s and inst_id=%s', (transition['id'], workitem['inst_id']))
@@ -173,15 +173,15 @@
         cr.executemany('insert into wkf_witm_trans (trans_id,inst_id) values (%s,%s)', transitions)
         cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
         for t in transitions:
-            _join_test(cr, t[0], t[1], ident, stack)
+            _join_test(cr, t[0], t[1], ident, stack, context)
         return True
     return False
 
-def _join_test(cr, trans_id, inst_id, ident, stack):
+def _join_test(cr, trans_id, inst_id, ident, stack, context=None):
     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':
-        create(cr,[activity], inst_id, ident, stack)
+        create(cr,[activity], inst_id, ident, stack, context)
         cr.execute('delete from wkf_witm_trans where inst_id=%s and trans_id=%s', (inst_id,trans_id))
     else:
         cr.execute('select id from wkf_transition where act_to=%s', (activity['id'],))
@@ -196,7 +196,7 @@
         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))
-            create(cr, [activity], inst_id, ident, stack)
+            create(cr, [activity], inst_id, ident, stack, context)
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 


Follow ups