Merge lp:~openerp-community/openobject-server/context-in-workflows into lp:openobject-server
- context-in-workflows
- Merge into trunk
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 |
Related bugs: |
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 |
Commit message
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/
[..]
File "/home/
return _eval_expr(cr, ident, workitem, transition[
File "/home/
ret = eval(line, env, nocopy=True)
File "/home/
return eval(test_
File "", line 1, in <module>
File "/home/
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:/
(in orm.py)
Raphaël Valyi - http://www.akretion.com (rvalyi) : | # |
Christophe CHAUVET (christophe-chauvet) wrote : | # |
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
Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
Hi Raphaël,
thanks for the effort! A tiny detail in openerp/
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.
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
>
Raphaël Valyi - http://www.akretion.com (rvalyi) wrote : | # |
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://
or
http://
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...
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.
Raphaël Valyi - http://www.akretion.com (rvalyi) wrote : | # |
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://
or
http://
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...
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-
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.
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-
> 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://
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
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 |
+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.