diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py index e7e4c7bab1..060b1a1645 100644 --- a/trytond/osv/orm.py +++ b/trytond/osv/orm.py @@ -1422,7 +1422,8 @@ class ORM(object): wf_service = LocalService("workflow") for obj_id in ids: - wf_service.trg_delete(user, self._name, obj_id, cursor) + wf_service.trg_delete(user, self._name, obj_id, cursor, + context=context) if not self.check_xml_record(cursor, user, ids, None, context=context): self.raise_user_error(cursor, 'delete_xml_record', @@ -1699,7 +1700,8 @@ class ORM(object): wf_service = LocalService("workflow") for obj_id in ids: - wf_service.trg_write(user, self._name, obj_id, cursor) + wf_service.trg_write(user, self._name, obj_id, cursor, + context=context) return True def __clean_defaults(self, defaults): @@ -1839,7 +1841,7 @@ class ORM(object): self._update_tree(cursor, user, id_new, k, field.left, field.right) wf_service = LocalService("workflow") - wf_service.trg_create(user, self._name, id_new, cursor) + wf_service.trg_create(user, self._name, id_new, cursor, context=context) return id_new def fields_get(self, cursor, user, fields_names=None, context=None): diff --git a/trytond/osv/osv.py b/trytond/osv/osv.py index 77eddf4fa0..11035c800e 100644 --- a/trytond/osv/osv.py +++ b/trytond/osv/osv.py @@ -90,10 +90,12 @@ class OSVService(Service): cursor.close() return res - def exec_workflow_cr(self, cursor, user, object_name, method, *args): + def exec_workflow_cr(self, cursor, user, object_name, method, object_id, + *args): wf_service = LocalService("workflow") try: - wf_service.trg_validate(user, object_name, args[0], method, cursor) + wf_service.trg_validate(user, object_name, object_id, method, + cursor, *args) except Exception, exception: if CONFIG['verbose']: tb_s = reduce(lambda x, y: x+y, @@ -108,13 +110,14 @@ class OSVService(Service): raise return True - def exec_workflow(self, dbname, user, object_name, method, *args): + def exec_workflow(self, dbname, user, object_name, method, object_id, + *args): cursor = pooler.get_db(dbname).cursor() # TODO add retry when exception for concurency update try: try: res = self.exec_workflow_cr(cursor, user, object_name, method, - *args) + object_id, *args) cursor.commit() except Exception: cursor.rollback() diff --git a/trytond/web_service/object.py b/trytond/web_service/object.py index 45beef1c5e..5ed9e4b041 100644 --- a/trytond/web_service/object.py +++ b/trytond/web_service/object.py @@ -13,12 +13,12 @@ class Object(Service): Service.export_method(self, self.obj_list) def exec_workflow(self, database, user, session, object_name, method, - object_id): + object_id, *args): security.check(database, user, session) Cache.clean(database) service = LocalService("object_proxy") res = service.exec_workflow(database, user, object_name, method, - object_id) + object_id, *args) return res def execute(self, database, user, session, object_name, method, *args): diff --git a/trytond/wkf_service/instance.py b/trytond/wkf_service/instance.py index 2ce517e1fb..0e0b5291ad 100644 --- a/trytond/wkf_service/instance.py +++ b/trytond/wkf_service/instance.py @@ -1,7 +1,7 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. import workitem -def create(cursor, ident, wkf_id): +def create(cursor, ident, wkf_id, context=None): (user, res_type, res_id) = ident cursor.execute("SELECT NEXTVAL('wkf_instance_id_seq')") (id_new,) = cursor.fetchone() @@ -12,30 +12,32 @@ def create(cursor, ident, wkf_id): cursor.execute('SELECT * FROM wkf_activity ' \ 'WHERE flow_start = True and workflow = %s', (wkf_id,)) res = cursor.dictfetchall() - workitem.create(cursor, res, id_new, ident) + workitem.create(cursor, res, id_new, ident, context=context) update(cursor, id_new, ident) return id_new -def delete(cursor, ident): +def delete(cursor, ident, context=None): (user, res_type, res_id) = ident cursor.execute('DELETE FROM wkf_instance ' \ 'WHERE res_id = %s AND res_type = %s', (res_id, res_type)) -def validate(cursor, inst_id, ident, signal, force_running=False): +def validate(cursor, inst_id, ident, signal, force_running=False, + context=None): cursor.execute("SELECT * FROM wkf_workitem WHERE instance = %s", (inst_id,)) for witem in cursor.dictfetchall(): - workitem.process(cursor, witem, ident, signal, force_running) - return _update_end(cursor, inst_id, ident) + workitem.process(cursor, witem, ident, signal, force_running, + context=context) + return _update_end(cursor, inst_id, ident, context=context) -def update(cursor, inst_id, ident): +def update(cursor, inst_id, ident, context=None): cursor.execute("SELECT * FROM wkf_workitem WHERE instance = %s", (inst_id,)) for witem in cursor.dictfetchall(): - workitem.process(cursor, witem, ident) - return _update_end(cursor, inst_id, ident) + workitem.process(cursor, witem, ident, context=context) + return _update_end(cursor, inst_id, ident, context=context) -def _update_end(cursor, inst_id, ident): +def _update_end(cursor, inst_id, ident, context=None): cursor.execute('SELECT state, flow_stop FROM wkf_workitem w ' \ 'LEFT JOIN wkf_activity a ' \ 'ON (a.id = w.activity) WHERE w.instance = %s', @@ -65,5 +67,5 @@ def _update_end(cursor, inst_id, ident): for i in cursor.fetchall(): for act_name in act_names: validate(cursor, i[0], (ident[0], i[1], i[2]), - 'subflow.' + act_name[0]) + 'subflow.' + act_name[0], context=context) return res diff --git a/trytond/wkf_service/wkf_expr.py b/trytond/wkf_service/wkf_expr.py index 51a51e69f9..be6fc25ba9 100644 --- a/trytond/wkf_service/wkf_expr.py +++ b/trytond/wkf_service/wkf_expr.py @@ -4,46 +4,53 @@ from trytond import pooler class EnvCall(object): - def __init__(self, obj, cursor, user, method, obj_id): + def __init__(self, obj, cursor, user, method, obj_id, context=None): self.obj = obj self.cursor = cursor self.user = user self.method = method self.obj_id = obj_id + self.context = context def __call__(self, *args, **kargs): + kargs = kargs.copy() + kargs['context'] = self.context return getattr(self.obj, self.method)(self.cursor, self.user, self.obj_id, *args, **kargs) class Env(dict): - def __init__(self, cursor, user, model, obj_id): + def __init__(self, cursor, user, model, obj_id, context=None): super(Env, self).__init__() self.cursor = cursor self.user = user self.model = model self.obj_id = obj_id + self.context = context self.obj = pooler.get_pool(cursor.dbname).get(model) self.browse = self.obj.browse(cursor, user, obj_id) self.columns = self.obj._columns.keys() + \ self.obj._inherit_fields.keys() def __getitem__(self, key): - if key in self.columns: + if key == 'context': + return self.context + elif key in self.columns: res = self.browse[key] return res elif key in dir(self.obj): - return EnvCall(self.obj, self.cursor, self.user, key, self.obj_id) + return EnvCall(self.obj, self.cursor, self.user, key, self.obj_id, + context=self.context) else: return super(Env, self).__getitem__(key) -def eval_expr(cursor, ident, action): +def eval_expr(cursor, ident, action, context=None): res = False user = ident[0] model = ident[1] obj_id = ident[2] - env = Env(cursor, user, model, obj_id) + env = Env(cursor, user, model, obj_id, context=context) for line in action.split('\n'): if line == 'True': res = True @@ -53,10 +60,10 @@ def eval_expr(cursor, ident, action): res = eval(line, env) return res -def execute(cursor, ident, activity): - return eval_expr(cursor, ident, activity['action']) +def execute(cursor, ident, activity, context=None): + return eval_expr(cursor, ident, activity['action'], context=context) -def check(cursor, ident, transition, signal): +def check(cursor, ident, transition, signal, context=None): res = True if transition['signal']: res = (signal == transition['signal']) @@ -64,7 +71,9 @@ def check(cursor, ident, transition, signal): if transition['group']: user = ident[0] user_obj = pooler.get_pool(cursor.dbname).get('res.user') - user_groups = user_obj.read(cursor, user, user, ['groups'])['groups'] + user_groups = user_obj.read(cursor, user, user, ['groups'], + context=context)['groups'] res = res and transition['group'] in user_groups - res = res and eval_expr(cursor, ident, transition['condition']) + res = res and eval_expr(cursor, ident, transition['condition'], + context=context) return res diff --git a/trytond/wkf_service/wkf_service.py b/trytond/wkf_service/wkf_service.py index 1e3a01f591..3784b61eac 100644 --- a/trytond/wkf_service/wkf_service.py +++ b/trytond/wkf_service/wkf_service.py @@ -22,16 +22,16 @@ class WorkflowService(Service): "Clear workflow cache" self.wkf_on_create_cache[cursor.dbname] = {} - def trg_write(self, user, res_type, res_id, cursor): + def trg_write(self, user, res_type, res_id, cursor, context=None): "Trigger write" ident = (user, res_type, res_id) cursor.execute('SELECT id FROM wkf_instance ' \ 'WHERE res_id = %s AND res_type = %s AND state = %s', (res_id, res_type, 'active')) for (instance_id,) in cursor.fetchall(): - instance.update(cursor, instance_id, ident) + instance.update(cursor, instance_id, ident, context=context) - def trg_trigger(self, user, res_type, res_id, cursor): + def trg_trigger(self, user, res_type, res_id, cursor, context=None): "Trigger trigger" cursor.execute('SELECT instance FROM wkf_trigger ' \ 'WHERE res_id = %s AND model = %s', (res_id, res_type)) @@ -40,14 +40,14 @@ class WorkflowService(Service): cursor.execute('SELECT uid, res_type, res_id FROM wkf_instance ' \ 'WHERE id = %s', (instance_id,)) ident = cursor.fetchone() - instance.update(cursor, instance_id, ident) + instance.update(cursor, instance_id, ident, context=context) - def trg_delete(self, user, res_type, res_id, cursor): + def trg_delete(self, user, res_type, res_id, cursor, context=None): "Trigger delete" ident = (user, res_type, res_id) - instance.delete(cursor, ident) + instance.delete(cursor, ident, context=context) - def trg_create(self, user, res_type, res_id, cursor): + def trg_create(self, user, res_type, res_id, cursor, context=None): "Trigger create" ident = (user, res_type, res_id) self.wkf_on_create_cache.setdefault(cursor.dbname, {}) @@ -68,9 +68,10 @@ class WorkflowService(Service): raise Exception("Error", "Another active workflow already "\ "exist for this record: %s@%s."% \ (res_id, res_type)) - instance.create(cursor, ident, wkf_id) + instance.create(cursor, ident, wkf_id, context=context) - def trg_validate(self, user, res_type, res_id, signal, cursor): + def trg_validate(self, user, res_type, res_id, signal, cursor, + context=None): "Trigger validate" ident = (user, res_type, res_id) # ids of all active workflow instances @@ -79,9 +80,11 @@ class WorkflowService(Service): 'WHERE res_id = %s AND res_type = %s AND state = %s', (res_id, res_type, 'active')) for (instance_id,) in cursor.fetchall(): - instance.validate(cursor, instance_id, ident, signal) + instance.validate(cursor, instance_id, ident, signal, + context=context) - def trg_redirect(self, user, res_type, res_id, new_rid, cursor): + def trg_redirect(self, user, res_type, res_id, new_rid, cursor, + context=None): """ Trigger redirect make all workitems which are waiting for a (subflow) workflow instance diff --git a/trytond/wkf_service/workitem.py b/trytond/wkf_service/workitem.py index 7cbee6291b..78d5fd2286 100644 --- a/trytond/wkf_service/workitem.py +++ b/trytond/wkf_service/workitem.py @@ -2,7 +2,7 @@ import wkf_expr import instance -def create(cursor, act_datas, inst_id, ident): +def create(cursor, act_datas, inst_id, ident, context=None): for act in act_datas: cursor.execute("SELECT NEXTVAL('wkf_workitem_id_seq')") (id_new,) = cursor.fetchone() @@ -11,16 +11,17 @@ def create(cursor, act_datas, inst_id, ident): (id_new, act['id'], inst_id)) cursor.execute('SELECT * FROM wkf_workitem WHERE id=%s', (id_new,)) res = cursor.dictfetchone() - process(cursor, res, ident) + process(cursor, res, ident, context=context) -def process(cursor, workitem, ident, signal=None, force_running=False): +def process(cursor, workitem, ident, signal=None, force_running=False, + context=None): cursor.execute('SELECT * FROM wkf_activity WHERE id = %s', (workitem['activity'],)) activity = cursor.dictfetchone() triggers = False if workitem['state'] == 'active': triggers = True - if not _execute(cursor, workitem, activity, ident): + if not _execute(cursor, workitem, activity, ident, context=context): return False if workitem['state'] == 'running': @@ -28,7 +29,7 @@ def process(cursor, workitem, ident, signal=None, force_running=False): if workitem['state'] == 'complete' or force_running: res = _split_test(cursor, workitem, activity['split_mode'], ident, - signal) + signal, context=context) triggers = triggers and not res if triggers: @@ -38,7 +39,7 @@ def process(cursor, workitem, ident, signal=None, force_running=False): for trans in alltrans: if trans['trigger_model']: ids = wkf_expr.eval_expr(cursor, ident, - trans['trigger_expr_id']) + trans['trigger_expr_id'], context=context) for res_id in ids: cursor.execute('SELECT NEXTVAL(\'wkf_trigger_id_seq\')') (new_id,) = cursor.fetchone() @@ -54,7 +55,7 @@ def _state_set(cursor, workitem, state): 'SET state = %s WHERE id = %s', (state, workitem['id'])) workitem['state'] = state -def _execute(cursor, workitem, activity, ident): +def _execute(cursor, workitem, activity, ident, context=None): "send a signal to parent workflow (signal: subflow.signal_name)" if (workitem['state'] == 'active') and activity['signal_send']: @@ -67,7 +68,8 @@ def _execute(cursor, workitem, activity, ident): "WHERE wi.subflow = %s)", (workitem['instance'],)) for i in cursor.fetchall(): instance.validate(cursor, i[0], (ident[0], i[1], i[2]), - activity['signal_send'], force_running=True) + activity['signal_send'], force_running=True, + context=context) if activity['kind'] == 'dummy': if workitem['state'] == 'active': @@ -75,7 +77,7 @@ def _execute(cursor, workitem, activity, ident): elif activity['kind'] == 'function': if workitem['state'] == 'active': _state_set(cursor, workitem, 'running') - wkf_expr.execute(cursor, ident, activity) + wkf_expr.execute(cursor, ident, activity, context=context) _state_set(cursor, workitem, 'complete') elif activity['kind'] == 'stopall': if workitem['state'] == 'active': @@ -84,13 +86,14 @@ def _execute(cursor, workitem, activity, ident): 'WHERE instance = %s AND id <> %s', (workitem['instance'], workitem['id'])) if activity['action']: - wkf_expr.execute(cursor, ident, activity) + wkf_expr.execute(cursor, ident, activity, context=context) _state_set(cursor, workitem, 'complete') elif activity['kind'] == 'subflow': if workitem['state'] == 'active': _state_set(cursor, workitem, 'running') if activity.get('action', False): - id_new = wkf_expr.execute(cursor, ident, activity) + id_new = wkf_expr.execute(cursor, ident, activity, + context=context) if not (id_new): cursor.execute('DELETE FROM wkf_workitem ' \ 'WHERE id = %s', (workitem['id'],)) @@ -103,7 +106,8 @@ def _execute(cursor, workitem, activity, ident): (id_new, activity['subflow'])) (id_new,) = cursor.fetchone() else: - id_new = instance.create(cursor, ident, activity['subflow']) + id_new = instance.create(cursor, ident, activity['subflow'], + context=context) cursor.execute('UPDATE wkf_workitem ' \ 'SET subflow = %s WHERE id = %s', (id_new, workitem['id'])) @@ -116,7 +120,7 @@ def _execute(cursor, workitem, activity, ident): _state_set(cursor, workitem, 'complete') return True -def _split_test(cursor, workitem, split_mode, ident, signal=None): +def _split_test(cursor, workitem, split_mode, ident, signal=None, context=None): test = False transitions = [] cursor.execute('SELECT * FROM wkf_transition ' \ @@ -124,7 +128,8 @@ def _split_test(cursor, workitem, split_mode, ident, signal=None): alltrans = cursor.dictfetchall() if split_mode == 'XOR' or split_mode == 'OR': for transition in alltrans: - if wkf_expr.check(cursor, ident, transition, signal): + if wkf_expr.check(cursor, ident, transition, signal, + context=context): test = True transitions.append((transition['id'], workitem['instance'])) if split_mode == 'XOR': @@ -132,7 +137,8 @@ def _split_test(cursor, workitem, split_mode, ident, signal=None): else: test = True for transition in alltrans: - if not wkf_expr.check(cursor, ident, transition, signal): + if not wkf_expr.check(cursor, ident, transition, signal, + context=context): test = False break cursor.execute('SELECT COUNT(*) ' \ @@ -147,18 +153,19 @@ def _split_test(cursor, workitem, split_mode, ident, signal=None): cursor.execute('DELETE FROM wkf_workitem WHERE id = %s', (workitem['id'],)) for transition in transitions: - _join_test(cursor, transition[0], transition[1], ident) + _join_test(cursor, transition[0], transition[1], ident, + context=context) return True return False -def _join_test(cursor, trans_id, inst_id, ident): +def _join_test(cursor, trans_id, inst_id, ident, context=None): cursor.execute('SELECT a.* FROM wkf_activity a ' \ 'LEFT JOIN wkf_transition t ' \ 'ON (a.id = t.act_to) ' \ 'WHERE t.id = %s', (trans_id,)) activity = cursor.dictfetchone() if activity['join_mode'] == 'XOR': - create(cursor, [activity], inst_id, ident) + create(cursor, [activity], inst_id, ident, context=context) cursor.execute('DELETE FROM wkf_witm_trans ' \ 'WHERE inst_id = %s AND trans_id = %s', (inst_id, trans_id)) else: @@ -179,4 +186,4 @@ def _join_test(cursor, trans_id, inst_id, ident): cursor.execute('DELETE FROM wkf_witm_trans ' \ 'WHERE trans_id = %s AND inst_id = %s', (trans_id, inst_id)) - create(cursor, [activity], inst_id, ident) + create(cursor, [activity], inst_id, ident, context=context)