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

Proposed by Raphaël Valyi - http://www.akretion.com
Status: Work in progress
Proposed branch: lp:~openerp-community/openobject-server/context-in-workflows
Merge into: lp:openobject-server
Diff against target: 392 lines (+61/-53)
5 files modified
openerp/osv/osv.py (+4/-4)
openerp/workflow/instance.py (+10/-10)
openerp/workflow/wkf_expr.py (+13/-10)
openerp/workflow/wkf_service.py (+13/-8)
openerp/workflow/workitem.py (+21/-21)
To merge this branch: bzr merge lp:~openerp-community/openobject-server/context-in-workflows
Reviewer Review Type Date Requested Status
Stefan Rijnhart (Opener) (community) Needs Fixing
Christophe CHAUVET (community) Needs Information
Raphaël Valyi - http://www.akretion.com (community) Needs Fixing
OpenERP Core Team Pending
Review via email: mp+85518@code.launchpad.net

Description of the change

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)

To post a comment you must log in.
Revision history for this message
Raphaël Valyi - http://www.akretion.com (rvalyi) :
review: Needs Fixing
Revision history for this message
Christophe CHAUVET (christophe-chauvet) wrote :

+1 to have context on workflow

It does also transmit context to the workflow, if it define on button element as

<button name="wkf_test" context="{'test': 'ok'}"/>

Christophe.

review: Needs Information
Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

FWIW, you can have a look at the branch where I tested merging the same thing[1].

As I explain on my merge prop there, I think we need more work on the addons side in order to get any benefit from this work, and that will be much more tricky. I started a wip of changing addons in this manner[2] but it is far from complete or working yet, and perhaps it's too much at this point.

[1] lp:~openerp-dev/openobject-server/trunk-workflow-context-odo
[2] lp:~openerp-dev/openobject-addons/trunk-workflow-context-odo

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Hi Raphaël,

thanks for the effort! A tiny detail in openerp/workflow/wkf_expr.py, line 133: looks like you lose the context here.

I see the problem with the API change and existing methods that Olivier also points out. However, it is one thing to make all models pass the context when calling the workflow service API, but for 6.1 it might minimally suffice to just make the methods mentioned in wkf_activity.action accept the context keyword argument. That should be doable.

Cheers,
Stefan.

review: Needs Fixing
Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

Olivier,

Are you sure it's a good idea to allow context in workflows ?

Workflows are not used for user interface interactions, but for
controlling the
evolution of the object. It has no sense to put context on constraints
and, for
the same reason, I don't think we should allow context in workflows.

An activity should not depend on a context but on data stored on the object.
The goal of the workflow is to easily modify it, add/remove some
transitions, etc.
If we depend on contexts, we may loose this modularity.

I am still not sure about this, but I would not allow the context to be
used in
workflows, for the same reason that it has no sense to put context on
constraints.

On 13/12/11 19:05, Olivier Dony (OpenERP) wrote:
> FWIW, you can have a look at the branch where I tested merging the same thing[1].
>
> As I explain on my merge prop there, I think we need more work on the addons side in order to get any benefit from this work, and that will be much more tricky. I started a wip of changing addons in this manner[2] but it is far from complete or working yet, and perhaps it's too much at this point.
>
>
> [1] lp:~openerp-dev/openobject-server/trunk-workflow-context-odo
> [2] lp:~openerp-dev/openobject-addons/trunk-workflow-context-odo
>

Revision history for this message
Raphaël Valyi - http://www.akretion.com (rvalyi) wrote :
Download full text (4.5 KiB)

Hello Fabien,

1) If I'm not wrong about it, on issue is that if you don't pass the
context you'll for instance miss important things like translations. So for
instance if I create an invoice upon the order (through the workflow) but
if if that invoice fails to create for some reason, the error message it
will display will not be localized properly (you'll eventually find out the
sale_order.user_id lang or something like that but won't be very precise.
Or am I wrong? Also I believe you were using some Python frame inspection
for that but is was extremely slow, am I correct, was it related?

2) we also have an issue with the methods the workflow call: those methods
are easily called from different paths and easily they need the standard
context system to be modular.
Look at such methods:
http://bazaar.launchpad.net/~akretion-team/openobject-addons/sale-modular-picking-better-context/revision/5761
or
http://bazaar.launchpad.net/~akretion-team/openobject-addons/sale-modular-picking-better-context/revision/5760

Actually for a modular API, we really have the case were Sebastien Beau
would need to override the beginning of the method chain to inject some
custom context key.
Later in the call chain, the will override again and hope he will get his
context back so he can take some custom action. Usually it's done in the
same transaction and it seems overkill to write some data in the database
to just re-read it a few method calls later while it could come from memory.

The issue is that is seems pointless then to have all those clean methods
correctly propagating the context and using it normally being usually
called by some piggy workflow method that wouldn't pass them the context
and would instead use a useless meaningless arg* signature.

Wouldn't it be better to just propagate the context all the way through?

IMHO it was right to say constraints should not depend on context. But for
the workflow I'm really not sure. I see many modularity benefit of having
that. A system like Christophe gives with things like <button
name="wkf_test" context="{'test': 'ok'}"/> could be extremely powerful with
context being propagated.

So in any case I'm not 100% what is best here. But probably if we decide
workflows don't propagate the context, we should still refactor the methods
it call to support the standard context arguments so it can be used in a
more modular way, no?

So at the end what would we decide:
1) workflow propagate the context (it doesn't mean it should use it itself;
this can be checked) ?
2) the methods called by workflow don't propagate the context at all either
(sounds it sucks to me, cause would force overkill database accesses when
we are already not that fast in transactions)
3) the methods called by the workflow engine, pass the context if they have
some passed. However the workflow engine don't give them the context, so
it's useful only in overrides or when those methods are called outside from
the workflow engine.

What do you guys think?

On Tue, Dec 13, 2011 at 5:58 PM, Fabien (Open ERP) <email address hidden> wrote:

> Olivier,
>
> Are you sure it's a good idea to allow context in workflows ?
>
> Workflows are not used for user interfac...

Read more...

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

On 12/13/2011 08:58 PM, Fabien (Open ERP) wrote:
> Olivier,
>
> Are you sure it's a good idea to allow context in workflows ?
>
> Workflows are not used for user interface interactions, but for
> controlling the
> evolution of the object. It has no sense to put context on constraints
> and, for
> the same reason, I don't think we should allow context in workflows.

Actually, workflows execute business operations requested by users, and
are mostly called in the context of user transactions. When a workflow
button sends a signal, the workflow engine actually produces the result
to send back to the user, by invoking business code.
Granted, the workflow activities themselves don't need the context, but
the business code they are calling does.

> An activity should not depend on a context but on data stored on the object.
> The goal of the workflow is to easily modify it, add/remove some
> transitions, etc.
> If we depend on contexts, we may loose this modularity.

Indeed the goal is not to make the workflow itself depend on the
context, but to have it propagate it to the business code that it calls.
The workflow is a controller that invokes business code in the context
of a user transaction, and business code is in general context-aware, so
why would the workflow engine break this contract?

> I am still not sure about this, but I would not allow the context to
> be used in workflows, for the same reason that it has no sense to
> put context on constraints.

We don't want constraints to be context-aware because it would be a bad
incentive for developers. And they really don't need it, because
constraints should never alter the data in any way (not call mutating
business code) and have their error messages properly localized by the
ORM. I think this is different for the workflow engine, which acts as a
controller.

BTW, we'll have to address this in a more global way for next release if
we change the API to have a thread-local (or similar) handling of the
transaction context. If we can allow that transaction context to be
propagated to business code in all circumstances, and that without
making it available to the actual workflow engine, I'm happy with it.
However if we have to choose, it looks to me that having business code
behave properly in all circumstances (even when called via workflows) is
a more important goal than preventing the design of context-dependent
workflows.

Revision history for this message
Raphaël Valyi - http://www.akretion.com (rvalyi) wrote :
Download full text (5.1 KiB)

Ok,

So Olivier and Fabien, I'm happy that it seems we totally share the same
opinion: none of us wants workflow behavior to be context driven, this
would be bad. BUT, there is not valid reason usual business methods to be
half broken because today when they are invoked through the workflow they
receive no context.

What I propose is:

1) as a first step: change the signature of those common business methods
such as:
http://bazaar.launchpad.net/~akretion-team/openobject-addons/sale-modular-picking-better-context/revision/5761
or
http://bazaar.launchpad.net/~akretion-team/openobject-addons/sale-modular-picking-better-context/revision/5760
So they accept the standard context argument and properly propagate it.

Honestly there aren't too many such actions, if we quickly review stock,
sale, purchase and account, with probably a change in around 20 methods I'm
pretty sure we would cover 90% of the use cases.

I insist today those methods usually have a nonsense useless *args argument
that limit a lot their capabilities when overridden or when calling other
normal business methods. So I mean this is not like if we were breaking
something already self consistent: it's not consistent today.
Today that *args is not used, so it's quite safe I think to replace it by a
"context" arg, basic testing would ensure we are not breaking anything.

2) Eventually, still up to debate, the workflow engine propagate the
context (I'm not 100% sure but I would go for that while still having the
strong policy that workflow themselves should not be context dependent).
If the workflow engine starts passing the context, we should avoid passing
the context where it's not expected. May be one way is to simply pass it as
the last argument (would not break where *args is expected) rather than a
keyword argument, OR, eventually we do some signature checking before
calling as Albert did (sounds less good to me but not sure).

3) Eventually workflow calls and clients can be modified to properly pass
the context when triggering workflow actions. But once most of the common
business signatures are in place, to me this is no API change so this could
be gradually added during the 6.1 release life cycle.

As for keeping the context and the cursor and uid in thread local, of
course, I think this will need to be done in the future. But this may be
effective may be only in 2 years, so we also need a decent solution for the
next 2 years.

On Wed, Dec 14, 2011 at 8:29 AM, Olivier Dony (OpenERP) <email address hidden>wrote:

> On 12/13/2011 08:58 PM, Fabien (Open ERP) wrote:
> > Olivier,
> >
> > Are you sure it's a good idea to allow context in workflows ?
> >
> > Workflows are not used for user interface interactions, but for
> > controlling the
> > evolution of the object. It has no sense to put context on constraints
> > and, for
> > the same reason, I don't think we should allow context in workflows.
>
> Actually, workflows execute business operations requested by users, and
> are mostly called in the context of user transactions. When a workflow
> button sends a signal, the workflow engine actually produces the result
> to send back to the user, by invoking business code.
> Granted, t...

Read more...

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

> 2) Eventually, still up to debate, the workflow engine propagate the
> context (I'm not 100% sure but I would go for that while still having the
> strong policy that workflow themselves should not be context dependent).
> If the workflow engine starts passing the context, we should avoid passing
> the context where it's not expected. May be one way is to simply pass it as
> the last argument (would not break where *args is expected) rather than a
> keyword argument, OR, eventually we do some signature checking before
> calling as Albert did (sounds less good to me but not sure).

The simplest is just to add the keyword argument. If you install all addons from openobject-addons/trunk (well, n10l_de breaks for me) you can get an idea of the scale of the impact. The query "SELECT wkf.osv, wkf_activity.action FROM wkf, wkf_activity WHERE wkf_id = wkf.id AND kind = 'function' AND action LIKE '%()%'" gives you some 80 methods to convert.

The person to prepare such a merge could temporarily hack in Nan's introspection check to iterate over these methods at loading time to see if all have been covered.

The question is whether this 'last minute'(?) API change is fair towards 3rd party addon developers but AFAIK stranger things have been pulled off in this community ;-) In that respect it would be informative when a proper RC1 is planned.

Cheers,
Stefan.

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

On 12/14/2011 02:28 PM, Stefan Rijnhart (Therp) wrote:
> The simplest is just to add the keyword argument. If you install all
> addons from openobject-addons/trunk (well, n10l_de breaks for me) you
> can get an idea of the scale of the impact. The query "SELECT
> wkf.osv, wkf_activity.action FROM wkf, wkf_activity WHERE wkf_id =
> wkf.id AND kind = 'function' AND action LIKE '%()%'" gives you some
> 80 methods to convert.

You also need to be careful with a few other fields that are evaluated
during the course of workflow execution, such as 'trigger_expr_id' or
'condition' on workflow transitions.

> The person to prepare such a merge could temporarily hack in Nan's
> introspection check to iterate over these methods at loading time to
> see if all have been covered.

Yup. Similarly, changing browse_record to default to an empty dict
context when no context is passed would make it pass that context
automatically to all workflow-called methods. If this does not trigger
too many false positives in other places, running the full test suite
(as runbot does) with such a server would validate that all relevant
methods have been indeed updated.

> The question is whether this 'last minute'(?) API change is fair
> towards 3rd party addon developers but AFAIK stranger things have
> been pulled off in this community ;-) In that respect it would be
> informative when a proper RC1 is planned.

This seems acceptable before releasing RC1, but we probably won't have
the resources to do it internally. If anyone from the community is ready
to do this (just changing the API of business methods called by
workflows to allow an optional context), we would however be happy to
review and merge it.
RC1 is planned very soon (if possible before the end of the year),
provided we are able to finish stabilizing the new web client and bring
down bugs and merge props to an acceptable level (something like 0
remaining Medium+ bugs, less than 40 pending R&D merge proposals, and 0
pending merge proposals from the community)
This post[1] from Fabien on the forum provides some more details on what
we're still working on.

[1] http://www.openerp.com/forum/post96294.html#p96294

PS: sorry if you receive this msg twice, I'm re-posting to the mp for
the record.

Unmerged revisions

3875. By Raphaël Valyi - http://www.akretion.com

[REF] merged (with changes) v5.0 NaNTic lp:~albert-nan/openobject-server/workflow-context branch to make workflow engine propagate the context arg
warning: will not work properly yet because:
1) clients like GTK don't pass the context when calling a workflow action (button)
2) when called from Python code, it seems this code will propagate the context, but it may provocate extra context param
call errors when calling object methods. Probably we should replicate the signature check NaN put in its former "browse_executer" class.
Warning, includes a few debug print statements at this stage

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openerp/osv/osv.py'
2--- openerp/osv/osv.py 2011-10-01 01:22:10 +0000
3+++ openerp/osv/osv.py 2011-12-13 17:01:27 +0000
4@@ -180,16 +180,16 @@
5 cr.close()
6 return res
7
8- def exec_workflow_cr(self, cr, uid, obj, method, *args):
9+ def exec_workflow_cr(self, cr, uid, obj, method, id, context=None):
10 wf_service = netsvc.LocalService("workflow")
11- return wf_service.trg_validate(uid, obj, args[0], method, cr)
12+ return wf_service.trg_validate(uid, obj, id, method, cr, context)
13
14 @check
15- def exec_workflow(self, db, uid, obj, method, *args):
16+ def exec_workflow(self, db, uid, obj, method, id, context=None):
17 cr = pooler.get_db(db).cursor()
18 try:
19 try:
20- res = self.exec_workflow_cr(cr, uid, obj, method, *args)
21+ res = self.exec_workflow_cr(cr, uid, obj, method, id, context)
22 cr.commit()
23 except Exception:
24 cr.rollback()
25
26=== modified file 'openerp/workflow/instance.py'
27--- openerp/workflow/instance.py 2011-02-07 12:57:23 +0000
28+++ openerp/workflow/instance.py 2011-12-13 17:01:27 +0000
29@@ -25,14 +25,14 @@
30 import openerp.netsvc as netsvc
31 import openerp.pooler as pooler
32
33-def create(cr, ident, wkf_id):
34+def create(cr, ident, wkf_id, context=None):
35 (uid,res_type,res_id) = ident
36 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))
37 id_new = cr.fetchone()[0]
38 cr.execute('select * from wkf_activity where flow_start=True and wkf_id=%s', (wkf_id,))
39 res = cr.dictfetchall()
40 stack = []
41- workitem.create(cr, res, id_new, ident, stack=stack)
42+ workitem.create(cr, res, id_new, ident, stack=stack, context=context)
43 update(cr, id_new, ident)
44 return id_new
45
46@@ -40,24 +40,24 @@
47 (uid,res_type,res_id) = ident
48 cr.execute('delete from wkf_instance where res_id=%s and res_type=%s', (res_id,res_type))
49
50-def validate(cr, inst_id, ident, signal, force_running=False):
51+def validate(cr, inst_id, ident, signal, force_running=False, context=None):
52 cr.execute("select * from wkf_workitem where inst_id=%s", (inst_id,))
53 stack = []
54 for witem in cr.dictfetchall():
55 stack = []
56- workitem.process(cr, witem, ident, signal, force_running, stack=stack)
57+ workitem.process(cr, witem, ident, signal, force_running, stack=stack, context=context)
58 # An action is returned
59- _update_end(cr, inst_id, ident)
60+ _update_end(cr, inst_id, ident, context)
61 return stack and stack[0] or False
62
63-def update(cr, inst_id, ident):
64+def update(cr, inst_id, ident, context=None):
65 cr.execute("select * from wkf_workitem where inst_id=%s", (inst_id,))
66 for witem in cr.dictfetchall():
67 stack = []
68- workitem.process(cr, witem, ident, stack=stack)
69- return _update_end(cr, inst_id, ident)
70+ workitem.process(cr, witem, ident, stack=stack, context=context)
71+ return _update_end(cr, inst_id, ident, context)
72
73-def _update_end(cr, inst_id, ident):
74+def _update_end(cr, inst_id, ident, context=None):
75 cr.execute('select wkf_id from wkf_instance where id=%s', (inst_id,))
76 wkf_id = cr.fetchone()[0]
77 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,))
78@@ -74,7 +74,7 @@
79 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,))
80 for i in cr.fetchall():
81 for act_name in act_names:
82- validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0])
83+ validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0], context)
84 return ok
85
86
87
88=== modified file 'openerp/workflow/wkf_expr.py'
89--- openerp/workflow/wkf_expr.py 2011-09-14 10:25:05 +0000
90+++ openerp/workflow/wkf_expr.py 2011-12-13 17:01:27 +0000
91@@ -26,22 +26,23 @@
92 from openerp.tools.safe_eval import safe_eval as eval
93
94 class Env(dict):
95- def __init__(self, cr, uid, model, ids):
96+ def __init__(self, cr, uid, model, ids, context=None):
97 self.cr = cr
98 self.uid = uid
99 self.model = model
100 self.ids = ids
101+ self.context = context
102 self.obj = pooler.get_pool(cr.dbname).get(model)
103 self.columns = self.obj._columns.keys() + self.obj._inherit_fields.keys()
104
105 def __getitem__(self, key):
106 if (key in self.columns) or (key in dir(self.obj)):
107- res = self.obj.browse(self.cr, self.uid, self.ids[0])
108+ res = self.obj.browse(self.cr, self.uid, self.ids[0], self.context)
109 return res[key]
110 else:
111 return super(Env, self).__getitem__(key)
112
113-def _eval_expr(cr, ident, workitem, action):
114+def _eval_expr(cr, ident, workitem, action, context=None):
115 ret=False
116 assert action, 'You used a NULL action in a workflow, use dummy node instead.'
117 for line in action.split('\n'):
118@@ -54,20 +55,23 @@
119 elif line =='False':
120 ret=False
121 else:
122- env = Env(cr, uid, model, ids)
123+ env = Env(cr, uid, model, ids, context)
124 ret = eval(line, env, nocopy=True)
125 return ret
126
127-def execute_action(cr, ident, workitem, activity):
128+def execute_action(cr, ident, workitem, activity, context=None):
129 obj = pooler.get_pool(cr.dbname).get('ir.actions.server')
130+ ctx = {}
131+ if context is not None:
132+ ctx.update( context )
133 ctx = {'active_model':ident[1], 'active_id':ident[2], 'active_ids':[ident[2]]}
134 result = obj.run(cr, ident[0], [activity['action_id']], ctx)
135 return result
136
137-def execute(cr, ident, workitem, activity):
138- return _eval_expr(cr, ident, workitem, activity['action'])
139+def execute(cr, ident, workitem, activity, context=None):
140+ return _eval_expr(cr, ident, workitem, activity['action'], context)
141
142-def check(cr, workitem, ident, transition, signal):
143+def check(cr, workitem, ident, transition, signal, context=None):
144 if transition['signal'] and signal != transition['signal']:
145 return False
146
147@@ -78,8 +82,7 @@
148 if not transition['group_id'] in user_groups:
149 return False
150
151- return _eval_expr(cr, ident, workitem, transition['condition'])
152-
153+ return _eval_expr(cr, ident, workitem, transition['condition'], context)
154
155 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
156
157
158=== modified file 'openerp/workflow/wkf_service.py'
159--- openerp/workflow/wkf_service.py 2011-09-24 14:52:58 +0000
160+++ openerp/workflow/wkf_service.py 2011-12-13 17:01:27 +0000
161@@ -44,7 +44,10 @@
162 def clear_cache(self, cr, uid):
163 self.wkf_on_create_cache[cr.dbname]={}
164
165- def trg_write(self, uid, res_type, res_id, cr):
166+ def trg_write(self, uid, res_type, res_id, cr, context=None):
167+ print "WWWWWWWWWWWWW", context
168+ import traceback
169+ traceback.print_stack()
170 """
171 Reevaluates the specified workflow instance. Thus if any condition for
172 a transition have been changed in the backend, then running ``trg_write``
173@@ -57,9 +60,10 @@
174 ident = (uid,res_type,res_id)
175 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'))
176 for (id,) in cr.fetchall():
177- instance.update(cr, id, ident)
178+ instance.update(cr, id, ident, context)
179
180- def trg_trigger(self, uid, res_type, res_id, cr):
181+ def trg_trigger(self, uid, res_type, res_id, cr, context=None):
182+ print "TTTTTTTTTTTTTTT", context
183 """
184 Activate a trigger.
185
186@@ -75,7 +79,7 @@
187 for (instance_id,) in res:
188 cr.execute('select %s,res_type,res_id from wkf_instance where id=%s', (uid, instance_id,))
189 ident = cr.fetchone()
190- instance.update(cr, instance_id, ident)
191+ instance.update(cr, instance_id, ident, context)
192
193 def trg_delete(self, uid, res_type, res_id, cr):
194 """
195@@ -88,7 +92,7 @@
196 ident = (uid,res_type,res_id)
197 instance.delete(cr, ident)
198
199- def trg_create(self, uid, res_type, res_id, cr):
200+ def trg_create(self, uid, res_type, res_id, cr, context=None):
201 """
202 Create a new workflow instance
203
204@@ -105,9 +109,10 @@
205 wkf_ids = cr.fetchall()
206 self.wkf_on_create_cache[cr.dbname][res_type] = wkf_ids
207 for (wkf_id,) in wkf_ids:
208- instance.create(cr, ident, wkf_id)
209+ instance.create(cr, ident, wkf_id, context)
210
211- def trg_validate(self, uid, res_type, res_id, signal, cr):
212+ def trg_validate(self, uid, res_type, res_id, signal, cr, context=None):
213+ print "VVVVVVVVVVV", context
214 """
215 Fire a signal on a given workflow instance
216
217@@ -121,7 +126,7 @@
218 # ids of all active workflow instances for a corresponding resource (id, model_nam)
219 cr.execute('select id from wkf_instance where res_id=%s and res_type=%s and state=%s', (res_id, res_type, 'active'))
220 for (id,) in cr.fetchall():
221- res2 = instance.validate(cr, id, ident, signal)
222+ res2 = instance.validate(cr, id, ident, signal, context=context)
223 result = result or res2
224 return result
225
226
227=== modified file 'openerp/workflow/workitem.py'
228--- openerp/workflow/workitem.py 2011-12-11 10:21:40 +0000
229+++ openerp/workflow/workitem.py 2011-12-13 17:01:27 +0000
230@@ -30,7 +30,7 @@
231 import wkf_expr
232 import wkf_logs
233
234-def create(cr, act_datas, inst_id, ident, stack):
235+def create(cr, act_datas, inst_id, ident, stack, context=None):
236 for act in act_datas:
237 cr.execute("select nextval('wkf_workitem_id_seq')")
238 id_new = cr.fetchone()[0]
239@@ -38,9 +38,9 @@
240 cr.execute('select * from wkf_workitem where id=%s',(id_new,))
241 res = cr.dictfetchone()
242 wkf_logs.log(cr,ident,act['id'],'active')
243- process(cr, res, ident, stack=stack)
244+ process(cr, res, ident, stack=stack, context=context)
245
246-def process(cr, workitem, ident, signal=None, force_running=False, stack=None):
247+def process(cr, workitem, ident, signal=None, force_running=False, stack=None, context=None):
248 if stack is None:
249 raise 'Error !!!'
250 result = True
251@@ -50,7 +50,7 @@
252 triggers = False
253 if workitem['state']=='active':
254 triggers = True
255- result = _execute(cr, workitem, activity, ident, stack)
256+ result = _execute(cr, workitem, activity, ident, stack, context)
257 if not result:
258 return False
259
260@@ -58,7 +58,7 @@
261 pass
262
263 if workitem['state']=='complete' or force_running:
264- ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack)
265+ ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack, context)
266 triggers = triggers and not ok
267
268 if triggers:
269@@ -66,7 +66,7 @@
270 alltrans = cr.dictfetchall()
271 for trans in alltrans:
272 if trans['trigger_model']:
273- ids = wkf_expr._eval_expr(cr,ident,workitem,trans['trigger_expr_id'])
274+ ids = wkf_expr._eval_expr(cr,ident,workitem,trans['trigger_expr_id'], context)
275 for res_id in ids:
276 cr.execute('select nextval(\'wkf_triggers_id_seq\')')
277 id =cr.fetchone()[0]
278@@ -82,7 +82,7 @@
279 workitem['state'] = state
280 wkf_logs.log(cr,ident,activity['id'],state)
281
282-def _execute(cr, workitem, activity, ident, stack):
283+def _execute(cr, workitem, activity, ident, stack, context=None):
284 result = True
285 #
286 # send a signal to parent workflow (signal: subflow.signal_name)
287@@ -97,18 +97,18 @@
288 if workitem['state']=='active':
289 _state_set(cr, workitem, activity, 'complete', ident)
290 if activity['action_id']:
291- res2 = wkf_expr.execute_action(cr, ident, workitem, activity)
292+ res2 = wkf_expr.execute_action(cr, ident, workitem, activity, context)
293 if res2:
294 stack.append(res2)
295 result=res2
296 elif activity['kind']=='function':
297 if workitem['state']=='active':
298 _state_set(cr, workitem, activity, 'running', ident)
299- returned_action = wkf_expr.execute(cr, ident, workitem, activity)
300+ returned_action = wkf_expr.execute(cr, ident, workitem, activity, context)
301 if type(returned_action) in (dict,):
302 stack.append(returned_action)
303 if activity['action_id']:
304- res2 = wkf_expr.execute_action(cr, ident, workitem, activity)
305+ res2 = wkf_expr.execute_action(cr, ident, workitem, activity, context)
306 # A client action has been returned
307 if res2:
308 stack.append(res2)
309@@ -119,13 +119,13 @@
310 _state_set(cr, workitem, activity, 'running', ident)
311 cr.execute('delete from wkf_workitem where inst_id=%s and id<>%s', (workitem['inst_id'], workitem['id']))
312 if activity['action']:
313- wkf_expr.execute(cr, ident, workitem, activity)
314+ wkf_expr.execute(cr, ident, workitem, activity, context)
315 _state_set(cr, workitem, activity, 'complete', ident)
316 elif activity['kind']=='subflow':
317 if workitem['state']=='active':
318 _state_set(cr, workitem, activity, 'running', ident)
319 if activity.get('action', False):
320- id_new = wkf_expr.execute(cr, ident, workitem, activity)
321+ id_new = wkf_expr.execute(cr, ident, workitem, activity, context)
322 if not (id_new):
323 cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
324 return False
325@@ -133,7 +133,7 @@
326 cr.execute('select id from wkf_instance where res_id=%s and wkf_id=%s', (id_new,activity['subflow_id']))
327 id_new = cr.fetchone()[0]
328 else:
329- id_new = instance.create(cr, ident, activity['subflow_id'])
330+ id_new = instance.create(cr, ident, activity['subflow_id'], context)
331 cr.execute('update wkf_workitem set subflow_id=%s where id=%s', (id_new, workitem['id']))
332 workitem['subflow_id'] = id_new
333 if workitem['state']=='running':
334@@ -142,11 +142,11 @@
335 if state=='complete':
336 _state_set(cr, workitem, activity, 'complete', ident)
337 for t in signal_todo:
338- instance.validate(cr, t[0], t[1], t[2], force_running=True)
339+ instance.validate(cr, t[0], t[1], t[2], force_running=True, context=context)
340
341 return result
342
343-def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None):
344+def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None, context=None):
345 if stack is None:
346 raise 'Error !!!'
347 cr.execute('select * from wkf_transition where act_from=%s', (workitem['act_id'],))
348@@ -155,7 +155,7 @@
349 alltrans = cr.dictfetchall()
350 if split_mode=='XOR' or split_mode=='OR':
351 for transition in alltrans:
352- if wkf_expr.check(cr, workitem, ident, transition,signal):
353+ if wkf_expr.check(cr, workitem, ident, transition, signal, context):
354 test = True
355 transitions.append((transition['id'], workitem['inst_id']))
356 if split_mode=='XOR':
357@@ -163,7 +163,7 @@
358 else:
359 test = True
360 for transition in alltrans:
361- if not wkf_expr.check(cr, workitem, ident, transition,signal):
362+ if not wkf_expr.check(cr, workitem, ident, transition, signal, context):
363 test = False
364 break
365 cr.execute('select count(*) from wkf_witm_trans where trans_id=%s and inst_id=%s', (transition['id'], workitem['inst_id']))
366@@ -173,15 +173,15 @@
367 cr.executemany('insert into wkf_witm_trans (trans_id,inst_id) values (%s,%s)', transitions)
368 cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
369 for t in transitions:
370- _join_test(cr, t[0], t[1], ident, stack)
371+ _join_test(cr, t[0], t[1], ident, stack, context)
372 return True
373 return False
374
375-def _join_test(cr, trans_id, inst_id, ident, stack):
376+def _join_test(cr, trans_id, inst_id, ident, stack, context=None):
377 cr.execute('select * from wkf_activity where id=(select act_to from wkf_transition where id=%s)', (trans_id,))
378 activity = cr.dictfetchone()
379 if activity['join_mode']=='XOR':
380- create(cr,[activity], inst_id, ident, stack)
381+ create(cr,[activity], inst_id, ident, stack, context)
382 cr.execute('delete from wkf_witm_trans where inst_id=%s and trans_id=%s', (inst_id,trans_id))
383 else:
384 cr.execute('select id from wkf_transition where act_to=%s', (activity['id'],))
385@@ -196,7 +196,7 @@
386 if ok:
387 for (id,) in trans_ids:
388 cr.execute('delete from wkf_witm_trans where trans_id=%s and inst_id=%s', (id,inst_id))
389- create(cr, [activity], inst_id, ident, stack)
390+ create(cr, [activity], inst_id, ident, stack, context)
391
392 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
393