From 7ef45906cfa7b8c6bcefb6280f3ab76e82256080 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Mon, 1 Feb 2010 15:23:55 +0530 Subject: [PATCH 01/37] [IMP]: initial work of diagram view. bzr revid: apa@tinyerp.com-20100201095355-gohn3f6z4eb98d87 --- bin/addons/base/ir/ir_ui_view.py | 1 + bin/addons/base/ir/workflow/workflow_view.xml | 17 +++++++++ bin/addons/base/rng/view.rng | 38 +++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index b66d7eedcf6..2391737c425 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -59,6 +59,7 @@ class view(osv.osv): ('mdx','mdx'), ('graph', 'Graph'), ('calendar', 'Calendar'), + ('diagram','Diagram'), ('gantt', 'Gantt'), ('search','Search')), 'View Type', required=True), 'arch': fields.text('View Architecture', required=True), diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index a0bfb7fdc03..6d3ddf9f5cc 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -23,6 +23,22 @@ + + + workflow.diagram + workflow + diagram + + + + + + + + + + + workflow.tree workflow @@ -41,6 +57,7 @@ workflow form + tree,form,diagram diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index 3a9c8f92369..82112a834c7 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -30,6 +30,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -332,6 +368,7 @@ + @@ -507,6 +544,7 @@ + From 487b9ea3dd501f48cb0a4e93cf9fa94047c12461 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Tue, 2 Feb 2010 17:24:22 +0530 Subject: [PATCH 02/37] [IMP]:Make get_graph method as generic.(REF.APA,YSA) bzr revid: apa@tinyerp.com-20100202115422-0mg6annejy8f09nd --- bin/addons/base/ir/ir_ui_view.py | 89 +++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 2391737c425..6c932eb312e 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # @@ -15,12 +15,13 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# along with this program. If not, see . # ############################################################################## from osv import fields,osv from lxml import etree +from tools import graph import tools import netsvc import os @@ -130,6 +131,90 @@ class view(osv.osv): return result return super(view, self).write(cr, uid, ids, vals, context) + + def graph_get(self, cr, uid, id, scale, context={}): + + _Node={} + _Arrow={} + _Ids = self.search(cr, uid, [('type','=','diagram')], context=context) + _Models = self.browse(cr, uid, _Ids, context) + + for _Model in _Models: + + from lxml import etree + import StringIO,xml.dom.minidom + + d = xml.dom.minidom.parseString(str(_Model.arch)) + de = d.documentElement + + def node_attributes(node): + result = {} + attrs = node.attributes + if attrs is None: + return {} + for i in range(attrs.length): + result[attrs.item(i).localName] = str(attrs.item(i).nodeValue) + if attrs.item(i).localName == "digits" and isinstance(attrs.item(i).nodeValue, (str, unicode)): + result[attrs.item(i).localName] = eval(attrs.item(i).nodeValue) + return result + + for n in [i for i in de.childNodes]: + if n.nodeName=='node': + _Node.update(node_attributes(n)) + elif n.nodeName=='arrow': + _Arrow.update(node_attributes(n)) + + _Model_Obj = self.pool.get(str(_Model.model)) + + for _Field,_Value in _Model_Obj._columns.items(): + if _Value._type == 'one2many': + if _Value._obj==_Node['object']: + _Node_Obj = self.pool.get(_Value._obj) + _Node_Field = _Field + _Flag=False + for _Field_1,_Value_1 in _Node_Obj._columns.items(): + if _Value_1._type == 'one2many': + if _Value_1._obj==_Arrow['object']: + _Arrow_Obj = self.pool.get(_Value_1._obj) + for _Key,_Val in _Arrow_Obj._columns.items(): + if _Arrow['source'] == _Key: + if _Flag: + _Source_Field = _Field_1 + if _Arrow['destination'] == _Key: + if not _Flag: + _Destination_Field = _Field_1 + _Flag = True + nodes= [] + nodes_name = [] + transitions = [] + start = [] + tres = {} + no_ancester = [] + workflow = _Model_Obj.read(cr, uid, id, [],context) + for a in _Node_Obj.read(cr,uid, workflow[_Node_Field],[]): + nodes_name.append((a['id'],a['name'])) + nodes.append(a['id']) + if a['flow_start']: + start.append(a['id']) + else: + if not a[_Source_Field]: + no_ancester.append(a['id']) + + for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): + transitions.append((a['id'], t[_Arrow['destination']][0])) + tres[t['id']] = (a['id'], t[_Arrow['destination']][0]) + + g = graph(nodes, transitions, no_ancester) + g.process(start) + g.scale(*scale) + result = g.result_get() + results = {} + + for node in nodes_name: + results[str(node[0])] = result[node[0]] + results[str(node[0])]['name'] = node[1] + + return {'nodes': results, 'transitions': tres} view() class view_sc(osv.osv): From d6c5fa26d32cbdc55f280b781dd042d778f87429 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Fri, 5 Feb 2010 18:28:52 +0530 Subject: [PATCH 03/37] [IMP]:improved the graph_get method for generic. bzr revid: apa@tinyerp.com-20100205125852-ykgf5ti2vuhswfle --- bin/addons/base/ir/ir_ui_view.py | 125 +++++++++++-------------------- 1 file changed, 45 insertions(+), 80 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 6c932eb312e..af4445e0b32 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -132,89 +132,54 @@ class view(osv.osv): return super(view, self).write(cr, uid, ids, vals, context) - def graph_get(self, cr, uid, id, scale, context={}): + def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node, scale,context={}): + nodes= [] + nodes_name = [] + transitions = [] + start = [] + tres = {} + no_ancester = [] - _Node={} - _Arrow={} - _Ids = self.search(cr, uid, [('type','=','diagram')], context=context) - _Models = self.browse(cr, uid, _Ids, context) + _Model_Obj = self.pool.get(model) + _Node_Obj = self.pool.get(node_obj) + _Arrow_Obj = self.pool.get(conn_obj) - for _Model in _Models: + for model_key,model_value in _Model_Obj._columns.items(): + if model_value._type == 'one2many': + if model_value._obj==node_obj: + _Node_Field=model_key + flag=False + for node_key,node_value in _Node_Obj._columns.items(): + if node_value._type=='one2many': + if src_node in _Arrow_Obj._columns: + if flag: + _Source_Field = node_key + if des_node in _Arrow_Obj._columns: + if not flag: + _Destination_Field = node_key + flag = True - from lxml import etree - import StringIO,xml.dom.minidom - - d = xml.dom.minidom.parseString(str(_Model.arch)) - de = d.documentElement - - def node_attributes(node): - result = {} - attrs = node.attributes - if attrs is None: - return {} - for i in range(attrs.length): - result[attrs.item(i).localName] = str(attrs.item(i).nodeValue) - if attrs.item(i).localName == "digits" and isinstance(attrs.item(i).nodeValue, (str, unicode)): - result[attrs.item(i).localName] = eval(attrs.item(i).nodeValue) - return result - - for n in [i for i in de.childNodes]: - if n.nodeName=='node': - _Node.update(node_attributes(n)) - elif n.nodeName=='arrow': - _Arrow.update(node_attributes(n)) - - _Model_Obj = self.pool.get(str(_Model.model)) - - for _Field,_Value in _Model_Obj._columns.items(): - if _Value._type == 'one2many': - if _Value._obj==_Node['object']: - _Node_Obj = self.pool.get(_Value._obj) - _Node_Field = _Field - _Flag=False - for _Field_1,_Value_1 in _Node_Obj._columns.items(): - if _Value_1._type == 'one2many': - if _Value_1._obj==_Arrow['object']: - _Arrow_Obj = self.pool.get(_Value_1._obj) - for _Key,_Val in _Arrow_Obj._columns.items(): - if _Arrow['source'] == _Key: - if _Flag: - _Source_Field = _Field_1 - if _Arrow['destination'] == _Key: - if not _Flag: - _Destination_Field = _Field_1 - _Flag = True - nodes= [] - nodes_name = [] - transitions = [] - start = [] - tres = {} - no_ancester = [] - workflow = _Model_Obj.read(cr, uid, id, [],context) - for a in _Node_Obj.read(cr,uid, workflow[_Node_Field],[]): - nodes_name.append((a['id'],a['name'])) - nodes.append(a['id']) - if a['flow_start']: - start.append(a['id']) - else: - if not a[_Source_Field]: - no_ancester.append(a['id']) - - for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): - transitions.append((a['id'], t[_Arrow['destination']][0])) - tres[t['id']] = (a['id'], t[_Arrow['destination']][0]) - - g = graph(nodes, transitions, no_ancester) - g.process(start) - g.scale(*scale) - result = g.result_get() - results = {} - - for node in nodes_name: - results[str(node[0])] = result[node[0]] - results[str(node[0])]['name'] = node[1] - - return {'nodes': results, 'transitions': tres} + datas = _Model_Obj.read(cr, uid, id, [],context) + for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]): + nodes_name.append((a['id'],a['name'])) + nodes.append(a['id']) + if a['flow_start']: + start.append(a['id']) + else: + if not a[_Source_Field]: + no_ancester.append(a['id']) + for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): + transitions.append((a['id'], t[des_node][0])) + tres[t['id']] = (a['id'], t[des_node][0]) + g = graph(nodes, transitions, no_ancester) + g.process(start) + g.scale(*scale) + result = g.result_get() + results = {} + for node in nodes_name: + results[str(node[0])] = result[node[0]] + results[str(node[0])]['name'] = node[1] + return {'nodes': results, 'transitions': tres} view() class view_sc(osv.osv): From e3bac5fec496f84f7f08314453fdf6737819c3bb Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Fri, 5 Feb 2010 20:08:13 +0530 Subject: [PATCH 04/37] [IMP]:improve code bzr revid: apa@tinyerp.com-20100205143813-s9nt379rgqdjx6pw --- bin/addons/base/ir/ir_ui_view.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index af4445e0b32..10d2b694ad2 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -145,19 +145,17 @@ class view(osv.osv): _Arrow_Obj = self.pool.get(conn_obj) for model_key,model_value in _Model_Obj._columns.items(): - if model_value._type == 'one2many': + if model_value._type=='one2many' and model_value._obj==node_obj: if model_value._obj==node_obj: _Node_Field=model_key flag=False for node_key,node_value in _Node_Obj._columns.items(): if node_value._type=='one2many': - if src_node in _Arrow_Obj._columns: - if flag: - _Source_Field = node_key - if des_node in _Arrow_Obj._columns: - if not flag: - _Destination_Field = node_key - flag = True + if src_node in _Arrow_Obj._columns and flag: + _Source_Field=node_key + if des_node in _Arrow_Obj._columns and not flag: + _Destination_Field=node_key + flag = True datas = _Model_Obj.read(cr, uid, id, [],context) for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]): From 01e68a6a56ad961d575f26c3e11b22b3587e84a4 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Fri, 5 Feb 2010 20:09:02 +0530 Subject: [PATCH 05/37] [IMP]:improve code bzr revid: apa@tinyerp.com-20100205143902-ih121kafmupku6pz --- bin/addons/base/ir/ir_ui_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 10d2b694ad2..59d0e431f3a 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -145,7 +145,7 @@ class view(osv.osv): _Arrow_Obj = self.pool.get(conn_obj) for model_key,model_value in _Model_Obj._columns.items(): - if model_value._type=='one2many' and model_value._obj==node_obj: + if model_value._type=='one2many': if model_value._obj==node_obj: _Node_Field=model_key flag=False From 7308ba4ca4796a01b73059841fc414f889dbbcb0 Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Tue, 9 Feb 2010 13:11:27 +0530 Subject: [PATCH 06/37] [IMP] add shape and bgcolor attribute add in diagram node bzr revid: ysa@tinyerp.co.in-20100209074127-okyugub65gmj56yo --- bin/addons/base/ir/workflow/workflow_view.xml | 2 +- bin/addons/base/rng/view.rng | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index 6d3ddf9f5cc..435d0f3d61f 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -30,7 +30,7 @@ diagram - + diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index 82112a834c7..90ca81251bc 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -44,7 +44,9 @@ - + + + From cc4bf70f1baa7920a278832ea4ab6b47d4e6737d Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Tue, 9 Feb 2010 15:41:32 +0530 Subject: [PATCH 07/37] [IMP] workflow get_graph method keep in comment bzr revid: ysa@tinyerp.co.in-20100209101132-kem10xbj4at56l0f --- bin/addons/base/ir/workflow/workflow.py | 68 ++++++++++++------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/bin/addons/base/ir/workflow/workflow.py b/bin/addons/base/ir/workflow/workflow.py index 89e18a08fb4..bc51ee1f8e4 100644 --- a/bin/addons/base/ir/workflow/workflow.py +++ b/bin/addons/base/ir/workflow/workflow.py @@ -49,40 +49,40 @@ class workflow(osv.osv): # - def graph_get(self, cr, uid, id, scale, context={}): - - nodes= [] - nodes_name = [] - transitions = [] - start = [] - tres = {} - no_ancester = [] - workflow = self.browse(cr, uid, id, context) - for a in workflow.activities: - nodes_name.append((a.id,a.name)) - nodes.append(a.id) - if a.flow_start: - start.append(a.id) - else: - if not a.in_transitions: - no_ancester.append(a.id) - - for t in a.out_transitions: - transitions.append((a.id, t.act_to.id)) - tres[t.id] = (a.id, t.act_to.id) - - - g = graph(nodes, transitions, no_ancester) - g.process(start) - g.scale(*scale) - result = g.result_get() - results = {} - - for node in nodes_name: - results[str(node[0])] = result[node[0]] - results[str(node[0])]['name'] = node[1] - - return {'nodes': results, 'transitions': tres} +# def graph_get(self, cr, uid, id, scale, context={}): +# +# nodes= [] +# nodes_name = [] +# transitions = [] +# start = [] +# tres = {} +# no_ancester = [] +# workflow = self.browse(cr, uid, id, context) +# for a in workflow.activities: +# nodes_name.append((a.id,a.name)) +# nodes.append(a.id) +# if a.flow_start: +# start.append(a.id) +# else: +# if not a.in_transitions: +# no_ancester.append(a.id) +# +# for t in a.out_transitions: +# transitions.append((a.id, t.act_to.id)) +# tres[t.id] = (a.id, t.act_to.id) +# +# +# g = graph(nodes, transitions, no_ancester) +# g.process(start) +# g.scale(*scale) +# result = g.result_get() +# results = {} +# +# for node in nodes_name: +# results[str(node[0])] = result[node[0]] +# results[str(node[0])]['name'] = node[1] +# +# return {'nodes': results, 'transitions': tres} def create(self, cr, user, vals, context=None): From 03f896fed992c0a62ef5d38bb80e29a9fa753ead Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Wed, 10 Feb 2010 14:28:03 +0530 Subject: [PATCH 08/37] [IMP] make optional node attribute in diagram view bzr revid: ysa@tinyerp.co.in-20100210085803-gp59xfor6zvv0taw --- bin/addons/base/rng/view.rng | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index 54cae3f5688..3b9d63ec989 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -45,8 +45,8 @@ - - + + From dfb5786fe03ab88a76e320f5fbb39d79b51a433c Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Wed, 10 Feb 2010 16:14:16 +0530 Subject: [PATCH 09/37] [IMP]:Improved the generic method for diagram view. bzr revid: apa@tinyerp.com-20100210104416-u5esh24kpb9j9ljq --- bin/addons/base/ir/ir_ui_view.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 59d0e431f3a..036ab1c582b 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -144,6 +144,7 @@ class view(osv.osv): _Node_Obj = self.pool.get(node_obj) _Arrow_Obj = self.pool.get(conn_obj) + print model, node_obj, conn_obj, src_node, des_node for model_key,model_value in _Model_Obj._columns.items(): if model_value._type=='one2many': if model_value._obj==node_obj: @@ -151,17 +152,19 @@ class view(osv.osv): flag=False for node_key,node_value in _Node_Obj._columns.items(): if node_value._type=='one2many': - if src_node in _Arrow_Obj._columns and flag: - _Source_Field=node_key - if des_node in _Arrow_Obj._columns and not flag: - _Destination_Field=node_key - flag = True + if node_value._obj==conn_obj: + if src_node in _Arrow_Obj._columns and flag: + _Source_Field=node_key + if des_node in _Arrow_Obj._columns and not flag: + _Destination_Field=node_key + flag = True datas = _Model_Obj.read(cr, uid, id, [],context) + print ":::::::::::::::::",_Node_Field,_Source_Field,_Destination_Field for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]): nodes_name.append((a['id'],a['name'])) nodes.append(a['id']) - if a['flow_start']: + if a.has_key('flow_start') and a['flow_start']: start.append(a['id']) else: if not a[_Source_Field]: @@ -169,7 +172,10 @@ class view(osv.osv): for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): transitions.append((a['id'], t[des_node][0])) tres[t['id']] = (a['id'], t[des_node][0]) + + print nodes, transitions, no_ancester g = graph(nodes, transitions, no_ancester) +# print start g.process(start) g.scale(*scale) result = g.result_get() @@ -177,6 +183,7 @@ class view(osv.osv): for node in nodes_name: results[str(node[0])] = result[node[0]] results[str(node[0])]['name'] = node[1] +# print {'nodes': results, 'transitions': tres} return {'nodes': results, 'transitions': tres} view() From c3b91a0a9d04bbc6897caeb96ec35b5d6db777a5 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Wed, 10 Feb 2010 17:15:18 +0530 Subject: [PATCH 10/37] [IMP]:improved code bzr revid: apa@tinyerp.com-20100210114518-ck2on1npoatsan1n --- bin/addons/base/ir/ir_ui_view.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 036ab1c582b..58fd6255399 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -144,11 +144,11 @@ class view(osv.osv): _Node_Obj = self.pool.get(node_obj) _Arrow_Obj = self.pool.get(conn_obj) - print model, node_obj, conn_obj, src_node, des_node for model_key,model_value in _Model_Obj._columns.items(): if model_value._type=='one2many': if model_value._obj==node_obj: _Node_Field=model_key + _Model_Field=model_value._fields_id flag=False for node_key,node_value in _Node_Obj._columns.items(): if node_value._type=='one2many': @@ -160,7 +160,6 @@ class view(osv.osv): flag = True datas = _Model_Obj.read(cr, uid, id, [],context) - print ":::::::::::::::::",_Node_Field,_Source_Field,_Destination_Field for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]): nodes_name.append((a['id'],a['name'])) nodes.append(a['id']) @@ -173,9 +172,7 @@ class view(osv.osv): transitions.append((a['id'], t[des_node][0])) tres[t['id']] = (a['id'], t[des_node][0]) - print nodes, transitions, no_ancester g = graph(nodes, transitions, no_ancester) -# print start g.process(start) g.scale(*scale) result = g.result_get() @@ -183,7 +180,6 @@ class view(osv.osv): for node in nodes_name: results[str(node[0])] = result[node[0]] results[str(node[0])]['name'] = node[1] -# print {'nodes': results, 'transitions': tres} return {'nodes': results, 'transitions': tres} view() From bff06b021a86ed6a8b9a74391826ed8486403fee Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Thu, 18 Feb 2010 12:08:58 +0530 Subject: [PATCH 11/37] Improve graph_get method. bzr revid: ysa@tinyerp.co.in-20100218063858-by8e5t1ycoei9snc --- bin/addons/base/ir/ir_ui_view.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 58fd6255399..4241d79178e 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -138,6 +138,7 @@ class view(osv.osv): transitions = [] start = [] tres = {} + signal = {} no_ancester = [] _Model_Obj = self.pool.get(model) @@ -170,8 +171,12 @@ class view(osv.osv): no_ancester.append(a['id']) for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): transitions.append((a['id'], t[des_node][0])) - tres[t['id']] = (a['id'], t[des_node][0]) - + tres[str(t['id'])] = (a['id'],t[des_node][0]) + if t['signal']: + signal[str(t['id'])] = t['signal'] + else: + signal[str(t['id'])] = t['condition'] + g = graph(nodes, transitions, no_ancester) g.process(start) g.scale(*scale) @@ -180,7 +185,7 @@ class view(osv.osv): for node in nodes_name: results[str(node[0])] = result[node[0]] results[str(node[0])]['name'] = node[1] - return {'nodes': results, 'transitions': tres} + return {'nodes': results, 'transitions': tres, 'signal' : signal} view() class view_sc(osv.osv): From cc6685b7612580e28124101e4166b7d65ce2454a Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 18 Feb 2010 14:48:04 +0530 Subject: [PATCH 12/37] [IMP]:code comment bzr revid: apa@tinyerp.com-20100218091804-0hcpceryiib2pmfh --- bin/addons/base/ir/ir_ui_view.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 4241d79178e..b59ddbf720f 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -172,11 +172,10 @@ class view(osv.osv): for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): transitions.append((a['id'], t[des_node][0])) tres[str(t['id'])] = (a['id'],t[des_node][0]) - if t['signal']: - signal[str(t['id'])] = t['signal'] - else: - signal[str(t['id'])] = t['condition'] - +# if t['signal']: +# signal[str(t['id'])] = t['signal'] +# else: +# signal[str(t['id'])] = t['condition'] g = graph(nodes, transitions, no_ancester) g.process(start) g.scale(*scale) @@ -185,7 +184,8 @@ class view(osv.osv): for node in nodes_name: results[str(node[0])] = result[node[0]] results[str(node[0])]['name'] = node[1] - return {'nodes': results, 'transitions': tres, 'signal' : signal} + return {'nodes': results, 'transitions': tres} +# return {'nodes': results, 'transitions': tres, 'signal' : signal} view() class view_sc(osv.osv): From 7b96c8e03a7c586f99d3d1bd7e28b6f475383bb6 Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Thu, 18 Feb 2010 16:59:35 +0530 Subject: [PATCH 13/37] Improve code bzr revid: ysa@tinyerp.co.in-20100218112935-mqfj2gum9l0z977f --- bin/addons/base/ir/ir_ui_view.py | 11 +++++------ bin/addons/base/ir/workflow/workflow_view.xml | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index b59ddbf720f..a092f2b2f45 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -172,10 +172,10 @@ class view(osv.osv): for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): transitions.append((a['id'], t[des_node][0])) tres[str(t['id'])] = (a['id'],t[des_node][0]) -# if t['signal']: -# signal[str(t['id'])] = t['signal'] -# else: -# signal[str(t['id'])] = t['condition'] + if t['signal']: + signal[str(t['id'])] = t['signal'] + else: + signal[str(t['id'])] = t['condition'] g = graph(nodes, transitions, no_ancester) g.process(start) g.scale(*scale) @@ -184,8 +184,7 @@ class view(osv.osv): for node in nodes_name: results[str(node[0])] = result[node[0]] results[str(node[0])]['name'] = node[1] - return {'nodes': results, 'transitions': tres} -# return {'nodes': results, 'transitions': tres, 'signal' : signal} + return {'nodes': results, 'transitions': tres, 'signal' : signal} view() class view_sc(osv.osv): diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index 435d0f3d61f..5cc8933c698 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -33,7 +33,7 @@ - + From 34d8f3faa0ede721bb13ebe68be8c887fd422dc6 Mon Sep 17 00:00:00 2001 From: "ksh (Axelor)" Date: Thu, 18 Feb 2010 17:10:34 +0530 Subject: [PATCH 14/37] [IMP] get active activities for the workflow for the record bzr revid: ksh@openerp.co.in-20100218114034-8gcx0yxege2taaid --- bin/addons/base/ir/workflow/workflow.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bin/addons/base/ir/workflow/workflow.py b/bin/addons/base/ir/workflow/workflow.py index bc51ee1f8e4..2a551e9549f 100644 --- a/bin/addons/base/ir/workflow/workflow.py +++ b/bin/addons/base/ir/workflow/workflow.py @@ -44,6 +44,25 @@ class workflow(osv.osv): wf_service.clear_cache(cr, user) return super(workflow, self).write(cr, user, ids, vals, context=context) + def get_active_workitems(self, cr, uid, res, res_id, context={}): + + cr.execute('select * from wkf where osv=%s limit 1',(res,)) + wkfinfo = cr.dictfetchone() + workitems = [] + + if wkfinfo: + cr.execute('SELECT id FROM wkf_instance \ + WHERE res_id=%s AND wkf_id=%s \ + ORDER BY state LIMIT 1', + (res_id, wkfinfo['id'])) + inst_id = cr.fetchone() + + cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%s group by act_id', (inst_id,)) + workitems = dict(cr.fetchall()) + + return {'wkf': wkfinfo, 'workitems': workitems} + + # # scale = (vertical-distance, horizontal-distance, min-node-width(optional), min-node-height(optional), margin(default=20)) # From 6eadc92bee06569e984cf9bcb034c90b1e0d76f8 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Mon, 22 Feb 2010 15:48:23 +0530 Subject: [PATCH 15/37] [IMP]:improved diagram code with signal in graph. bzr revid: apa@tinyerp.com-20100222101823-ksphjjdecxjn6cnp --- bin/addons/base/ir/ir_ui_view.py | 31 +++++++++++++++---------------- bin/addons/base/rng/view.rng | 15 ++++++++------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index a092f2b2f45..e7be5117828 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -132,18 +132,18 @@ class view(osv.osv): return super(view, self).write(cr, uid, ids, vals, context) - def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node, scale,context={}): - nodes= [] - nodes_name = [] - transitions = [] - start = [] - tres = {} - signal = {} - no_ancester = [] + def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node,signal,scale,context={}): + nodes=[] + nodes_name=[] + transitions=[] + start=[] + tres={} + sig={} + no_ancester=[] - _Model_Obj = self.pool.get(model) - _Node_Obj = self.pool.get(node_obj) - _Arrow_Obj = self.pool.get(conn_obj) + _Model_Obj=self.pool.get(model) + _Node_Obj=self.pool.get(node_obj) + _Arrow_Obj=self.pool.get(conn_obj) for model_key,model_value in _Model_Obj._columns.items(): if model_value._type=='one2many': @@ -172,10 +172,9 @@ class view(osv.osv): for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]): transitions.append((a['id'], t[des_node][0])) tres[str(t['id'])] = (a['id'],t[des_node][0]) - if t['signal']: - signal[str(t['id'])] = t['signal'] - else: - signal[str(t['id'])] = t['condition'] + if t.has_key(str(signal)) and str(t[signal])=='False': + t[signal]=' ' + sig[str(t['id'])] = (a['id'],t.get(signal,' ')) g = graph(nodes, transitions, no_ancester) g.process(start) g.scale(*scale) @@ -184,7 +183,7 @@ class view(osv.osv): for node in nodes_name: results[str(node[0])] = result[node[0]] results[str(node[0])]['name'] = node[1] - return {'nodes': results, 'transitions': tres, 'signal' : signal} + return {'nodes': results, 'transitions': tres, 'signal' : sig} view() class view_sc(osv.osv): diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index 3b9d63ec989..a69f06d9c39 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -32,7 +32,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -49,25 +49,26 @@ - + - + - + + - + - + From 7da66c83aef25ac247f5ace6a1bfb0763fe1be44 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Mon, 22 Feb 2010 16:34:19 +0530 Subject: [PATCH 16/37] smallchanges bzr revid: apa@tinyerp.com-20100222110419-0k51dftd64uycz02 --- bin/addons/base/ir/workflow/workflow_view.xml | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index 5cc8933c698..db42935b36f 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -2,13 +2,13 @@ - + - + workflow.form workflow @@ -23,20 +23,20 @@ - + workflow.diagram workflow diagram - + - + @@ -51,7 +51,7 @@ - + Workflows workflow @@ -126,7 +126,7 @@ - + Activities workflow.activity @@ -134,14 +134,14 @@ - - + + - + workflow.transition.form workflow.transition @@ -174,8 +174,8 @@ - - + + Transitions workflow.transition @@ -183,13 +183,13 @@ - + - + workflow.instance.form workflow.instance @@ -218,7 +218,7 @@ - + Instances workflow.instance @@ -226,13 +226,13 @@ - + - + workflow.workitem.form workflow.workitem @@ -259,7 +259,7 @@ - + Workitems workflow.workitem @@ -267,6 +267,6 @@ - + From c9763d3200c4c518fa8e462293b6f2b63d53a972 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Wed, 24 Feb 2010 17:18:06 +0530 Subject: [PATCH 17/37] [IMP]:improve orm for diagram view. bzr revid: apa@tinyerp.com-20100224114806-s10137rwshuflw3c --- bin/osv/orm.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 63235e158d4..38d6dd0c473 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -247,7 +247,7 @@ class browse_record(object): new_data[n] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(f._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context) else: new_data[n] = data[n] - self._data[data['id']].update(new_data) + self._data[data['id']].update(new_data) if not name in self._data[self._id]: #how did this happen? self.logger.notifyChannel("browse_record", netsvc.LOG_ERROR, @@ -1100,7 +1100,7 @@ class orm_template(object): if column._domain and not isinstance(column._domain, (str, unicode)): dom = column._domain dom += eval(node.get('domain','[]'), {'uid':user, 'time':time}) - context.update(eval(node.get('context','{}'))) + context.update(eval(node.get('context','{}'))) attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context) if (node.get('required') and not int(node.get('required'))) or not column.required: attrs['selection'].append((False,'')) @@ -1186,7 +1186,21 @@ class orm_template(object): button.set('readonly', str(int(not can_click))) arch = etree.tostring(node, encoding="utf-8").replace('\t', '') - fields = self.fields_get(cr, user, fields_def.keys(), context) + + #code for diagram view. + fields={} + if node.tag=='diagram': + if node.getchildren()[0].tag=='node': + node_fields=self.pool.get(node.getchildren()[0].get('object')).fields_get(cr, user, fields_def.keys(), context) + if node.getchildren()[1].tag=='arrow': + arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, fields_def.keys(), context) + for key,value in node_fields.items(): + fields[key]=value + for key,value in arrow_fields.items(): + fields[key]=value + else: + fields = self.fields_get(cr, user, fields_def.keys(), context) + for field in fields_def: if field == 'id': # sometime, the view may containt the (invisible) field 'id' needed for a domain (when 2 objects have cross references) @@ -1202,7 +1216,6 @@ class orm_template(object): msg += "\n\nEither you wrongly customised this view, or some modules bringing those views are not compatible with your current data model" netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg) raise except_orm('View error', msg) - return arch, fields def __get_default_calendar_view(self): @@ -1855,7 +1868,7 @@ class orm(orm_template): float_int_fields = filter(lambda x: fget[x]['type'] in ('float','integer'), fields) sum = {} - group_by = groupby + group_by = groupby if fget.get(groupby,False) and fget[groupby]['type'] in ('date','datetime'): flist = "to_char(%s,'yyyy-mm') as %s "%(groupby,groupby) groupby = "to_char(%s,'yyyy-mm')"%(groupby) @@ -2032,12 +2045,12 @@ class orm(orm_template): if isinstance(f, fields.one2many): cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,)) - + if self.pool.get(f._obj): if f._fields_id not in self.pool.get(f._obj)._columns.keys(): if not self.pool.get(f._obj)._inherits or (f._fields_id not in self.pool.get(f._obj)._inherit_fields.keys()): raise except_orm('Programming Error', ("There is no reference field '%s' found for '%s'") % (f._fields_id,f._obj,)) - + if cr.fetchone(): cr.execute("SELECT count(1) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj, f._fields_id)) res = cr.fetchone()[0] From 0dbd853c36fdb735f499c0c4f08fa4c4612bb5ba Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Thu, 25 Feb 2010 12:51:04 +0530 Subject: [PATCH 18/37] [IMP] add field in diagram view bzr revid: ysa@tinyerp.co.in-20100225072104-6jnnoc30xepzt1cs --- bin/addons/base/ir/workflow/workflow_view.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index db42935b36f..9e9bc47c300 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -34,7 +34,9 @@ - + + + From 6cf08db5e37879878659da8f2b134cf66e6e24fe Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Fri, 26 Feb 2010 14:00:41 +0530 Subject: [PATCH 19/37] [IMP]:improved code for diagram view bzr revid: apa@tinyerp.com-20100226083041-qzsttqezrzuuwniz --- bin/addons/base/ir/ir_ui_view.py | 5 +- bin/tools/graph.py | 495 ++++++++++++++++--------------- 2 files changed, 251 insertions(+), 249 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index e7be5117828..52631a06990 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -162,8 +162,9 @@ class view(osv.osv): datas = _Model_Obj.read(cr, uid, id, [],context) for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]): - nodes_name.append((a['id'],a['name'])) - nodes.append(a['id']) + if a[_Source_Field] or a[_Destination_Field]: + nodes_name.append((a['id'],a['name'])) + nodes.append(a['id']) if a.has_key('flow_start') and a['flow_start']: start.append(a['id']) else: diff --git a/bin/tools/graph.py b/bin/tools/graph.py index 7b0375504f2..c0d2194f0ce 100644 --- a/bin/tools/graph.py +++ b/bin/tools/graph.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # @@ -16,7 +16,7 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# along with this program. If not, see . # ############################################################################## @@ -26,80 +26,80 @@ import math class graph(object): def __init__(self, nodes, transitions, no_ancester=None): """Initailize graph's object - + @param nodes: list of ids of nodes in the graph @param transitions: list of edges in the graph in the form (source_node, destination_node) - @param no_ancester: list of nodes with no incoming edges + @param no_ancester: list of nodes with no incoming edges """ - + self.nodes = nodes or [] self.edges = transitions or [] self.no_ancester = no_ancester or {} trans = {} - + for t in transitions: trans.setdefault(t[0], []) trans[t[0]].append(t[1]) self.transitions = trans self.result = {} - - + + def init_rank(self): """Computes rank of the nodes of the graph by finding initial feasible tree """ self.edge_wt = {} for link in self.links: self.edge_wt[link] = self.result[link[1]]['x'] - self.result[link[0]]['x'] - + tot_node = self.partial_order.__len__() - #do until all the nodes in the component are searched + #do until all the nodes in the component are searched while self.tight_tree()self.edge_wt[edge]-1): slack = self.edge_wt[edge]-1 new_edge = edge - + if new_edge[0] not in self.reachable_nodes: delta = -(self.edge_wt[new_edge]-1) else: delta = self.edge_wt[new_edge]-1 - + for node in self.result: if node in self.reachable_nodes: self.result[node]['x'] += delta - + for edge in self.edge_wt: - self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] - - self.init_cutvalues() - - + self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] + + self.init_cutvalues() + + def tight_tree(self): self.reachable_nodes = [] self.tree_edges = [] - self.reachable_node(self.start) + self.reachable_node(self.start) return self.reachable_nodes.__len__() - - + + def reachable_node(self, node): - """Find the nodes of the graph which are only 1 rank apart from each other + """Find the nodes of the graph which are only 1 rank apart from each other """ - + if node not in self.reachable_nodes: self.reachable_nodes.append(node) for edge in self.edge_wt: @@ -109,27 +109,27 @@ class graph(object): if edge[1] not in self.reachable_nodes: self.reachable_nodes.append(edge[1]) self.reachable_node(edge[1]) - - + + def init_cutvalues(self): """Initailize cut values of edges of the feasible tree. - Edges with negative cut-values are removed from the tree to optimize rank assignment + Edges with negative cut-values are removed from the tree to optimize rank assignment """ self.cut_edges = {} self.head_nodes = [] i=0; - + for edge in self.tree_edges: self.head_nodes = [] rest_edges = [] rest_edges += self.tree_edges rest_edges.__delitem__(i) - self.head_component(self.start, rest_edges) + self.head_component(self.start, rest_edges) i+=1 positive = 0 negative = 0 for source_node in self.transitions: - if source_node in self.head_nodes: + if source_node in self.head_nodes: for dest_node in self.transitions[source_node]: if dest_node not in self.head_nodes: negative+=1 @@ -140,62 +140,62 @@ class graph(object): self.cut_edges[edge] = positive - negative - + def head_component(self, node, rest_edges): """Find nodes which are reachable from the starting node, after removing an edge """ if node not in self.head_nodes: self.head_nodes.append(node) - + for edge in rest_edges: - if edge[0]==node: + if edge[0]==node: self.head_component(edge[1],rest_edges) - + def process_ranking(self, node, level=0): """Computes initial feasible ranking after making graph acyclic with depth-first search """ - + if node not in self.result: self.result[node] = {'y': None, 'x':level, 'mark':0} else: if level > self.result[node]['x']: self.result[node]['x'] = level - + if self.result[node]['mark']==0: self.result[node]['mark'] = 1 for sec_end in self.transitions.get(node, []): self.process_ranking(sec_end, level+1) - - + + def make_acyclic(self, parent, node, level, tree): """Computes Partial-order of the nodes with depth-first search """ - + if node not in self.partial_order: self.partial_order[node] = {'level':level, 'mark':0} if parent: tree.append((parent, node)) - + if self.partial_order[node]['mark']==0: self.partial_order[node]['mark'] = 1 for sec_end in self.transitions.get(node, []): self.links.append((node, sec_end)) self.make_acyclic(node, sec_end, level+1, tree) - return tree + return tree + - def rev_edges(self, tree): - """reverse the direction of the edges whose source-node-partail_order> destination-node-partail_order - to make the graph acyclic + """reverse the direction of the edges whose source-node-partail_order> destination-node-partail_order + to make the graph acyclic """ Is_Cyclic = False - i=0 + i=0 for link in self.links: src = link[0] des = link[1] - edge_len = self.partial_order[des]['level'] - self.partial_order[src]['level'] + edge_len = self.partial_order[des]['level'] - self.partial_order[src]['level'] if edge_len < 0: self.links.__delitem__(i) self.links.insert(i, (des, src)) @@ -205,9 +205,9 @@ class graph(object): elif math.fabs(edge_len) > 1: Is_Cyclic = True i += 1 - + return Is_Cyclic - + def exchange(self, e, f): """Exchange edges to make feasible-tree optimized @param edge: edge with negative cut-value @@ -216,144 +216,144 @@ class graph(object): self.tree_edges.__delitem__(self.tree_edges.index(e)) self.tree_edges.append(f) self.init_cutvalues() - - + + def enter_edge(self, edge): - """Finds a new_edge with minimum slack value to replace an edge with negative cut-value - - @param edge: edge with negative cut-value + """Finds a new_edge with minimum slack value to replace an edge with negative cut-value + + @param edge: edge with negative cut-value """ - + self.head_nodes = [] rest_edges = [] rest_edges += self.tree_edges rest_edges.__delitem__(rest_edges.index(edge)) self.head_component(self.start, rest_edges) - + if self.head_nodes.__contains__(edge[1]): l = [] for node in self.result: if not self.head_nodes.__contains__(node): - l.append(node) + l.append(node) self.head_nodes = l - + slack = 100 new_edge = edge for source_node in self.transitions: - if source_node in self.head_nodes: + if source_node in self.head_nodes: for dest_node in self.transitions[source_node]: if dest_node not in self.head_nodes: if(slack>(self.edge_wt[edge]-1)): slack = self.edge_wt[edge]-1 new_edge = (source_node, dest_node) - - return new_edge - + + return new_edge + def leave_edge(self): - """Returns the edge with negative cut_value(if exists) + """Returns the edge with negative cut_value(if exists) """ if self.critical_edges: for edge in self.critical_edges: self.cut_edges[edge] = 0 - + for edge in self.cut_edges: if self.cut_edges[edge]<0: return edge - - return None - - + + return None + + def finalize_rank(self, node, level): self.result[node]['x'] = level for destination in self.optimal_edges.get(node, []): self.finalize_rank(destination, level+1) - - + + def normalize(self): """The ranks are normalized by setting the least rank to zero. """ - + least_rank = min(map(lambda x: x['x'], self.result.values())) - + if(least_rank!=0): for node in self.result: - self.result[node]['x']-=least_rank - - - def make_chain(self): - """Edges between nodes more than one rank apart are replaced by chains of unit + self.result[node]['x']-=least_rank + + + def make_chain(self): + """Edges between nodes more than one rank apart are replaced by chains of unit length edges between temporary nodes. """ - + for edge in self.edge_wt: if self.edge_wt[edge]>1: self.transitions[edge[0]].remove(edge[1]) start = self.result[edge[0]]['x'] end = self.result[edge[1]]['x'] - - for rank in range(start+1, end): - if not self.result.get((rank, 'temp'), False): + + for rank in range(start+1, end): + if not self.result.get((rank, 'temp'), False): self.result[(rank, 'temp')] = {'y': None, 'x': rank, 'mark': 0} - + for rank in range(start, end): - if start==rank: + if start==rank: self.transitions[edge[0]].append((rank+1, 'temp')) elif rank==end-1: self.transitions.setdefault((rank, 'temp'), []).append(edge[1]) else: - self.transitions.setdefault((rank, 'temp'), []).append((rank+1, 'temp')) - - + self.transitions.setdefault((rank, 'temp'), []).append((rank+1, 'temp')) + + def init_order(self, node, level): """Initialize orders the nodes in each rank with depth-first search - """ - if not self.result[node]['y']: + """ + if not self.result[node]['y']: self.result[node]['y'] = self.order[level] - self.order[level] = self.order[level]+1 - + self.order[level] = self.order[level]+1 + for sec_end in self.transitions.get(node, []): self.init_order(sec_end, self.result[sec_end]['x']) - - + + def order_heuristic(self): for i in range(12): self.wmedian() - - + + def wmedian(self): """Applies median heuristic to find optimzed order of the nodes with in their ranks """ - for level in self.levels: - + for level in self.levels: + node_median = [] - nodes = self.levels[level] - for node in nodes: + nodes = self.levels[level] + for node in nodes: node_median.append((node, self.median_value(node, level-1))) sort_list = sorted(node_median, key=operator.itemgetter(1)) new_list = [tuple[0] for tuple in sort_list] - + self.levels[level] = new_list order = 0 for node in nodes: self.result[node]['y'] = order - order +=1 + order +=1 def median_value(self, node, adj_rank): - """Returns median value of a vertex , defined as the median position of the adjacent vertices - - @param node: node to process - @param adj_rank: rank 1 less than the node's rank + """Returns median value of a vertex , defined as the median position of the adjacent vertices + + @param node: node to process + @param adj_rank: rank 1 less than the node's rank """ adj_nodes = self.adj_position(node, adj_rank) l = len(adj_nodes) m = l/2 - + if l==0: - return -1.0 + return -1.0 elif l%2 == 1: return adj_nodes[m]#median of the middle element elif l==2: @@ -361,106 +361,106 @@ class graph(object): else: left = adj_nodes[m-1] - adj_nodes[0] right = adj_nodes[l-1] - adj_nodes[m] - return ((adj_nodes[m-1]*right) + (adj_nodes[m]*left))/(left+right) - - + return ((adj_nodes[m-1]*right) + (adj_nodes[m]*left))/(left+right) + + def adj_position(self, node, adj_rank): """Returns list of the present positions of the nodes adjacent to node in the given adjacent rank. - - @param node: node to process - @param adj_rank: rank 1 less than the node's rank + + @param node: node to process + @param adj_rank: rank 1 less than the node's rank """ - - pre_level_nodes = self.levels.get(adj_rank, []) + + pre_level_nodes = self.levels.get(adj_rank, []) adj_nodes = [] - + if pre_level_nodes: for src in pre_level_nodes: if (self.transitions.get(src) and self.transitions[src].__contains__(node)): adj_nodes.append(self.result[src]['y']) - - return adj_nodes - - + + return adj_nodes + + def preprocess_order(self): levels = {} - + for r in self.partial_order: l = self.result[r]['x'] levels.setdefault(l,[]) levels[l].append(r) - + self.levels = levels - - - def graph_order(self): - """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component + + + def graph_order(self): + """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component """ - mid_pos = None + mid_pos = 0.0 max_level = max(map(lambda x: len(x), self.levels.values())) - + for level in self.levels: if level: no = len(self.levels[level]) - factor = (max_level - no) * 0.10 - list = self.levels[level] + factor = (max_level - no) * 0.10 + list = self.levels[level] list.reverse() - + if no%2==0: first_half = list[no/2:] - factor = -factor + factor = -factor else: first_half = list[no/2+1:] if max_level==1:#for the case when horizontal graph is there self.result[list[no/2]]['y'] = mid_pos + (self.result[list[no/2]]['x']%2 * 0.5) else: self.result[list[no/2]]['y'] = mid_pos + factor - - last_half = list[:no/2] - + + last_half = list[:no/2] + i=1 for node in first_half: self.result[node]['y'] = mid_pos - (i + factor) i += 1 - + i=1 for node in last_half: self.result[node]['y'] = mid_pos + (i + factor) i += 1 - else: + else: self.max_order += max_level+1 - mid_pos = self.result[self.start]['y'] - + mid_pos = self.result[self.start]['y'] + def tree_order(self, node, last=0): mid_pos = self.result[node]['y'] l = self.transitions.get(node, []) l.reverse() no = len(l) - + if no%2==0: - first_half = l[no/2:] - factor = 1 + first_half = l[no/2:] + factor = 1 else: first_half = l[no/2+1:] factor = 0 - - last_half = l[:no/2] - + + last_half = l[:no/2] + i=1 for child in first_half: self.result[child]['y'] = mid_pos - (i - (factor * 0.5)) i += 1 - + if self.transitions.get(child, False): if last: self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 last = self.tree_order(child, last) - + if no%2: mid_node = l[no/2] - self.result[mid_node]['y'] = mid_pos - + self.result[mid_node]['y'] = mid_pos + if self.transitions.get((mid_node), False): if last: self.result[mid_node]['y'] = last + len(self.transitions[mid_node])/2 + 1 @@ -469,51 +469,51 @@ class graph(object): if last: self.result[mid_node]['y'] = last + 1 self.result[node]['y'] = self.result[mid_node]['y'] - mid_pos = self.result[node]['y'] - - i=1 + mid_pos = self.result[node]['y'] + + i=1 last_child = None - for child in last_half: + for child in last_half: self.result[child]['y'] = mid_pos + (i - (factor * 0.5)) - last_child = child + last_child = child i += 1 if self.transitions.get(child, False): if last: - self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 + self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 last = self.tree_order(child, last) - + if last_child: last = self.result[last_child]['y'] - - return last - - - def process_order(self): - """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component + + return last + + + def process_order(self): + """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component """ - + if self.Is_Cyclic: max_level = max(map(lambda x: len(x), self.levels.values())) - + if max_level%2: self.result[self.start]['y'] = (max_level+1)/2 + self.max_order + (self.max_order and 1) else: self.result[self.start]['y'] = (max_level)/2 + self.max_order + (self.max_order and 1) - + self.graph_order() - - else: - self.result[self.start]['y'] = 0 + + else: + self.result[self.start]['y'] = 0 self.tree_order(self.start, 0) min_order = math.fabs(min(map(lambda x: x['y'], self.result.values()))) - - index = self.start_nodes.index(self.start) + + index = self.start_nodes.index(self.start) same = False - + roots = [] - if index>0: + if index>0: for start in self.start_nodes[:index]: - same = True + same = True for edge in self.tree_list[start][1:]: if self.tree_list[self.start].__contains__(edge): continue @@ -522,46 +522,48 @@ class graph(object): break if same: roots.append(start) - - if roots: - min_order += self.max_order + + if roots: + min_order += self.max_order else: min_order += self.max_order + 1 - + for level in self.levels: for node in self.levels[level]: self.result[node]['y'] += min_order - + if roots: roots.append(self.start) + print "self.start==>",self.start + print "self.tree_list==>",self.tree_list one_level_el = self.tree_list[self.start][0][1] base = self.result[one_level_el]['y']# * 2 / (index + 2) - - + + no = len(roots) first_half = roots[:no/2] - + if no%2==0: last_half = roots[no/2:] else: last_half = roots[no/2+1:] - + factor = -math.floor(no/2) for start in first_half: - self.result[start]['y'] = base + factor + self.result[start]['y'] = base + factor factor += 1 - + if no%2: self.result[roots[no/2]]['y'] = base + factor factor +=1 - + for start in last_half: - self.result[start]['y'] = base + factor + self.result[start]['y'] = base + factor factor += 1 - + self.max_order = max(map(lambda x: x['y'], self.result.values())) - - def find_starts(self): + + def find_starts(self): """Finds other start nodes of the graph in the case when graph is disconneted """ rem_nodes = [] @@ -577,7 +579,7 @@ class graph(object): count = 0 new_start = rem_nodes[0] largest_tree = [] - + for node in rem_nodes: self.partial_order = {} tree = self.make_acyclic(None, node, 0, []) @@ -589,120 +591,119 @@ class graph(object): if not largest_tree: new_start = rem_nodes[0] rem_nodes.remove(new_start) - + self.start_nodes.append(new_start) - - + + for edge in largest_tree: if rem_nodes.__contains__(edge[0]): rem_nodes.remove(edge[0]) if rem_nodes.__contains__(edge[1]): rem_nodes.remove(edge[1]) - + if not rem_nodes: break - + def rank(self): """Finds the optimized rank of the nodes using Network-simplex algorithm - + @param start: starting node of the component """ - self.levels = {} + self.levels = {} self.critical_edges = [] self.partial_order = {} self.links = [] self.Is_Cyclic = False - + self.tree_list[self.start] = self.make_acyclic(None, self.start, 0, []) - self.Is_Cyclic = self.rev_edges(self.tree_list[self.start]) + self.Is_Cyclic = self.rev_edges(self.tree_list[self.start]) self.process_ranking(self.start) self.init_rank() - + #make cut values of all tree edges to 0 to optimize feasible tree - e = self.leave_edge() - + e = self.leave_edge() + while e : f = self.enter_edge(e) if e==f: self.critical_edges.append(e) else: - self.exchange(e,f) + self.exchange(e,f) e = self.leave_edge() - + #finalize rank using optimum feasible tree # self.optimal_edges = {} # for edge in self.tree_edges: # source = self.optimal_edges.setdefault(edge[0], []) # source.append(edge[1]) - + # self.finalize_rank(self.start, 0) - + #normalization - self.normalize() + self.normalize() for edge in self.edge_wt: self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] - + def order_in_rank(self): """Finds optimized order of the nodes within their ranks using median heuristic - - @param start: starting node of the component + + @param start: starting node of the component """ - + self.make_chain() self.preprocess_order() self.order = {} max_rank = max(map(lambda x: x, self.levels.keys())) - + for i in range(max_rank+1): self.order[i] = 0 - + self.init_order(self.start, self.result[self.start]['x']) - + for level in self.levels: self.levels[level].sort(lambda x, y: cmp(self.result[x]['y'], self.result[y]['y'])) - - self.order_heuristic() + + self.order_heuristic() self.process_order() - + def process(self, starting_node): """Process the graph to find ranks and order of the nodes - - @param starting_node: node from where to start the graph search + + @param starting_node: node from where to start the graph search """ - + self.start_nodes = starting_node or [] - self.partial_order = {} - self.links = [] - self.tree_list = {} - + self.partial_order = {} + self.links = [] + self.tree_list = {} + if self.nodes: if self.start_nodes: #add dummy edges to the nodes which does not have any incoming edges tree = self.make_acyclic(None, self.start_nodes[0], 0, []) - for node in self.no_ancester: for sec_node in self.transitions.get(node, []): if sec_node in self.partial_order.keys(): self.transitions[self.start_nodes[0]].append(node) break - + self.partial_order = {} tree = self.make_acyclic(None, self.start_nodes[0], 0, []) - - - # if graph is disconnected or no start-node is given - #than to find starting_node for each component of the node + + + # if graph is disconnected or no start-node is given + #than to find starting_node for each component of the node if len(self.nodes) > len(self.partial_order): - self.find_starts() - - self.max_order = 0 + self.find_starts() + + self.max_order = 0 #for each component of the graph find ranks and order of the nodes - for s in self.start_nodes: + for s in self.start_nodes: self.start = s self.rank() # First step:Netwoek simplex algorithm - self.order_in_rank() #Second step: ordering nodes within ranks - + self.order_in_rank() #Second step: ordering nodes within ranks + def __str__(self): result = '' @@ -716,21 +717,21 @@ class graph(object): def scale(self, maxx, maxy, nwidth=0, nheight=0, margin=20): """Computes actual co-ordiantes of the nodes """ - + #for flat edges ie. source an destination nodes are on the same rank for src in self.transitions: for des in self.transitions[src]: - if (self.result[des]['x'] - self.result[src]['x'] == 0): + if (self.result[des]['x'] - self.result[src]['x'] == 0): self.result[src]['x'] += 0.08 self.result[des]['x'] -= 0.08 - + factorX = maxx + nheight - factorY = maxy + nwidth - + factorY = maxy + nwidth + for node in self.result: self.result[node]['y'] = (self.result[node]['y']) * factorX + margin self.result[node]['x'] = (self.result[node]['x']) * factorY + margin - + def result_get(self): return self.result @@ -763,14 +764,14 @@ if __name__=='__main__': import ImageDraw img = Image.new("RGB", (800, 600), "#ffffff") draw = ImageDraw.Draw(img) - + result = g.result_get() node_res = {} for node in nodes: node_res[node] = result[node] - + for name,node in node_res.items(): - + draw.arc( (int(node['y']-radius), int(node['x']-radius),int(node['y']+radius), int(node['x']+radius) ), 0, 360, (128,128,128)) draw.text( (int(node['y']), int(node['x'])), name, (128,128,128)) From 878ef36359d3b5281e3626cdf8ba534df9da858b Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Fri, 26 Feb 2010 14:16:15 +0530 Subject: [PATCH 20/37] Remove print statement bzr revid: ysa@tinyerp.co.in-20100226084615-9ehu6ybx7m7w4rfb --- bin/tools/graph.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bin/tools/graph.py b/bin/tools/graph.py index c0d2194f0ce..e25d1c710e0 100644 --- a/bin/tools/graph.py +++ b/bin/tools/graph.py @@ -534,8 +534,6 @@ class graph(object): if roots: roots.append(self.start) - print "self.start==>",self.start - print "self.tree_list==>",self.tree_list one_level_el = self.tree_list[self.start][0][1] base = self.result[one_level_el]['y']# * 2 / (index + 2) From c77153ce30deab2f70dde5c984ffe349b8dfc694 Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Fri, 26 Feb 2010 15:23:21 +0530 Subject: [PATCH 21/37] [IMP] diagram:- blank node pass client side bzr revid: ysa@tinyerp.co.in-20100226095321-1p7wtfq4cm03g56v --- bin/addons/base/ir/ir_ui_view.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 52631a06990..95ce993c8b1 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -140,6 +140,7 @@ class view(osv.osv): tres={} sig={} no_ancester=[] + blank_nodes = [] _Model_Obj=self.pool.get(model) _Node_Obj=self.pool.get(node_obj) @@ -165,6 +166,9 @@ class view(osv.osv): if a[_Source_Field] or a[_Destination_Field]: nodes_name.append((a['id'],a['name'])) nodes.append(a['id']) + else: + blank_nodes.append({'id': a['id'],'name':a['name']}) + if a.has_key('flow_start') and a['flow_start']: start.append(a['id']) else: @@ -184,7 +188,7 @@ class view(osv.osv): for node in nodes_name: results[str(node[0])] = result[node[0]] results[str(node[0])]['name'] = node[1] - return {'nodes': results, 'transitions': tres, 'signal' : sig} + return {'nodes': results, 'transitions': tres, 'signal' : sig, 'blank_nodes': blank_nodes} view() class view_sc(osv.osv): From f22c3eb50f2335db7dfb5ab745881694791f5b38 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Fri, 26 Feb 2010 18:19:38 +0530 Subject: [PATCH 22/37] [IMP]:improved view bzr revid: apa@tinyerp.com-20100226124938-h38kbfnyoy4m9ho8 --- bin/addons/base/ir/workflow/workflow_view.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index 9e9bc47c300..04457f21338 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -32,8 +32,9 @@ + - + From 2aba47bbcfb561ff9c4681193772efcc1492200e Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Tue, 2 Mar 2010 11:06:17 +0530 Subject: [PATCH 23/37] reverted graph.py bzr revid: apa@tinyerp.com-20100302053617-jdpc1ww462jhwffg --- bin/tools/graph.py | 493 +++++++++++++++++++++++---------------------- 1 file changed, 247 insertions(+), 246 deletions(-) diff --git a/bin/tools/graph.py b/bin/tools/graph.py index e25d1c710e0..7b0375504f2 100644 --- a/bin/tools/graph.py +++ b/bin/tools/graph.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # @@ -16,7 +16,7 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# along with this program. If not, see . # ############################################################################## @@ -26,80 +26,80 @@ import math class graph(object): def __init__(self, nodes, transitions, no_ancester=None): """Initailize graph's object - + @param nodes: list of ids of nodes in the graph @param transitions: list of edges in the graph in the form (source_node, destination_node) - @param no_ancester: list of nodes with no incoming edges + @param no_ancester: list of nodes with no incoming edges """ - + self.nodes = nodes or [] self.edges = transitions or [] self.no_ancester = no_ancester or {} trans = {} - + for t in transitions: trans.setdefault(t[0], []) trans[t[0]].append(t[1]) self.transitions = trans self.result = {} - - + + def init_rank(self): """Computes rank of the nodes of the graph by finding initial feasible tree """ self.edge_wt = {} for link in self.links: self.edge_wt[link] = self.result[link[1]]['x'] - self.result[link[0]]['x'] - + tot_node = self.partial_order.__len__() - #do until all the nodes in the component are searched + #do until all the nodes in the component are searched while self.tight_tree()self.edge_wt[edge]-1): slack = self.edge_wt[edge]-1 new_edge = edge - + if new_edge[0] not in self.reachable_nodes: delta = -(self.edge_wt[new_edge]-1) else: delta = self.edge_wt[new_edge]-1 - + for node in self.result: if node in self.reachable_nodes: self.result[node]['x'] += delta - + for edge in self.edge_wt: - self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] - - self.init_cutvalues() - - + self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] + + self.init_cutvalues() + + def tight_tree(self): self.reachable_nodes = [] self.tree_edges = [] - self.reachable_node(self.start) + self.reachable_node(self.start) return self.reachable_nodes.__len__() - - + + def reachable_node(self, node): - """Find the nodes of the graph which are only 1 rank apart from each other + """Find the nodes of the graph which are only 1 rank apart from each other """ - + if node not in self.reachable_nodes: self.reachable_nodes.append(node) for edge in self.edge_wt: @@ -109,27 +109,27 @@ class graph(object): if edge[1] not in self.reachable_nodes: self.reachable_nodes.append(edge[1]) self.reachable_node(edge[1]) - - + + def init_cutvalues(self): """Initailize cut values of edges of the feasible tree. - Edges with negative cut-values are removed from the tree to optimize rank assignment + Edges with negative cut-values are removed from the tree to optimize rank assignment """ self.cut_edges = {} self.head_nodes = [] i=0; - + for edge in self.tree_edges: self.head_nodes = [] rest_edges = [] rest_edges += self.tree_edges rest_edges.__delitem__(i) - self.head_component(self.start, rest_edges) + self.head_component(self.start, rest_edges) i+=1 positive = 0 negative = 0 for source_node in self.transitions: - if source_node in self.head_nodes: + if source_node in self.head_nodes: for dest_node in self.transitions[source_node]: if dest_node not in self.head_nodes: negative+=1 @@ -140,62 +140,62 @@ class graph(object): self.cut_edges[edge] = positive - negative - + def head_component(self, node, rest_edges): """Find nodes which are reachable from the starting node, after removing an edge """ if node not in self.head_nodes: self.head_nodes.append(node) - + for edge in rest_edges: - if edge[0]==node: + if edge[0]==node: self.head_component(edge[1],rest_edges) - + def process_ranking(self, node, level=0): """Computes initial feasible ranking after making graph acyclic with depth-first search """ - + if node not in self.result: self.result[node] = {'y': None, 'x':level, 'mark':0} else: if level > self.result[node]['x']: self.result[node]['x'] = level - + if self.result[node]['mark']==0: self.result[node]['mark'] = 1 for sec_end in self.transitions.get(node, []): self.process_ranking(sec_end, level+1) - - + + def make_acyclic(self, parent, node, level, tree): """Computes Partial-order of the nodes with depth-first search """ - + if node not in self.partial_order: self.partial_order[node] = {'level':level, 'mark':0} if parent: tree.append((parent, node)) - + if self.partial_order[node]['mark']==0: self.partial_order[node]['mark'] = 1 for sec_end in self.transitions.get(node, []): self.links.append((node, sec_end)) self.make_acyclic(node, sec_end, level+1, tree) - return tree - + return tree + def rev_edges(self, tree): - """reverse the direction of the edges whose source-node-partail_order> destination-node-partail_order - to make the graph acyclic + """reverse the direction of the edges whose source-node-partail_order> destination-node-partail_order + to make the graph acyclic """ Is_Cyclic = False - i=0 + i=0 for link in self.links: src = link[0] des = link[1] - edge_len = self.partial_order[des]['level'] - self.partial_order[src]['level'] + edge_len = self.partial_order[des]['level'] - self.partial_order[src]['level'] if edge_len < 0: self.links.__delitem__(i) self.links.insert(i, (des, src)) @@ -205,9 +205,9 @@ class graph(object): elif math.fabs(edge_len) > 1: Is_Cyclic = True i += 1 - + return Is_Cyclic - + def exchange(self, e, f): """Exchange edges to make feasible-tree optimized @param edge: edge with negative cut-value @@ -216,144 +216,144 @@ class graph(object): self.tree_edges.__delitem__(self.tree_edges.index(e)) self.tree_edges.append(f) self.init_cutvalues() - - + + def enter_edge(self, edge): - """Finds a new_edge with minimum slack value to replace an edge with negative cut-value - - @param edge: edge with negative cut-value + """Finds a new_edge with minimum slack value to replace an edge with negative cut-value + + @param edge: edge with negative cut-value """ - + self.head_nodes = [] rest_edges = [] rest_edges += self.tree_edges rest_edges.__delitem__(rest_edges.index(edge)) self.head_component(self.start, rest_edges) - + if self.head_nodes.__contains__(edge[1]): l = [] for node in self.result: if not self.head_nodes.__contains__(node): - l.append(node) + l.append(node) self.head_nodes = l - + slack = 100 new_edge = edge for source_node in self.transitions: - if source_node in self.head_nodes: + if source_node in self.head_nodes: for dest_node in self.transitions[source_node]: if dest_node not in self.head_nodes: if(slack>(self.edge_wt[edge]-1)): slack = self.edge_wt[edge]-1 new_edge = (source_node, dest_node) - - return new_edge - + + return new_edge + def leave_edge(self): - """Returns the edge with negative cut_value(if exists) + """Returns the edge with negative cut_value(if exists) """ if self.critical_edges: for edge in self.critical_edges: self.cut_edges[edge] = 0 - + for edge in self.cut_edges: if self.cut_edges[edge]<0: return edge - - return None - - + + return None + + def finalize_rank(self, node, level): self.result[node]['x'] = level for destination in self.optimal_edges.get(node, []): self.finalize_rank(destination, level+1) - - + + def normalize(self): """The ranks are normalized by setting the least rank to zero. """ - + least_rank = min(map(lambda x: x['x'], self.result.values())) - + if(least_rank!=0): for node in self.result: - self.result[node]['x']-=least_rank - - - def make_chain(self): - """Edges between nodes more than one rank apart are replaced by chains of unit + self.result[node]['x']-=least_rank + + + def make_chain(self): + """Edges between nodes more than one rank apart are replaced by chains of unit length edges between temporary nodes. """ - + for edge in self.edge_wt: if self.edge_wt[edge]>1: self.transitions[edge[0]].remove(edge[1]) start = self.result[edge[0]]['x'] end = self.result[edge[1]]['x'] - - for rank in range(start+1, end): - if not self.result.get((rank, 'temp'), False): + + for rank in range(start+1, end): + if not self.result.get((rank, 'temp'), False): self.result[(rank, 'temp')] = {'y': None, 'x': rank, 'mark': 0} - + for rank in range(start, end): - if start==rank: + if start==rank: self.transitions[edge[0]].append((rank+1, 'temp')) elif rank==end-1: self.transitions.setdefault((rank, 'temp'), []).append(edge[1]) else: - self.transitions.setdefault((rank, 'temp'), []).append((rank+1, 'temp')) - - + self.transitions.setdefault((rank, 'temp'), []).append((rank+1, 'temp')) + + def init_order(self, node, level): """Initialize orders the nodes in each rank with depth-first search - """ - if not self.result[node]['y']: + """ + if not self.result[node]['y']: self.result[node]['y'] = self.order[level] - self.order[level] = self.order[level]+1 - + self.order[level] = self.order[level]+1 + for sec_end in self.transitions.get(node, []): self.init_order(sec_end, self.result[sec_end]['x']) - - + + def order_heuristic(self): for i in range(12): self.wmedian() - - + + def wmedian(self): """Applies median heuristic to find optimzed order of the nodes with in their ranks """ - for level in self.levels: - + for level in self.levels: + node_median = [] - nodes = self.levels[level] - for node in nodes: + nodes = self.levels[level] + for node in nodes: node_median.append((node, self.median_value(node, level-1))) sort_list = sorted(node_median, key=operator.itemgetter(1)) new_list = [tuple[0] for tuple in sort_list] - + self.levels[level] = new_list order = 0 for node in nodes: self.result[node]['y'] = order - order +=1 + order +=1 def median_value(self, node, adj_rank): - """Returns median value of a vertex , defined as the median position of the adjacent vertices - - @param node: node to process - @param adj_rank: rank 1 less than the node's rank + """Returns median value of a vertex , defined as the median position of the adjacent vertices + + @param node: node to process + @param adj_rank: rank 1 less than the node's rank """ adj_nodes = self.adj_position(node, adj_rank) l = len(adj_nodes) m = l/2 - + if l==0: - return -1.0 + return -1.0 elif l%2 == 1: return adj_nodes[m]#median of the middle element elif l==2: @@ -361,106 +361,106 @@ class graph(object): else: left = adj_nodes[m-1] - adj_nodes[0] right = adj_nodes[l-1] - adj_nodes[m] - return ((adj_nodes[m-1]*right) + (adj_nodes[m]*left))/(left+right) - - + return ((adj_nodes[m-1]*right) + (adj_nodes[m]*left))/(left+right) + + def adj_position(self, node, adj_rank): """Returns list of the present positions of the nodes adjacent to node in the given adjacent rank. - - @param node: node to process - @param adj_rank: rank 1 less than the node's rank + + @param node: node to process + @param adj_rank: rank 1 less than the node's rank """ - - pre_level_nodes = self.levels.get(adj_rank, []) + + pre_level_nodes = self.levels.get(adj_rank, []) adj_nodes = [] - + if pre_level_nodes: for src in pre_level_nodes: if (self.transitions.get(src) and self.transitions[src].__contains__(node)): adj_nodes.append(self.result[src]['y']) - - return adj_nodes - - + + return adj_nodes + + def preprocess_order(self): levels = {} - + for r in self.partial_order: l = self.result[r]['x'] levels.setdefault(l,[]) levels[l].append(r) - + self.levels = levels - - - def graph_order(self): - """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component + + + def graph_order(self): + """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component """ - mid_pos = 0.0 + mid_pos = None max_level = max(map(lambda x: len(x), self.levels.values())) - + for level in self.levels: if level: no = len(self.levels[level]) - factor = (max_level - no) * 0.10 - list = self.levels[level] + factor = (max_level - no) * 0.10 + list = self.levels[level] list.reverse() - + if no%2==0: first_half = list[no/2:] - factor = -factor + factor = -factor else: first_half = list[no/2+1:] if max_level==1:#for the case when horizontal graph is there self.result[list[no/2]]['y'] = mid_pos + (self.result[list[no/2]]['x']%2 * 0.5) else: self.result[list[no/2]]['y'] = mid_pos + factor - - last_half = list[:no/2] - + + last_half = list[:no/2] + i=1 for node in first_half: self.result[node]['y'] = mid_pos - (i + factor) i += 1 - + i=1 for node in last_half: self.result[node]['y'] = mid_pos + (i + factor) i += 1 - else: + else: self.max_order += max_level+1 - mid_pos = self.result[self.start]['y'] - + mid_pos = self.result[self.start]['y'] + def tree_order(self, node, last=0): mid_pos = self.result[node]['y'] l = self.transitions.get(node, []) l.reverse() no = len(l) - + if no%2==0: - first_half = l[no/2:] - factor = 1 + first_half = l[no/2:] + factor = 1 else: first_half = l[no/2+1:] factor = 0 - - last_half = l[:no/2] - + + last_half = l[:no/2] + i=1 for child in first_half: self.result[child]['y'] = mid_pos - (i - (factor * 0.5)) i += 1 - + if self.transitions.get(child, False): if last: self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 last = self.tree_order(child, last) - + if no%2: mid_node = l[no/2] - self.result[mid_node]['y'] = mid_pos - + self.result[mid_node]['y'] = mid_pos + if self.transitions.get((mid_node), False): if last: self.result[mid_node]['y'] = last + len(self.transitions[mid_node])/2 + 1 @@ -469,51 +469,51 @@ class graph(object): if last: self.result[mid_node]['y'] = last + 1 self.result[node]['y'] = self.result[mid_node]['y'] - mid_pos = self.result[node]['y'] - - i=1 + mid_pos = self.result[node]['y'] + + i=1 last_child = None - for child in last_half: + for child in last_half: self.result[child]['y'] = mid_pos + (i - (factor * 0.5)) - last_child = child + last_child = child i += 1 if self.transitions.get(child, False): if last: - self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 + self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 last = self.tree_order(child, last) - + if last_child: last = self.result[last_child]['y'] - - return last - - - def process_order(self): - """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component + + return last + + + def process_order(self): + """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component """ - + if self.Is_Cyclic: max_level = max(map(lambda x: len(x), self.levels.values())) - + if max_level%2: self.result[self.start]['y'] = (max_level+1)/2 + self.max_order + (self.max_order and 1) else: self.result[self.start]['y'] = (max_level)/2 + self.max_order + (self.max_order and 1) - + self.graph_order() - - else: - self.result[self.start]['y'] = 0 + + else: + self.result[self.start]['y'] = 0 self.tree_order(self.start, 0) min_order = math.fabs(min(map(lambda x: x['y'], self.result.values()))) - - index = self.start_nodes.index(self.start) + + index = self.start_nodes.index(self.start) same = False - + roots = [] - if index>0: + if index>0: for start in self.start_nodes[:index]: - same = True + same = True for edge in self.tree_list[start][1:]: if self.tree_list[self.start].__contains__(edge): continue @@ -522,46 +522,46 @@ class graph(object): break if same: roots.append(start) - - if roots: - min_order += self.max_order + + if roots: + min_order += self.max_order else: min_order += self.max_order + 1 - + for level in self.levels: for node in self.levels[level]: self.result[node]['y'] += min_order - + if roots: roots.append(self.start) one_level_el = self.tree_list[self.start][0][1] base = self.result[one_level_el]['y']# * 2 / (index + 2) - - + + no = len(roots) first_half = roots[:no/2] - + if no%2==0: last_half = roots[no/2:] else: last_half = roots[no/2+1:] - + factor = -math.floor(no/2) for start in first_half: - self.result[start]['y'] = base + factor + self.result[start]['y'] = base + factor factor += 1 - + if no%2: self.result[roots[no/2]]['y'] = base + factor factor +=1 - + for start in last_half: - self.result[start]['y'] = base + factor + self.result[start]['y'] = base + factor factor += 1 - + self.max_order = max(map(lambda x: x['y'], self.result.values())) - - def find_starts(self): + + def find_starts(self): """Finds other start nodes of the graph in the case when graph is disconneted """ rem_nodes = [] @@ -577,7 +577,7 @@ class graph(object): count = 0 new_start = rem_nodes[0] largest_tree = [] - + for node in rem_nodes: self.partial_order = {} tree = self.make_acyclic(None, node, 0, []) @@ -589,119 +589,120 @@ class graph(object): if not largest_tree: new_start = rem_nodes[0] rem_nodes.remove(new_start) - + self.start_nodes.append(new_start) - - + + for edge in largest_tree: if rem_nodes.__contains__(edge[0]): rem_nodes.remove(edge[0]) if rem_nodes.__contains__(edge[1]): rem_nodes.remove(edge[1]) - + if not rem_nodes: break - + def rank(self): """Finds the optimized rank of the nodes using Network-simplex algorithm - + @param start: starting node of the component """ - self.levels = {} + self.levels = {} self.critical_edges = [] self.partial_order = {} self.links = [] self.Is_Cyclic = False - + self.tree_list[self.start] = self.make_acyclic(None, self.start, 0, []) - self.Is_Cyclic = self.rev_edges(self.tree_list[self.start]) + self.Is_Cyclic = self.rev_edges(self.tree_list[self.start]) self.process_ranking(self.start) self.init_rank() - + #make cut values of all tree edges to 0 to optimize feasible tree - e = self.leave_edge() - + e = self.leave_edge() + while e : f = self.enter_edge(e) if e==f: self.critical_edges.append(e) else: - self.exchange(e,f) + self.exchange(e,f) e = self.leave_edge() - + #finalize rank using optimum feasible tree # self.optimal_edges = {} # for edge in self.tree_edges: # source = self.optimal_edges.setdefault(edge[0], []) # source.append(edge[1]) - + # self.finalize_rank(self.start, 0) - + #normalization - self.normalize() + self.normalize() for edge in self.edge_wt: self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] - + def order_in_rank(self): """Finds optimized order of the nodes within their ranks using median heuristic - - @param start: starting node of the component + + @param start: starting node of the component """ - + self.make_chain() self.preprocess_order() self.order = {} max_rank = max(map(lambda x: x, self.levels.keys())) - + for i in range(max_rank+1): self.order[i] = 0 - + self.init_order(self.start, self.result[self.start]['x']) - + for level in self.levels: self.levels[level].sort(lambda x, y: cmp(self.result[x]['y'], self.result[y]['y'])) - - self.order_heuristic() + + self.order_heuristic() self.process_order() - + def process(self, starting_node): """Process the graph to find ranks and order of the nodes - - @param starting_node: node from where to start the graph search + + @param starting_node: node from where to start the graph search """ - + self.start_nodes = starting_node or [] - self.partial_order = {} - self.links = [] - self.tree_list = {} - + self.partial_order = {} + self.links = [] + self.tree_list = {} + if self.nodes: if self.start_nodes: #add dummy edges to the nodes which does not have any incoming edges tree = self.make_acyclic(None, self.start_nodes[0], 0, []) + for node in self.no_ancester: for sec_node in self.transitions.get(node, []): if sec_node in self.partial_order.keys(): self.transitions[self.start_nodes[0]].append(node) break - + self.partial_order = {} tree = self.make_acyclic(None, self.start_nodes[0], 0, []) - - - # if graph is disconnected or no start-node is given - #than to find starting_node for each component of the node + + + # if graph is disconnected or no start-node is given + #than to find starting_node for each component of the node if len(self.nodes) > len(self.partial_order): - self.find_starts() - - self.max_order = 0 + self.find_starts() + + self.max_order = 0 #for each component of the graph find ranks and order of the nodes - for s in self.start_nodes: + for s in self.start_nodes: self.start = s self.rank() # First step:Netwoek simplex algorithm - self.order_in_rank() #Second step: ordering nodes within ranks - + self.order_in_rank() #Second step: ordering nodes within ranks + def __str__(self): result = '' @@ -715,21 +716,21 @@ class graph(object): def scale(self, maxx, maxy, nwidth=0, nheight=0, margin=20): """Computes actual co-ordiantes of the nodes """ - + #for flat edges ie. source an destination nodes are on the same rank for src in self.transitions: for des in self.transitions[src]: - if (self.result[des]['x'] - self.result[src]['x'] == 0): + if (self.result[des]['x'] - self.result[src]['x'] == 0): self.result[src]['x'] += 0.08 self.result[des]['x'] -= 0.08 - + factorX = maxx + nheight - factorY = maxy + nwidth - + factorY = maxy + nwidth + for node in self.result: self.result[node]['y'] = (self.result[node]['y']) * factorX + margin self.result[node]['x'] = (self.result[node]['x']) * factorY + margin - + def result_get(self): return self.result @@ -762,14 +763,14 @@ if __name__=='__main__': import ImageDraw img = Image.new("RGB", (800, 600), "#ffffff") draw = ImageDraw.Draw(img) - + result = g.result_get() node_res = {} for node in nodes: node_res[node] = result[node] - + for name,node in node_res.items(): - + draw.arc( (int(node['y']-radius), int(node['x']-radius),int(node['y']+radius), int(node['x']+radius) ), 0, 360, (128,128,128)) draw.text( (int(node['y']), int(node['x'])), name, (128,128,128)) From abfe6add89abad9fd202e8aa034b96ad88ae3be2 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Tue, 2 Mar 2010 11:08:53 +0530 Subject: [PATCH 24/37] improved bzr revid: apa@tinyerp.com-20100302053853-laq7143efs4q4tbu --- bin/tools/graph.py | 494 ++++++++++++++++++++++----------------------- 1 file changed, 247 insertions(+), 247 deletions(-) diff --git a/bin/tools/graph.py b/bin/tools/graph.py index 7b0375504f2..05a1b8302f0 100644 --- a/bin/tools/graph.py +++ b/bin/tools/graph.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). # @@ -16,7 +16,7 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# along with this program. If not, see . # ############################################################################## @@ -26,80 +26,80 @@ import math class graph(object): def __init__(self, nodes, transitions, no_ancester=None): """Initailize graph's object - + @param nodes: list of ids of nodes in the graph @param transitions: list of edges in the graph in the form (source_node, destination_node) - @param no_ancester: list of nodes with no incoming edges + @param no_ancester: list of nodes with no incoming edges """ - + self.nodes = nodes or [] self.edges = transitions or [] self.no_ancester = no_ancester or {} trans = {} - + for t in transitions: trans.setdefault(t[0], []) trans[t[0]].append(t[1]) self.transitions = trans self.result = {} - - + + def init_rank(self): """Computes rank of the nodes of the graph by finding initial feasible tree """ self.edge_wt = {} for link in self.links: self.edge_wt[link] = self.result[link[1]]['x'] - self.result[link[0]]['x'] - + tot_node = self.partial_order.__len__() - #do until all the nodes in the component are searched + #do until all the nodes in the component are searched while self.tight_tree()self.edge_wt[edge]-1): slack = self.edge_wt[edge]-1 new_edge = edge - + if new_edge[0] not in self.reachable_nodes: delta = -(self.edge_wt[new_edge]-1) else: delta = self.edge_wt[new_edge]-1 - + for node in self.result: if node in self.reachable_nodes: self.result[node]['x'] += delta - + for edge in self.edge_wt: - self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] - - self.init_cutvalues() - - + self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] + + self.init_cutvalues() + + def tight_tree(self): self.reachable_nodes = [] self.tree_edges = [] - self.reachable_node(self.start) + self.reachable_node(self.start) return self.reachable_nodes.__len__() - - + + def reachable_node(self, node): - """Find the nodes of the graph which are only 1 rank apart from each other + """Find the nodes of the graph which are only 1 rank apart from each other """ - + if node not in self.reachable_nodes: self.reachable_nodes.append(node) for edge in self.edge_wt: @@ -109,27 +109,27 @@ class graph(object): if edge[1] not in self.reachable_nodes: self.reachable_nodes.append(edge[1]) self.reachable_node(edge[1]) - - + + def init_cutvalues(self): """Initailize cut values of edges of the feasible tree. - Edges with negative cut-values are removed from the tree to optimize rank assignment + Edges with negative cut-values are removed from the tree to optimize rank assignment """ self.cut_edges = {} self.head_nodes = [] i=0; - + for edge in self.tree_edges: self.head_nodes = [] rest_edges = [] rest_edges += self.tree_edges rest_edges.__delitem__(i) - self.head_component(self.start, rest_edges) + self.head_component(self.start, rest_edges) i+=1 positive = 0 negative = 0 for source_node in self.transitions: - if source_node in self.head_nodes: + if source_node in self.head_nodes: for dest_node in self.transitions[source_node]: if dest_node not in self.head_nodes: negative+=1 @@ -140,62 +140,62 @@ class graph(object): self.cut_edges[edge] = positive - negative - + def head_component(self, node, rest_edges): """Find nodes which are reachable from the starting node, after removing an edge """ if node not in self.head_nodes: self.head_nodes.append(node) - + for edge in rest_edges: - if edge[0]==node: + if edge[0]==node: self.head_component(edge[1],rest_edges) - + def process_ranking(self, node, level=0): """Computes initial feasible ranking after making graph acyclic with depth-first search """ - + if node not in self.result: self.result[node] = {'y': None, 'x':level, 'mark':0} else: if level > self.result[node]['x']: self.result[node]['x'] = level - + if self.result[node]['mark']==0: self.result[node]['mark'] = 1 for sec_end in self.transitions.get(node, []): self.process_ranking(sec_end, level+1) - - + + def make_acyclic(self, parent, node, level, tree): """Computes Partial-order of the nodes with depth-first search """ - + if node not in self.partial_order: self.partial_order[node] = {'level':level, 'mark':0} if parent: tree.append((parent, node)) - + if self.partial_order[node]['mark']==0: self.partial_order[node]['mark'] = 1 for sec_end in self.transitions.get(node, []): self.links.append((node, sec_end)) self.make_acyclic(node, sec_end, level+1, tree) - return tree + return tree + - def rev_edges(self, tree): - """reverse the direction of the edges whose source-node-partail_order> destination-node-partail_order - to make the graph acyclic + """reverse the direction of the edges whose source-node-partail_order> destination-node-partail_order + to make the graph acyclic """ Is_Cyclic = False - i=0 + i=0 for link in self.links: src = link[0] des = link[1] - edge_len = self.partial_order[des]['level'] - self.partial_order[src]['level'] + edge_len = self.partial_order[des]['level'] - self.partial_order[src]['level'] if edge_len < 0: self.links.__delitem__(i) self.links.insert(i, (des, src)) @@ -205,9 +205,9 @@ class graph(object): elif math.fabs(edge_len) > 1: Is_Cyclic = True i += 1 - + return Is_Cyclic - + def exchange(self, e, f): """Exchange edges to make feasible-tree optimized @param edge: edge with negative cut-value @@ -216,144 +216,144 @@ class graph(object): self.tree_edges.__delitem__(self.tree_edges.index(e)) self.tree_edges.append(f) self.init_cutvalues() - - + + def enter_edge(self, edge): - """Finds a new_edge with minimum slack value to replace an edge with negative cut-value - - @param edge: edge with negative cut-value + """Finds a new_edge with minimum slack value to replace an edge with negative cut-value + + @param edge: edge with negative cut-value """ - + self.head_nodes = [] rest_edges = [] rest_edges += self.tree_edges rest_edges.__delitem__(rest_edges.index(edge)) self.head_component(self.start, rest_edges) - + if self.head_nodes.__contains__(edge[1]): l = [] for node in self.result: if not self.head_nodes.__contains__(node): - l.append(node) + l.append(node) self.head_nodes = l - + slack = 100 new_edge = edge for source_node in self.transitions: - if source_node in self.head_nodes: + if source_node in self.head_nodes: for dest_node in self.transitions[source_node]: if dest_node not in self.head_nodes: if(slack>(self.edge_wt[edge]-1)): slack = self.edge_wt[edge]-1 new_edge = (source_node, dest_node) - - return new_edge - + + return new_edge + def leave_edge(self): - """Returns the edge with negative cut_value(if exists) + """Returns the edge with negative cut_value(if exists) """ if self.critical_edges: for edge in self.critical_edges: self.cut_edges[edge] = 0 - + for edge in self.cut_edges: if self.cut_edges[edge]<0: return edge - - return None - - + + return None + + def finalize_rank(self, node, level): self.result[node]['x'] = level for destination in self.optimal_edges.get(node, []): self.finalize_rank(destination, level+1) - - + + def normalize(self): """The ranks are normalized by setting the least rank to zero. """ - + least_rank = min(map(lambda x: x['x'], self.result.values())) - + if(least_rank!=0): for node in self.result: - self.result[node]['x']-=least_rank - - - def make_chain(self): - """Edges between nodes more than one rank apart are replaced by chains of unit + self.result[node]['x']-=least_rank + + + def make_chain(self): + """Edges between nodes more than one rank apart are replaced by chains of unit length edges between temporary nodes. """ - + for edge in self.edge_wt: if self.edge_wt[edge]>1: self.transitions[edge[0]].remove(edge[1]) start = self.result[edge[0]]['x'] end = self.result[edge[1]]['x'] - - for rank in range(start+1, end): - if not self.result.get((rank, 'temp'), False): + + for rank in range(start+1, end): + if not self.result.get((rank, 'temp'), False): self.result[(rank, 'temp')] = {'y': None, 'x': rank, 'mark': 0} - + for rank in range(start, end): - if start==rank: + if start==rank: self.transitions[edge[0]].append((rank+1, 'temp')) elif rank==end-1: self.transitions.setdefault((rank, 'temp'), []).append(edge[1]) else: - self.transitions.setdefault((rank, 'temp'), []).append((rank+1, 'temp')) - - + self.transitions.setdefault((rank, 'temp'), []).append((rank+1, 'temp')) + + def init_order(self, node, level): """Initialize orders the nodes in each rank with depth-first search - """ - if not self.result[node]['y']: + """ + if not self.result[node]['y']: self.result[node]['y'] = self.order[level] - self.order[level] = self.order[level]+1 - + self.order[level] = self.order[level]+1 + for sec_end in self.transitions.get(node, []): self.init_order(sec_end, self.result[sec_end]['x']) - - + + def order_heuristic(self): for i in range(12): self.wmedian() - - + + def wmedian(self): """Applies median heuristic to find optimzed order of the nodes with in their ranks """ - for level in self.levels: - + for level in self.levels: + node_median = [] - nodes = self.levels[level] - for node in nodes: + nodes = self.levels[level] + for node in nodes: node_median.append((node, self.median_value(node, level-1))) sort_list = sorted(node_median, key=operator.itemgetter(1)) new_list = [tuple[0] for tuple in sort_list] - + self.levels[level] = new_list order = 0 for node in nodes: self.result[node]['y'] = order - order +=1 + order +=1 def median_value(self, node, adj_rank): - """Returns median value of a vertex , defined as the median position of the adjacent vertices - - @param node: node to process - @param adj_rank: rank 1 less than the node's rank + """Returns median value of a vertex , defined as the median position of the adjacent vertices + + @param node: node to process + @param adj_rank: rank 1 less than the node's rank """ adj_nodes = self.adj_position(node, adj_rank) l = len(adj_nodes) m = l/2 - + if l==0: - return -1.0 + return -1.0 elif l%2 == 1: return adj_nodes[m]#median of the middle element elif l==2: @@ -361,106 +361,106 @@ class graph(object): else: left = adj_nodes[m-1] - adj_nodes[0] right = adj_nodes[l-1] - adj_nodes[m] - return ((adj_nodes[m-1]*right) + (adj_nodes[m]*left))/(left+right) - - + return ((adj_nodes[m-1]*right) + (adj_nodes[m]*left))/(left+right) + + def adj_position(self, node, adj_rank): """Returns list of the present positions of the nodes adjacent to node in the given adjacent rank. - - @param node: node to process - @param adj_rank: rank 1 less than the node's rank + + @param node: node to process + @param adj_rank: rank 1 less than the node's rank """ - - pre_level_nodes = self.levels.get(adj_rank, []) + + pre_level_nodes = self.levels.get(adj_rank, []) adj_nodes = [] - + if pre_level_nodes: for src in pre_level_nodes: if (self.transitions.get(src) and self.transitions[src].__contains__(node)): adj_nodes.append(self.result[src]['y']) - - return adj_nodes - - + + return adj_nodes + + def preprocess_order(self): levels = {} - + for r in self.partial_order: l = self.result[r]['x'] levels.setdefault(l,[]) levels[l].append(r) - + self.levels = levels - - - def graph_order(self): - """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component + + + def graph_order(self): + """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component """ - mid_pos = None + mid_pos = 0.0 max_level = max(map(lambda x: len(x), self.levels.values())) - + for level in self.levels: if level: no = len(self.levels[level]) - factor = (max_level - no) * 0.10 - list = self.levels[level] + factor = (max_level - no) * 0.10 + list = self.levels[level] list.reverse() - + if no%2==0: first_half = list[no/2:] - factor = -factor + factor = -factor else: first_half = list[no/2+1:] if max_level==1:#for the case when horizontal graph is there self.result[list[no/2]]['y'] = mid_pos + (self.result[list[no/2]]['x']%2 * 0.5) else: self.result[list[no/2]]['y'] = mid_pos + factor - - last_half = list[:no/2] - + + last_half = list[:no/2] + i=1 for node in first_half: self.result[node]['y'] = mid_pos - (i + factor) i += 1 - + i=1 for node in last_half: self.result[node]['y'] = mid_pos + (i + factor) i += 1 - else: + else: self.max_order += max_level+1 - mid_pos = self.result[self.start]['y'] - + mid_pos = self.result[self.start]['y'] + def tree_order(self, node, last=0): mid_pos = self.result[node]['y'] l = self.transitions.get(node, []) l.reverse() no = len(l) - + if no%2==0: - first_half = l[no/2:] - factor = 1 + first_half = l[no/2:] + factor = 1 else: first_half = l[no/2+1:] factor = 0 - - last_half = l[:no/2] - + + last_half = l[:no/2] + i=1 for child in first_half: self.result[child]['y'] = mid_pos - (i - (factor * 0.5)) i += 1 - + if self.transitions.get(child, False): if last: self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 last = self.tree_order(child, last) - + if no%2: mid_node = l[no/2] - self.result[mid_node]['y'] = mid_pos - + self.result[mid_node]['y'] = mid_pos + if self.transitions.get((mid_node), False): if last: self.result[mid_node]['y'] = last + len(self.transitions[mid_node])/2 + 1 @@ -469,51 +469,51 @@ class graph(object): if last: self.result[mid_node]['y'] = last + 1 self.result[node]['y'] = self.result[mid_node]['y'] - mid_pos = self.result[node]['y'] - - i=1 + mid_pos = self.result[node]['y'] + + i=1 last_child = None - for child in last_half: + for child in last_half: self.result[child]['y'] = mid_pos + (i - (factor * 0.5)) - last_child = child + last_child = child i += 1 if self.transitions.get(child, False): if last: - self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 + self.result[child]['y'] = last + len(self.transitions[child])/2 + 1 last = self.tree_order(child, last) - + if last_child: last = self.result[last_child]['y'] - - return last - - - def process_order(self): - """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component + + return last + + + def process_order(self): + """Finds actual-order of the nodes with respect to maximum number of nodes in a rank in component """ - + if self.Is_Cyclic: max_level = max(map(lambda x: len(x), self.levels.values())) - + if max_level%2: self.result[self.start]['y'] = (max_level+1)/2 + self.max_order + (self.max_order and 1) else: self.result[self.start]['y'] = (max_level)/2 + self.max_order + (self.max_order and 1) - + self.graph_order() - - else: - self.result[self.start]['y'] = 0 + + else: + self.result[self.start]['y'] = 0 self.tree_order(self.start, 0) min_order = math.fabs(min(map(lambda x: x['y'], self.result.values()))) - - index = self.start_nodes.index(self.start) + + index = self.start_nodes.index(self.start) same = False - + roots = [] - if index>0: + if index>0: for start in self.start_nodes[:index]: - same = True + same = True for edge in self.tree_list[start][1:]: if self.tree_list[self.start].__contains__(edge): continue @@ -522,46 +522,46 @@ class graph(object): break if same: roots.append(start) - - if roots: - min_order += self.max_order + + if roots: + min_order += self.max_order else: min_order += self.max_order + 1 - + for level in self.levels: for node in self.levels[level]: self.result[node]['y'] += min_order - + if roots: roots.append(self.start) one_level_el = self.tree_list[self.start][0][1] base = self.result[one_level_el]['y']# * 2 / (index + 2) - - + + no = len(roots) first_half = roots[:no/2] - + if no%2==0: last_half = roots[no/2:] else: last_half = roots[no/2+1:] - + factor = -math.floor(no/2) for start in first_half: - self.result[start]['y'] = base + factor + self.result[start]['y'] = base + factor factor += 1 - + if no%2: self.result[roots[no/2]]['y'] = base + factor factor +=1 - + for start in last_half: - self.result[start]['y'] = base + factor + self.result[start]['y'] = base + factor factor += 1 - + self.max_order = max(map(lambda x: x['y'], self.result.values())) - - def find_starts(self): + + def find_starts(self): """Finds other start nodes of the graph in the case when graph is disconneted """ rem_nodes = [] @@ -577,7 +577,7 @@ class graph(object): count = 0 new_start = rem_nodes[0] largest_tree = [] - + for node in rem_nodes: self.partial_order = {} tree = self.make_acyclic(None, node, 0, []) @@ -589,120 +589,120 @@ class graph(object): if not largest_tree: new_start = rem_nodes[0] rem_nodes.remove(new_start) - + self.start_nodes.append(new_start) - - + + for edge in largest_tree: if rem_nodes.__contains__(edge[0]): rem_nodes.remove(edge[0]) if rem_nodes.__contains__(edge[1]): rem_nodes.remove(edge[1]) - + if not rem_nodes: break - + def rank(self): """Finds the optimized rank of the nodes using Network-simplex algorithm - + @param start: starting node of the component """ - self.levels = {} + self.levels = {} self.critical_edges = [] self.partial_order = {} self.links = [] self.Is_Cyclic = False - + self.tree_list[self.start] = self.make_acyclic(None, self.start, 0, []) - self.Is_Cyclic = self.rev_edges(self.tree_list[self.start]) + self.Is_Cyclic = self.rev_edges(self.tree_list[self.start]) self.process_ranking(self.start) self.init_rank() - + #make cut values of all tree edges to 0 to optimize feasible tree - e = self.leave_edge() - + e = self.leave_edge() + while e : f = self.enter_edge(e) if e==f: self.critical_edges.append(e) else: - self.exchange(e,f) + self.exchange(e,f) e = self.leave_edge() - + #finalize rank using optimum feasible tree # self.optimal_edges = {} # for edge in self.tree_edges: # source = self.optimal_edges.setdefault(edge[0], []) # source.append(edge[1]) - + # self.finalize_rank(self.start, 0) - + #normalization - self.normalize() + self.normalize() for edge in self.edge_wt: self.edge_wt[edge] = self.result[edge[1]]['x'] - self.result[edge[0]]['x'] - + def order_in_rank(self): """Finds optimized order of the nodes within their ranks using median heuristic - - @param start: starting node of the component + + @param start: starting node of the component """ - + self.make_chain() self.preprocess_order() self.order = {} max_rank = max(map(lambda x: x, self.levels.keys())) - + for i in range(max_rank+1): self.order[i] = 0 - + self.init_order(self.start, self.result[self.start]['x']) - + for level in self.levels: self.levels[level].sort(lambda x, y: cmp(self.result[x]['y'], self.result[y]['y'])) - - self.order_heuristic() + + self.order_heuristic() self.process_order() - + def process(self, starting_node): """Process the graph to find ranks and order of the nodes - - @param starting_node: node from where to start the graph search + + @param starting_node: node from where to start the graph search """ - + self.start_nodes = starting_node or [] - self.partial_order = {} - self.links = [] - self.tree_list = {} - + self.partial_order = {} + self.links = [] + self.tree_list = {} + if self.nodes: if self.start_nodes: #add dummy edges to the nodes which does not have any incoming edges tree = self.make_acyclic(None, self.start_nodes[0], 0, []) - + for node in self.no_ancester: for sec_node in self.transitions.get(node, []): if sec_node in self.partial_order.keys(): self.transitions[self.start_nodes[0]].append(node) break - + self.partial_order = {} tree = self.make_acyclic(None, self.start_nodes[0], 0, []) - - - # if graph is disconnected or no start-node is given - #than to find starting_node for each component of the node + + + # if graph is disconnected or no start-node is given + #than to find starting_node for each component of the node if len(self.nodes) > len(self.partial_order): - self.find_starts() - - self.max_order = 0 + self.find_starts() + + self.max_order = 0 #for each component of the graph find ranks and order of the nodes - for s in self.start_nodes: + for s in self.start_nodes: self.start = s self.rank() # First step:Netwoek simplex algorithm - self.order_in_rank() #Second step: ordering nodes within ranks - + self.order_in_rank() #Second step: ordering nodes within ranks + def __str__(self): result = '' @@ -716,21 +716,21 @@ class graph(object): def scale(self, maxx, maxy, nwidth=0, nheight=0, margin=20): """Computes actual co-ordiantes of the nodes """ - + #for flat edges ie. source an destination nodes are on the same rank for src in self.transitions: for des in self.transitions[src]: - if (self.result[des]['x'] - self.result[src]['x'] == 0): + if (self.result[des]['x'] - self.result[src]['x'] == 0): self.result[src]['x'] += 0.08 self.result[des]['x'] -= 0.08 - + factorX = maxx + nheight - factorY = maxy + nwidth - + factorY = maxy + nwidth + for node in self.result: self.result[node]['y'] = (self.result[node]['y']) * factorX + margin self.result[node]['x'] = (self.result[node]['x']) * factorY + margin - + def result_get(self): return self.result @@ -763,14 +763,14 @@ if __name__=='__main__': import ImageDraw img = Image.new("RGB", (800, 600), "#ffffff") draw = ImageDraw.Draw(img) - + result = g.result_get() node_res = {} for node in nodes: node_res[node] = result[node] - + for name,node in node_res.items(): - + draw.arc( (int(node['y']-radius), int(node['x']-radius),int(node['y']+radius), int(node['x']+radius) ), 0, 360, (128,128,128)) draw.text( (int(node['y']), int(node['x'])), name, (128,128,128)) From b528a80036cc13b539af76e5ff23f195a4086213 Mon Sep 17 00:00:00 2001 From: "HDA (OpenERP)" Date: Fri, 12 Mar 2010 15:45:34 +0530 Subject: [PATCH 25/37] Fixed for directory not having __openerp__ or __terp__ files bzr revid: hda@tinyerp.com-20100312101534-gmpph29sc47wweo6 --- bin/addons/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index c5e73e7c709..13ff5133b31 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -32,6 +32,7 @@ import pooler import netsvc from osv import fields +import addons import zipfile import release @@ -302,8 +303,8 @@ def load_information_from_description_file(module): description_file = addons.get_module_resource(module, filename) if os.path.isfile(description_file): return eval(tools.file_open(description_file).read()) - - raise Exception('The module %s does not contain a description file: __openerp__.py or __terp__.py (deprecated)') + return +# raise Exception('The module %s does not contain a description file: __openerp__.py or __terp__.py (deprecated)') def get_modules_with_version(): modules = get_modules() From bb091a77b0c08964356629df19547de2c9203bd7 Mon Sep 17 00:00:00 2001 From: "Rga (Open ERP)" Date: Fri, 12 Mar 2010 17:22:58 +0530 Subject: [PATCH 26/37] [IMP]set default_focus attribute on buttons and fields bzr revid: rga@tinyerp.co.in-20100312115258-2txw1jm58nlog9gw --- bin/addons/base/rng/view.rng | 12 +++++++----- bin/osv/orm.py | 4 ++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index 4f99a03a8c1..69a4cc466d6 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -89,7 +89,7 @@ - + @@ -139,7 +139,7 @@ - + @@ -357,6 +357,7 @@ + @@ -429,7 +430,7 @@ - + @@ -442,9 +443,9 @@ - + - + @@ -469,6 +470,7 @@ + diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 7f412be0a7a..44f0bff3a14 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -1501,6 +1501,10 @@ class orm_template(object): 'action': resaction, 'relate': resrelate } + if result['type']=='form' and result['arch'].count("default_focus")>1: + msg = "Form View contain more than one default_focus attribute" + netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg) + raise except_orm('View Error !',msg) return result _view_look_dom_arch = __view_look_dom_arch From c0935c2513e3150d9cdbe3a07563db670562ffcb Mon Sep 17 00:00:00 2001 From: "nch@tinyerp.com" <> Date: Tue, 16 Mar 2010 10:56:09 +0530 Subject: [PATCH 27/37] [IMP]:added parent_field in attrs of O2M field and set limit=20 for O2M field bzr revid: nch@tinyerp.com-20100316052609-mp5q3o4859xej9a0 --- bin/osv/fields.py | 4 +++- bin/osv/orm.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/osv/fields.py b/bin/osv/fields.py index ce02de20a27..19735857680 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -81,6 +81,7 @@ class _column(object): self.select = select self.selectable = True self.group_operator = args.get('group_operator', False) + self.parent_field = args.get('parent_field', False) for a in args: if args[a]: setattr(self, a, args[a]) @@ -365,7 +366,8 @@ class one2many(_column): _prefetch = False _type = 'one2many' - def __init__(self, obj, fields_id, string='unknown', limit=None, **args): + def __init__(self, obj, fields_id, string='unknown', limit=20, **args): + args.update({'parent_field':fields_id}) _column.__init__(self, string=string, **args) self._obj = obj self._fields_id = fields_id diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 2bf846d631d..83934f8b1a2 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -1008,7 +1008,7 @@ class orm_template(object): continue res[f] = {'type': self._columns[f]._type} for arg in ('string', 'readonly', 'states', 'size', 'required', 'group_operator', - 'change_default', 'translate', 'help', 'select', 'selectable'): + 'change_default', 'translate', 'help', 'select', 'selectable','parent_field'): if getattr(self._columns[f], arg): res[f][arg] = getattr(self._columns[f], arg) if not read_access: From bf77cfe0059775dcbf8d073845ae5172ddddc093 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Tue, 16 Mar 2010 12:18:12 +0530 Subject: [PATCH 28/37] command line server management with -i and -u options bzr revid: rvo@tinyerp.co.in-20100316064812-5uurt3vhids9o57u --- bin/addons/base/module/module.py | 2 ++ bin/osv/orm.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py index e11b4964471..01ea8cc0745 100644 --- a/bin/addons/base/module/module.py +++ b/bin/addons/base/module/module.py @@ -237,6 +237,8 @@ class module(osv.osv): def button_upgrade(self, cr, uid, ids, context=None): depobj = self.pool.get('ir.module.module.dependency') todo = self.browse(cr, uid, ids, context=context) + self.update_list(cr, uid) + i = 0 while i Date: Tue, 16 Mar 2010 18:35:17 +0530 Subject: [PATCH 29/37] [IMP]:allowed default_get method to throw exception for osv_memory wizards bzr revid: rvo@tinyerp.co.in-20100316130517-z5ojbxincq62mkle --- bin/osv/orm.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 7d7afccf2b2..a7f6056c4c9 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -370,6 +370,10 @@ class orm_template(object): CONCURRENCY_CHECK_FIELD = '__last_update' + def view_init(self, cr , uid , fields_list, context=None): + """Override this method to do specific things when a view on the object is opened.""" + pass + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None): raise _('The read_group method is not implemented on this object !') @@ -1697,6 +1701,7 @@ class orm_memory(orm_template): return id_new def default_get(self, cr, uid, fields_list, context=None): + self.view_init(cr, uid, fields_list, context) if not context: context = {} value = {} From 122901dc7a068b1ca154b611fd45df45d9caefcc Mon Sep 17 00:00:00 2001 From: "Rga (Open ERP)" Date: Tue, 16 Mar 2010 19:44:08 +0530 Subject: [PATCH 30/37] [IMP] browse() on reference fields bzr revid: rga@tinyerp.co.in-20100316141408-7u2hg1nmgplpu0n5 --- bin/osv/orm.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index a7f6056c4c9..4871658b833 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -247,6 +247,15 @@ class browse_record(object): new_data[n] = browse_null() elif f._type in ('one2many', 'many2many') and len(data[n]): new_data[n] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(f._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context) + elif f._type in ('reference'): + if data[n]: + ref_obj, ref_id = data[n].split(',') + ref_id = long(ref_id) + obj = self._table.pool.get(ref_obj) + compids = False + new_data[n] = browse_record(self._cr, self._uid, ref_id, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) + else: + new_data[n] = browse_null() else: new_data[n] = data[n] self._data[data['id']].update(new_data) @@ -1481,7 +1490,7 @@ class orm_template(object): if context and context.get('active_id',False): data_menu = self.pool.get('ir.ui.menu').browse(cr, user, context['active_id'], context).action if data_menu: - act_id = int(data_menu.split(',')[1]) + act_id = data_menu.id if act_id: data_action = self.pool.get('ir.actions.act_window').browse(cr, user, [act_id], context)[0] result['submenu'] = getattr(data_action,'menus', False) From f318384603a8c179b5b1132381e24b5c5cb3cd0f Mon Sep 17 00:00:00 2001 From: Launchpad Code Hosting Date: Wed, 17 Mar 2010 04:39:54 +0000 Subject: [PATCH 31/37] Launchpad automatic translations update. bzr revid: codehost@crowberry-20100316044616-puyw52gzump8t07e bzr revid: codehost@crowberry-20100317043954-csvaufi5xr2jl4ii --- bin/addons/base/i18n/et.po | 8 +- bin/addons/base/i18n/hr.po | 4 +- bin/addons/base/i18n/ja.po | 4 +- bin/addons/base/i18n/ro.po | 167 ++++++++++++++++++---------------- bin/addons/base/i18n/sv.po | 4 +- bin/addons/base/i18n/uk.po | 4 +- bin/addons/base/i18n/zh_CN.po | 6 +- debian/po/nb.po | 10 +- 8 files changed, 109 insertions(+), 98 deletions(-) diff --git a/bin/addons/base/i18n/et.po b/bin/addons/base/i18n/et.po index 81ccf3ae5ed..d4461213af4 100644 --- a/bin/addons/base/i18n/et.po +++ b/bin/addons/base/i18n/et.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2009-12-15 06:29+0000\n" -"Last-Translator: Fabien (Open ERP) \n" +"PO-Revision-Date: 2010-03-16 05:12+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-01-14 04:45+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base @@ -46,7 +46,7 @@ msgstr "Vaata Ülesehitust" #: code:addons/base/ir/ir_model.py:0 #, python-format msgid "You can not create this kind of document! (%s)" -msgstr "" +msgstr "Sa ei saa luua seda tüüpi dokumenti! (%s)" #. module: base #: wizard_field:module.lang.import,init,code:0 diff --git a/bin/addons/base/i18n/hr.po b/bin/addons/base/i18n/hr.po index fe562db02a5..897cc4ed51c 100644 --- a/bin/addons/base/i18n/hr.po +++ b/bin/addons/base/i18n/hr.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-03-14 05:39+0000\n" +"PO-Revision-Date: 2010-03-16 05:10+0000\n" "Last-Translator: goranc \n" "Language-Team: openerp-translators\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-03-15 04:48+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" "Language: hr\n" diff --git a/bin/addons/base/i18n/ja.po b/bin/addons/base/i18n/ja.po index dc4d590b5ac..8721b79b2c4 100644 --- a/bin/addons/base/i18n/ja.po +++ b/bin/addons/base/i18n/ja.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-03-14 05:37+0000\n" +"PO-Revision-Date: 2010-03-16 05:09+0000\n" "Last-Translator: Harry (Open ERP) \n" "Language-Team: Japanese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-03-15 04:48+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/ro.po b/bin/addons/base/i18n/ro.po index cd94fb57e9f..f2d25122948 100644 --- a/bin/addons/base/i18n/ro.po +++ b/bin/addons/base/i18n/ro.po @@ -7,19 +7,19 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2009-12-21 06:09+0000\n" +"PO-Revision-Date: 2010-03-16 16:14+0000\n" "Last-Translator: Valentin Caragea \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-01-14 04:47+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base #: model:res.country,name:base.sh msgid "Saint Helena" -msgstr "Sfanta Elena" +msgstr "Sfânta Elena" #. module: base #: wizard_view:res.partner.sms_send,init:0 @@ -29,12 +29,12 @@ msgstr "SMS - Gateway: clickatell" #. module: base #: view:res.lang:0 msgid "%j - Day of the year as a decimal number [001,366]." -msgstr "%j - Ziua anului ca numar zecimal [001,366]" +msgstr "%j - Ziua anului ca numar zecimal [001,366]." #. module: base #: field:ir.values,meta_unpickle:0 msgid "Metadata" -msgstr "Metadata" +msgstr "Metadate" #. module: base #: field:ir.ui.view,arch:0 @@ -313,50 +313,50 @@ msgstr "Nume cimp" #: model:ir.actions.act_window,name:base.open_module_tree_uninstall #: model:ir.ui.menu,name:base.menu_module_tree_uninstall msgid "Uninstalled modules" -msgstr "" +msgstr "Module neinstalate" #. module: base #: selection:ir.actions.report.xml,report_type:0 msgid "txt" -msgstr "" +msgstr "txt" #. module: base #: wizard_view:server.action.create,init:0 #: wizard_field:server.action.create,init,type:0 msgid "Select Action Type" -msgstr "" +msgstr "Selectare tip acţiune" #. module: base #: selection:ir.actions.todo,type:0 msgid "Configure" -msgstr "" +msgstr "Configurare" #. module: base #: model:res.country,name:base.tv msgid "Tuvalu" -msgstr "" +msgstr "Tuvalu" #. module: base #: selection:ir.model,state:0 #: selection:ir.model.grid,state:0 msgid "Custom Object" -msgstr "" +msgstr "Obiect personalizat" #. module: base #: field:res.lang,date_format:0 msgid "Date Format" -msgstr "" +msgstr "Formatul Datei" #. module: base #: field:res.bank,email:0 #: field:res.partner.address,email:0 msgid "E-Mail" -msgstr "" +msgstr "E-mail" #. module: base #: model:res.country,name:base.an msgid "Netherlands Antilles" -msgstr "" +msgstr "Antilele Olandeze" #. module: base #: code:addons/base/res/res_user.py:0 @@ -365,21 +365,23 @@ msgid "" "You can not remove the admin user as it is used internally for resources " "created by OpenERP (updates, module installation, ...)" msgstr "" +"Nu puteti sterge utilizatorul 'admin' deoarece este utilizat intern pentru " +"resursele create de OpenERP (actualizari, instalare de module,...)" #. module: base #: model:res.country,name:base.gf msgid "French Guyana" -msgstr "" +msgstr "Guyana Franceza" #. module: base #: field:ir.ui.view.custom,ref_id:0 msgid "Original View" -msgstr "" +msgstr "Vedere Originala" #. module: base #: selection:module.lang.install,init,lang:0 msgid "Bosnian / bosanski jezik" -msgstr "" +msgstr "Bosniaca / bosanski jezik" #. module: base #: help:ir.actions.report.xml,attachment_use:0 @@ -387,21 +389,23 @@ msgid "" "If you check this, then the second time the user prints with same attachment " "name, it returns the previous report." msgstr "" +"Dacă bifaţi, atunci când utilizatorul va tipări pentru a doua oară cu " +"acelaşi nume de ataşament, va primi , de fapt, raportul anterior." #. module: base #: help:res.lang,iso_code:0 msgid "This ISO code is the name of po files to use for translations" -msgstr "" +msgstr "Codul ISO este numele fisierelor po folosite in traduceri" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_MEDIA_REWIND" -msgstr "" +msgstr "STOCK_MEDIA_REWIND" #. module: base #: field:ir.actions.todo,note:0 msgid "Text" -msgstr "" +msgstr "Text" #. module: base #: field:res.country,name:0 @@ -411,12 +415,12 @@ msgstr "Nume tara" #. module: base #: model:res.country,name:base.coreturn msgid "Colombia" -msgstr "" +msgstr "Columbia" #. module: base #: view:ir.module.module:0 msgid "Schedule Upgrade" -msgstr "" +msgstr "Planificare actualizare" #. module: base #: field:ir.actions.report.custom,report_id:0 @@ -429,133 +433,136 @@ msgid "" "The ISO country code in two chars.\n" "You can use this field for quick search." msgstr "" +"Codul ISO de tara din doua caractere.\n" +"Puteti utiliza acest camp pentru cautari rapide." #. module: base #: selection:workflow.activity,join_mode:0 #: selection:workflow.activity,split_mode:0 msgid "Xor" -msgstr "" +msgstr "Xor" #. module: base #: view:res.partner:0 msgid "Sales & Purchases" -msgstr "" +msgstr "Vânzări şi achiziţii" #. module: base #: view:ir.actions.wizard:0 #: field:wizard.ir.model.menu.create.line,wizard_id:0 msgid "Wizard" -msgstr "" +msgstr "Asistent" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_CUT" -msgstr "" +msgstr "STOCK_CUT" #. module: base #: model:ir.actions.act_window,name:base.ir_action_wizard #: view:ir.actions.wizard:0 #: model:ir.ui.menu,name:base.menu_ir_action_wizard msgid "Wizards" -msgstr "" +msgstr "Asistenti" #. module: base #: selection:res.config.view,view:0 msgid "Extended Interface" -msgstr "" +msgstr "Interfata Extinsa" #. module: base #: code:addons/base/ir/ir_model.py:0 #, python-format msgid "Custom fields must have a name that starts with 'x_' !" -msgstr "" +msgstr "Câmpurile client trebuie să aibă la inceputul numelui 'x_' !" #. module: base #: help:ir.actions.server,action_id:0 msgid "Select the Action Window, Report, Wizard to be executed." msgstr "" +"Selectati fereastra de actiune, raportul sau asistentul care va fi executat." #. module: base #: view:wizard.module.lang.export:0 msgid "Export done" -msgstr "" +msgstr "Export finalizat" #. module: base #: view:ir.model:0 msgid "Model Description" -msgstr "" +msgstr "Descriere Model" #. module: base #: field:workflow.transition,trigger_expr_id:0 msgid "Trigger Expression" -msgstr "" +msgstr "Expresie Declansator" #. module: base #: model:res.country,name:base.jo msgid "Jordan" -msgstr "" +msgstr "Iordania" #. module: base #: code:addons/base/ir/ir_model.py:0 #, python-format msgid "You can not remove the model '%s' !" -msgstr "" +msgstr "Nu puteti inlatura modelul '%s' !" #. module: base #: model:res.country,name:base.er msgid "Eritrea" -msgstr "" +msgstr "Eritreea" #. module: base #: view:res.config.view:0 msgid "Configure simple view" -msgstr "" +msgstr "Configurare view simplu" #. module: base #: selection:module.lang.install,init,lang:0 msgid "Bulgarian / български" -msgstr "" +msgstr "Bulgara / български" #. module: base #: model:ir.model,name:base.model_ir_actions_actions msgid "ir.actions.actions" -msgstr "" +msgstr "ir.actions.actions" #. module: base #: model:ir.actions.act_window,name:base.action_report_custom #: view:ir.report.custom:0 msgid "Custom Report" -msgstr "" +msgstr "Raport Personalizat" #. module: base #: selection:ir.report.custom,type:0 msgid "Bar Chart" -msgstr "" +msgstr "Grafic bare" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_DIALOG_ERROR" -msgstr "" +msgstr "STOCK_DIALOG_ERROR" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_INDEX" -msgstr "" +msgstr "STOCK_INDEX" #. module: base #: model:res.country,name:base.rs msgid "Serbia" -msgstr "" +msgstr "Serbia" #. module: base #: selection:ir.translation,type:0 msgid "Wizard View" -msgstr "" +msgstr "View asistent" #. module: base #: model:res.country,name:base.kh msgid "Cambodia, Kingdom of" -msgstr "" +msgstr "Cambodia, Regatul" #. module: base #: model:ir.actions.act_window,name:base.ir_sequence_form @@ -563,43 +570,43 @@ msgstr "" #: model:ir.ui.menu,name:base.menu_ir_sequence_form #: model:ir.ui.menu,name:base.next_id_5 msgid "Sequences" -msgstr "" +msgstr "Secvenţe" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_DIALOG_QUESTION" -msgstr "" +msgstr "STOCK_DIALOG_QUESTION" #. module: base #: model:res.country,name:base.pg msgid "Papua New Guinea" -msgstr "" +msgstr "Papua Noua Guinee" #. module: base #: model:res.partner.category,name:base.res_partner_category_4 msgid "Basic Partner" -msgstr "" +msgstr "Partener de bază" #. module: base #: rml:ir.module.reference:0 msgid "," -msgstr "" +msgstr "," #. module: base #: view:res.partner:0 msgid "My Partners" -msgstr "" +msgstr "Partenerii Mei" #. module: base #: model:res.country,name:base.es msgid "Spain" -msgstr "" +msgstr "Spania" #. module: base #: wizard_view:module.upgrade,end:0 #: wizard_view:module.upgrade,start:0 msgid "You may have to reinstall some language pack." -msgstr "" +msgstr "Ar putea fi nevoie sa reinstalati cateva pachete de traduceri" #. module: base #: field:res.partner.address,mobile:0 @@ -609,65 +616,67 @@ msgstr "GSM" #. module: base #: model:res.country,name:base.om msgid "Oman" -msgstr "" +msgstr "Oman" #. module: base #: model:ir.actions.act_window,name:base.action_payterm_form #: model:ir.model,name:base.model_res_payterm msgid "Payment term" -msgstr "" +msgstr "Termen de Plata" #. module: base #: model:res.country,name:base.nu msgid "Niue" -msgstr "" +msgstr "Niue" #. module: base #: selection:ir.cron,interval_type:0 msgid "Work Days" -msgstr "" +msgstr "Zile lucratoare" #. module: base #: help:ir.values,action_id:0 msgid "This field is not used, it only helps you to select the right action." msgstr "" +"Câmpul nu este utilizat, există doar pentru a vă ajuta să selectaţi acţiunea " +"potrivită." #. module: base #: model:ir.actions.act_window,name:base.act_menu_create #: view:wizard.ir.model.menu.create:0 msgid "Create Menu" -msgstr "" +msgstr "Creare Meniu" #. module: base #: model:res.country,name:base.in msgid "India" -msgstr "" +msgstr "India" #. module: base #: model:ir.model,name:base.model_maintenance_contract_module msgid "maintenance contract modules" -msgstr "" +msgstr "Module ale contractului de mentenanta" #. module: base #: view:ir.values:0 msgid "client_action_multi, client_action_relate" -msgstr "" +msgstr "client_action_multi, client_action_relate" #. module: base #: model:res.country,name:base.ad msgid "Andorra, Principality of" -msgstr "" +msgstr "Andorra, Principatul" #. module: base #: field:ir.module.category,child_ids:0 #: field:res.partner.category,child_ids:0 msgid "Child Categories" -msgstr "" +msgstr "Categorii subordonate" #. module: base #: selection:wizard.module.lang.export,format:0 msgid "TGZ Archive" -msgstr "" +msgstr "Arhivă TGZ" #. module: base #: field:res.partner.som,factor:0 @@ -677,7 +686,7 @@ msgstr "Factor(Postas)" #. module: base #: view:res.lang:0 msgid "%B - Full month name." -msgstr "" +msgstr "%B - Denumirea completa a lunii." #. module: base #: field:ir.actions.report.xml,report_type:0 @@ -687,59 +696,59 @@ msgstr "" #: field:ir.values,key:0 #: view:res.partner:0 msgid "Type" -msgstr "" +msgstr "Tip" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_FILE" -msgstr "" +msgstr "STOCK_FILE" #. module: base #: model:res.country,name:base.gu msgid "Guam (USA)" -msgstr "" +msgstr "Guam (SUA)" #. module: base #: model:ir.model,name:base.model_ir_model_grid msgid "Objects Security Grid" -msgstr "" +msgstr "Grila de Securitate a Obiectelor" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_GO_DOWN" -msgstr "" +msgstr "STOCK_GO_DOWN" #. module: base #: selection:ir.ui.menu,icon:0 msgid "STOCK_OK" -msgstr "" +msgstr "STOCK_OK" #. module: base #: selection:ir.actions.server,state:0 #: selection:workflow.activity,kind:0 msgid "Dummy" -msgstr "" +msgstr "Dummy" #. module: base #: constraint:ir.ui.view:0 msgid "Invalid XML for View Architecture!" -msgstr "" +msgstr "Fișierul XML pentru arhitectura planului vizual nu este valid!" #. module: base #: model:res.country,name:base.ky msgid "Cayman Islands" -msgstr "" +msgstr "Insulele Cayman" #. module: base #: model:res.country,name:base.ir msgid "Iran" -msgstr "" +msgstr "Iran" #. module: base #: model:ir.actions.act_window,name:base.res_request-act #: model:ir.ui.menu,name:base.menu_res_request_act msgid "My Requests" -msgstr "" +msgstr "Cererile mele" #. module: base #: field:ir.sequence,name:0 @@ -750,17 +759,17 @@ msgstr "Nume numerotare" #. module: base #: model:res.country,name:base.td msgid "Chad" -msgstr "" +msgstr "Ciad" #. module: base #: selection:module.lang.install,init,lang:0 msgid "Spanish (AR) / Español (AR)" -msgstr "" +msgstr "Spaniola (AR) / Español (AR)" #. module: base #: model:res.country,name:base.ug msgid "Uganda" -msgstr "" +msgstr "Uganda" #. module: base #: model:res.country,name:base.ne diff --git a/bin/addons/base/i18n/sv.po b/bin/addons/base/i18n/sv.po index b72083a500a..d2b3d972b83 100644 --- a/bin/addons/base/i18n/sv.po +++ b/bin/addons/base/i18n/sv.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-03-14 05:41+0000\n" +"PO-Revision-Date: 2010-03-16 05:11+0000\n" "Last-Translator: Anders Wallenquist \n" "Language-Team: <>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-03-15 04:48+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/uk.po b/bin/addons/base/i18n/uk.po index b4e60aa2480..10a8a3edf9f 100644 --- a/bin/addons/base/i18n/uk.po +++ b/bin/addons/base/i18n/uk.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-03-14 05:41+0000\n" +"PO-Revision-Date: 2010-03-16 05:11+0000\n" "Last-Translator: Fabien (Open ERP) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-03-15 04:48+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/zh_CN.po b/bin/addons/base/i18n/zh_CN.po index a1012d925bd..8e6c890c2d9 100644 --- a/bin/addons/base/i18n/zh_CN.po +++ b/bin/addons/base/i18n/zh_CN.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-03-15 03:22+0000\n" -"Last-Translator: mrshelly \n" +"PO-Revision-Date: 2010-03-16 05:12+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-03-15 04:48+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/debian/po/nb.po b/debian/po/nb.po index 05f376bee05..decd2ada0fd 100644 --- a/debian/po/nb.po +++ b/debian/po/nb.po @@ -8,20 +8,20 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-08-24 22:41+0300\n" -"PO-Revision-Date: 2010-03-10 14:03+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2010-03-16 05:12+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: Norwegian Bokmal \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-03-12 04:51+0000\n" +"X-Launchpad-Export-Date: 2010-03-17 04:39+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. Type: string #. Description #: ../openerp-server.templates:1001 msgid "Dedicated system account for the Open ERP server:" -msgstr "" +msgstr "Dedikert systemkonto for Open ERP:" #. Type: string #. Description @@ -31,6 +31,8 @@ msgid "" "the system's security is not compromised by running it with superuser " "privileges." msgstr "" +"Open ERP må av hensyn til sikkerhet kjøre som en egen bruker i stedet for " +"systemets superbruker." #. Type: string #. Description From 4f77a10054811e1f33915fb5864b36863e98192f Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 17 Mar 2010 07:21:16 +0100 Subject: [PATCH 32/37] merge bzr revid: fp@tinyerp.com-20100317062116-odz1ombttmkgb68x --- addons/crm/crm.py | 1 + addons/crm/crm_view.xml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 282584b8c6f..f05b7193cc7 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -156,6 +156,7 @@ class crm_case_stage(osv.osv): 'object_id': fields.many2one('ir.model','Object Name'), 'probability': fields.float('Probability (%)', required=True), 'on_change': fields.boolean('Change Probability Automatically',help="Change Probability on next and previous stages."), + 'requirements': fields.text('Requirements') } def _find_object_id(self, cr, uid, context=None): object_id = context and context.get('object_id', False) or False diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index 7ac6e119c27..fddd3bf2598 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -71,13 +71,15 @@ form
+ - + + From d9207750504a92be2a44f73c4bb668ffe235cdf0 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Wed, 17 Mar 2010 15:22:54 +0530 Subject: [PATCH 33/37] [FIX] crm: crash send new email wizard lp bug: https://launchpad.net/bugs/539207 fixed bzr revid: hmo@tinyerp.com-20100317095254-glwv4kwumpfc0834 --- addons/crm/crm.py | 2 +- addons/crm/wizard/wizard_crm_new_send_email.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index f05b7193cc7..ebaa437ff41 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -357,7 +357,7 @@ class crm_case(osv.osv): 'section_id': case.section_id.id } obj = self.pool.get('crm.case.log') - if history and case.description: + if history: obj = self.pool.get('crm.case.history') data['description'] = details or case.description data['email'] = email or \ diff --git a/addons/crm/wizard/wizard_crm_new_send_email.py b/addons/crm/wizard/wizard_crm_new_send_email.py index b9a43546132..ef878f3249f 100644 --- a/addons/crm/wizard/wizard_crm_new_send_email.py +++ b/addons/crm/wizard/wizard_crm_new_send_email.py @@ -72,11 +72,7 @@ def _mass_mail_send(self, cr, uid, data, context): pool = pooler.get_pool(cr.dbname) case_pool=pool.get(data.get('model')) - case = case_pool.browse(cr,uid,data['ids'])[0] - case_pool.write(cr, uid, [case.id], { - #'som': False, - 'canal_id': False, - }) + case = case_pool.browse(cr,uid,data['ids'])[0] emails = [data['form']['to']] + (data['form']['cc'] or '').split(',') emails = filter(None, emails) body = data['form']['text'] From 397b5a36d837f44b7e12765cb2dfc1e1b608d72e Mon Sep 17 00:00:00 2001 From: "RGA(OpenERP)" <> Date: Wed, 17 Mar 2010 19:41:30 +0530 Subject: [PATCH 34/37] Improved related changes for browse support in reference field bzr revid: hda@tinyerp.com-20100317141130-08v21img7a8tu9qx --- addons/base_calendar/base_calendar.py | 22 +++++----------------- addons/crm/crm_meeting.py | 7 ++----- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 4d0f85376f3..62ffc7c39d8 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -325,26 +325,17 @@ class calendar_attendee(osv.osv): result[id][name] = ', '.join(fromdata) if name == 'event_date': if attdata.ref: - model, res_id = tuple(attdata.ref.split(',')) - model_obj = self.pool.get(model) - obj = model_obj.read(cr, uid, res_id, ['date'])[0] - result[id][name] = obj.get('date') + result[id][name] = attdata.ref.date else: result[id][name] = False if name == 'event_end_date': if attdata.ref: - model, res_id = tuple(attdata.ref.split(',')) - model_obj = self.pool.get(model) - obj = model_obj.read(cr, uid, res_id, ['date_deadline'])[0] - result[id][name] = obj.get('date_deadline') + result[id][name] = attdata.ref.date_deadline else: result[id][name] = False if name == 'sent_by_uid': if attdata.ref: - model, res_id = tuple(attdata.ref.split(',')) - model_obj = self.pool.get(model) - obj = model_obj.read(cr, uid, res_id, ['user_id'])[0] - result[id][name] = obj.get('user_id') + result[id][name] = (attdata.ref.user_id.id,attdata.ref.user_id.name) else: result[id][name] = uid if name == 'language': @@ -441,8 +432,7 @@ request was delegated to"), for att in self.browse(cr, uid, ids, context=context): sign = att.sent_by_uid and att.sent_by_uid.signature or '' sign = '
'.join(sign and sign.split('\n') or []) - model, res_id = tuple(att.ref.split(',')) - res_obj = self.pool.get(model).browse(cr, uid, res_id) + res_obj = att.ref if res_obj and len(res_obj): res_obj = res_obj[0] sub = '[%s Invitation][%d] %s' % (company, att.id, res_obj.name) @@ -490,9 +480,7 @@ request was delegated to"), if user: ref = vals.get('ref', None) if ref: - model, event = ref.split(',') - model_obj = self.pool.get(model) - event_ref = model_obj.browse(cr, uid, event, context=context)[0] + event_ref = ref if event_ref.user_id.id != user[0]: defaults = {'user_id': user[0]} new_event = model_obj.copy(cr, uid, event, default=defaults, context=context) diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py index ce818fa0189..de0f1f01c33 100644 --- a/addons/crm/crm_meeting.py +++ b/addons/crm/crm_meeting.py @@ -74,17 +74,14 @@ class calendar_attendee(osv.osv): result[id] = {} if name == 'categ_id': if attdata.ref: - model, res_id = tuple(attdata.ref.split(',')) - model_obj = self.pool.get(model) - obj = model_obj.read(cr, uid, res_id, ['categ_id'])[0] - result[id][name] = obj.get('categ_id') + result[id][name] = (attdata.ref.categ_id.id,attdata.ref.categ_id.name,) else: result[id][name] = False return result _columns = { 'categ_id': fields.function(_compute_data, method=True, \ string='Event Type', type="many2one", \ - relation="crm.case.categ", multi='categ_id'), + relation="crm.case.categ", multi='categ_id'), } calendar_attendee() From 38b78087090aaf735ebc139fc4f79588d3bd68b7 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Wed, 17 Mar 2010 15:24:01 +0100 Subject: [PATCH 35/37] [IMP] base_calendar: Change the layout of the alarm [IMP] crm: Change the layout [IMP] product: Change the layout [IMP] sale: Change the layout bzr revid: stephane@openerp.com-20100317142401-pobas1gc9qypd50x --- addons/base_calendar/base_calendar_view.xml | 2 +- addons/crm/crm_claims_view.xml | 2 +- addons/crm/crm_fund_view.xml | 26 ++++++++++----------- addons/crm/crm_helpdesk_view.xml | 3 ++- addons/crm/crm_opportunity_view.xml | 14 +++++------ addons/crm/crm_view.xml | 2 +- addons/product/pricelist_view.xml | 1 + addons/product/product_view.xml | 4 ++-- addons/sale/report/sale_report_view.xml | 4 ++-- addons/sale/sale_view.xml | 19 ++++++++------- 10 files changed, 40 insertions(+), 37 deletions(-) diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml index d6430ca98a2..f1e59583cc1 100644 --- a/addons/base_calendar/base_calendar_view.xml +++ b/addons/base_calendar/base_calendar_view.xml @@ -159,8 +159,8 @@ - + diff --git a/addons/crm/crm_claims_view.xml b/addons/crm/crm_claims_view.xml index 5a4c6a3f6ad..f51f6890d0c 100644 --- a/addons/crm/crm_claims_view.xml +++ b/addons/crm/crm_claims_view.xml @@ -45,7 +45,7 @@ crm.claim tree - + diff --git a/addons/crm/crm_fund_view.xml b/addons/crm/crm_fund_view.xml index 75aae19a4af..a78d5e3eeb6 100644 --- a/addons/crm/crm_fund_view.xml +++ b/addons/crm/crm_fund_view.xml @@ -202,24 +202,24 @@ - - - - - + default="1" domain="[('user_id','=',uid)]" + separator="1" + help="Funds Related to Current User" + /> + + + + + - + - - + + /> diff --git a/addons/crm/crm_helpdesk_view.xml b/addons/crm/crm_helpdesk_view.xml index 68a47fef9f8..ec662885a04 100644 --- a/addons/crm/crm_helpdesk_view.xml +++ b/addons/crm/crm_helpdesk_view.xml @@ -122,7 +122,8 @@ crm.helpdesk tree - + diff --git a/addons/crm/crm_opportunity_view.xml b/addons/crm/crm_opportunity_view.xml index 4bf7a0b858d..4ce8c647c13 100644 --- a/addons/crm/crm_opportunity_view.xml +++ b/addons/crm/crm_opportunity_view.xml @@ -215,24 +215,24 @@ /> + - - - - + + + diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index fddd3bf2598..908cdd6fa91 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -634,7 +634,7 @@ - + diff --git a/addons/product/pricelist_view.xml b/addons/product/pricelist_view.xml index 8045cc6cf22..1dbf110cd64 100644 --- a/addons/product/pricelist_view.xml +++ b/addons/product/pricelist_view.xml @@ -104,6 +104,7 @@ + diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index e6c5588f4f7..ab0d2118bd7 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -344,8 +344,8 @@ form
- - + + diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index 3592cda4662..2e36b7d9923 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -62,13 +62,13 @@ default="1" domain="[('state','<>','draft'),('state','<>','cancel')]"/> - + diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index b3cec7496e4..4b9e5df8f8c 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -30,9 +30,9 @@ + - @@ -80,6 +80,7 @@ + @@ -428,15 +429,15 @@ search - - - - + + + + From 062fc996a4c54f4bef3f3bb3d2fbf1e3652bbcfc Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Wed, 17 Mar 2010 15:29:51 +0100 Subject: [PATCH 36/37] [FIX] Add some missing views and change the layouts of some views bzr revid: stephane@openerp.com-20100317142951-8olx5ehlhibgl34c --- bin/addons/base/res/partner/crm_view.xml | 11 +++++++++++ bin/addons/base/res/partner/partner_view.xml | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/res/partner/crm_view.xml b/bin/addons/base/res/partner/crm_view.xml index da4e08c7644..3a0e25b8898 100644 --- a/bin/addons/base/res/partner/crm_view.xml +++ b/bin/addons/base/res/partner/crm_view.xml @@ -2,6 +2,17 @@ + + res.partner.canal.tree + res.partner.canal + tree + + + + + + + res.partner.canal.form res.partner.canal diff --git a/bin/addons/base/res/partner/partner_view.xml b/bin/addons/base/res/partner/partner_view.xml index 938ffc83d12..b638742fd41 100644 --- a/bin/addons/base/res/partner/partner_view.xml +++ b/bin/addons/base/res/partner/partner_view.xml @@ -182,6 +182,17 @@ Partner Titles ======================= --> + + res.partner.title.tree + res.partner.title + tree + + + + + + + res.partner.title.form res.partner.title @@ -340,8 +351,8 @@ default="context.get('default_supplier',False)"/> - + @@ -555,6 +566,7 @@ + From e328055359259bb9ea676dec3d7e0c2155ebe061 Mon Sep 17 00:00:00 2001 From: "qdp-launchpad@tinyerp.com" <> Date: Wed, 17 Mar 2010 18:09:26 +0100 Subject: [PATCH 37/37] [IMP] hr_holidays: improvements bzr revid: qdp-launchpad@tinyerp.com-20100317170926-zyw5frdscx3kpi1p --- addons/hr_holidays/__terp__.py | 3 +- addons/hr_holidays/hr.py | 131 ++++++------------ addons/hr_holidays/hr_holidays_report.xml | 71 ++++++++++ addons/hr_holidays/hr_holidays_wizard.xml | 7 +- addons/hr_holidays/hr_view.xml | 99 +++++-------- addons/hr_holidays/report/__init__.py | 6 +- .../hr_holidays/report/available_holidays.py | 33 +++++ .../report/holidays_summary_report.py | 1 - .../hr_holidays/security/ir.model.access.csv | 8 +- 9 files changed, 187 insertions(+), 172 deletions(-) create mode 100644 addons/hr_holidays/report/available_holidays.py diff --git a/addons/hr_holidays/__terp__.py b/addons/hr_holidays/__terp__.py index f44fe590015..26dc78675cc 100644 --- a/addons/hr_holidays/__terp__.py +++ b/addons/hr_holidays/__terp__.py @@ -53,9 +53,8 @@ 'security/ir.model.access.csv', 'hr_workflow.xml', 'hr_view.xml', - 'hr_holidays_report.xml', 'hr_holidays_wizard.xml', - 'report/hr_holidays_report_view.xml', + 'hr_holidays_report.xml', #'process/hr_holidays_process.xml' ], 'demo_xml': [], diff --git a/addons/hr_holidays/hr.py b/addons/hr_holidays/hr.py index b6c0313e13f..12cb02f659f 100755 --- a/addons/hr_holidays/hr.py +++ b/addons/hr_holidays/hr.py @@ -37,17 +37,13 @@ class hr_holidays_status(osv.osv): for record in self.browse(cr, uid, ids, context): res[record.id] = {} max_leaves = leaves_taken = 0 - obj_ids = self.pool.get('hr.holidays.per.user').search(cr, uid, [('user_id','=',uid),('employee_id','=',employee_id),('holiday_status','=',record.id)]) - if obj_ids: - br_ob=self.pool.get('hr.holidays.per.user').browse(cr, uid, obj_ids)[0] - max_leaves=br_ob.max_leaves if not return_false: cr.execute("""SELECT type, sum(number_of_days) FROM hr_holidays WHERE employee_id = %s AND state='validate' AND holiday_status_id = %s GROUP BY type""", (str(employee_id), str(record.id))) for line in cr.fetchall(): if line[0] =='remove': - leaves_taken = -line[1] + leaves_taken = -line[1] if line[0] =='add': - max_leaves = line[1] + max_leaves = line[1] res[record.id]['max_leaves'] = max_leaves res[record.id]['leaves_taken'] = leaves_taken res[record.id]['remaining_leaves'] = max_leaves - leaves_taken @@ -66,18 +62,19 @@ class hr_holidays_status(osv.osv): employee_id = employee_ids[0] else: return_false = True - res = self.get_days(cr, uid, ids, employee_id, return_false, context=context) + if employee_id: + res = self.get_days(cr, uid, ids, employee_id, return_false, context=context) return res _columns = { 'name' : fields.char('Name', size=64, required=True, translate=True), - 'section_id': fields.many2one('crm.case.section', 'CRM Section', help='If you link this type of leave with a section in the CRM, it will synchronize each leave asked with a case in this section, to display it in the company shared calendar for example.'), + 'categ_id': fields.many2one('crm.case.categ', 'Meeting Category', domain="[('object_id.model', '=', 'crm.meeting')]", help='If you link this type of leave with a category in the CRM, it will synchronize each leave asked with a case in this category, to display it in the company shared calendar for example.'), 'color_name' : fields.selection([('red', 'Red'), ('lightgreen', 'Light Green'), ('lightblue','Light Blue'), ('lightyellow', 'Light Yellow'), ('magenta', 'Magenta'),('lightcyan', 'Light Cyan'),('black', 'Black'),('lightpink', 'Light Pink'),('brown', 'Brown'),('violet', 'Violet'),('lightcoral', 'Light Coral'),('lightsalmon', 'Light Salmon'),('lavender', 'Lavender'),('wheat', 'Wheat'),('ivory', 'Ivory')],'Color of the status', required=True, help='This color will be used in the leaves summary located in Reporting\Print Summary of Leaves'), 'limit' : fields.boolean('Allow to override Limit', help='If you thick this checkbox, the system will allow, for this section, the employees to take more leaves than the available ones.'), - 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the leave type without removing it."), + 'active' : fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the leave type without removing it."), 'max_leaves' : fields.function(_user_left_days, method=True, string='Maximum Leaves Allowed', help='This value is given by the sum of all holidays requests with a positive value.', multi='user_left_days'), 'leaves_taken' : fields.function(_user_left_days, method=True, string='Leaves Already Taken', help='This value is given by the sum of all holidays requests with a negative value.', multi='user_left_days'), - 'remaining_leaves' : fields.function(_user_left_days, method=True, string='Remaining Leaves', multi='user_left_days'), + 'remaining_leaves' : fields.function(_user_left_days, method=True, string='Remaining Leaves', help='Maximum Leaves Allowed - Leaves Already Taken', multi='user_left_days'), } _defaults = { @@ -86,48 +83,6 @@ class hr_holidays_status(osv.osv): } hr_holidays_status() -class hr_holidays_per_user(osv.osv): - _name = "hr.holidays.per.user" - _description = "Holidays Per User" - _rec_name = "user_id" - - def _get_remaining_leaves(self, cr, uid, ids, field_name, arg=None, context={}): - obj_holiday = self.pool.get('hr.holidays') - result = {} - for holiday_user in self.browse(cr, uid, ids): - days = 0.0 - ids_request = obj_holiday.search(cr, uid, [('employee_id', '=', holiday_user.employee_id.id),('state', '=', 'validate'),('holiday_status_id', '=', holiday_user.holiday_status.id)]) - if ids_request: - holidays = obj_holiday.browse(cr, uid, ids_request) - for holiday in holidays: - days -= holiday.number_of_days - days = holiday_user.max_leaves - days - result[holiday_user.id] = days - return result - - _columns = { - 'employee_id': fields.many2one('hr.employee', "Employee's Name",required=True), - 'user_id' : fields.many2one('res.users','User'), - 'holiday_status' : fields.many2one("hr.holidays.status", "Holiday's Status", required=True), - 'max_leaves' : fields.float('Maximum Leaves Allowed',required=True), - 'leaves_taken' : fields.float('Leaves Already Taken',readonly=True), - 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the holidays per user without removing it."), - 'notes' : fields.text('Notes'), - 'remaining_leaves': fields.function(_get_remaining_leaves, method=True, string='Remaining Leaves', type='float'), - 'holiday_ids': fields.one2many('hr.holidays', 'holiday_user_id', 'Holidays') - } - _defaults = { - 'active' : lambda *a: True, - } - - def create(self, cr, uid, vals, *args, **kwargs): - if vals['employee_id']: - obj_emp=self.pool.get('hr.employee').browse(cr,uid,vals['employee_id']) - vals.update({'user_id': obj_emp.user_id.id}) - return super(osv.osv,self).create(cr, uid, vals, *args, **kwargs) - -hr_holidays_per_user() - class hr_holidays(osv.osv): _name = "hr.holidays" _description = "Holidays" @@ -152,13 +107,12 @@ class hr_holidays(osv.osv): 'notes' : fields.text('Notes',readonly=True, states={'draft':[('readonly',False)]}), 'number_of_days': fields.float('Number of Days', readonly=True, states={'draft':[('readonly',False)]}), 'number_of_days_temp': fields.float('Number of Days', readonly=True, states={'draft':[('readonly',False)]}), - 'case_id': fields.many2one('crm.case', 'Case'), + 'case_id': fields.many2one('crm.meeting', 'Case'), 'type': fields.selection([('remove','Leave Request'),('add','Allocation Request')], 'Request Type', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="Choose 'Leave Request' if someone wants to take an off-day. \nChoose 'Allocation Request' if you want to increase the number of leaves available for someone"), 'allocation_type': fields.selection([('employee','Employee Request'),('company','Company Allocation')], 'Allocation Type', required=True, readonly=True, states={'draft':[('readonly',False)]}, help='This field is only for informative purposes, to depict if the leave request/allocation comes from an employee or from the company'), 'parent_id': fields.many2one('hr.holidays', 'Parent'), 'linked_request_ids': fields.one2many('hr.holidays', 'parent_id', 'Linked Requests',), - 'holiday_user_id' : fields.many2one('hr.holidays.per.user', 'User'), - 'department_id':fields.many2one('hr.department','Department'), + 'department_id':fields.many2one('hr.department','Department', readonly=True, states={'draft':[('readonly',False)]} ), } _defaults = { @@ -170,6 +124,14 @@ class hr_holidays(osv.osv): } _order = 'date_from desc' + def create(self, cr, uid, vals, context={}): + if context: + if context.has_key('type'): + vals['type'] = context['type'] + if context.has_key('allocation_type'): + vals['allocation_type'] = context['allocation_type'] + return super(osv.osv,self).create(cr, uid, vals, context) + def onchange_date_from(self, cr, uid, ids, date_to, date_from): result = {} if date_to and date_from: @@ -188,16 +150,14 @@ class hr_holidays(osv.osv): def _update_user_holidays(self, cr, uid, ids): for record in self.browse(cr, uid, ids): if record.state=='validate': - holiday_id=self.pool.get('hr.holidays.per.user').search(cr, uid, [('employee_id','=', record.employee_id.id),('holiday_status','=',record.holiday_status_id.id)]) - if holiday_id: - obj_holidays_per_user=self.pool.get('hr.holidays.per.user').browse(cr, uid,holiday_id[0]) - self.pool.get('hr.holidays.per.user').write(cr,uid,obj_holidays_per_user.id,{'leaves_taken':obj_holidays_per_user.leaves_taken - record.number_of_days}) if record.case_id: - if record.case_id.state <> 'draft': - raise osv.except_osv(_('Warning !'), - _('You can not cancel this holiday request. first You have to make its case in draft state.')) - else: - self.pool.get('crm.case').unlink(cr,uid,[record.case_id.id]) + self.pool.get('crm.meeting').unlink(cr,uid,[record.case_id.id]) + if record.linked_request_ids: + list_ids = [] + for id in record.linked_request_ids: + list_ids.append(id.id) + self.holidays_cancel(cr,uid,list_ids) + self.unlink(cr,uid,list_ids) def _check_date(self, cr, uid, ids): if ids: @@ -209,26 +169,11 @@ class hr_holidays(osv.osv): _constraints = [(_check_date, 'Start date should not be larger than end date! ', ['number_of_days'])] - def create(self, cr, uid, vals, *args, **kwargs): - id_holiday = super(hr_holidays, self).create(cr, uid, vals, *args, **kwargs) - self._create_holiday(cr, uid, [id_holiday]) - return id_holiday def unlink(self, cr, uid, ids, context={}): self._update_user_holidays(cr, uid, ids) return super(hr_holidays, self).unlink(cr, uid, ids, context) - def _create_holiday(self, cr, uid, ids): - holidays_user_obj = self.pool.get('hr.holidays.per.user') - holidays_data = self.browse(cr, uid, ids[0]) - list_holiday = [] - ids_user_hdays = holidays_user_obj.search(cr, uid, [('employee_id', '=', holidays_data.employee_id.id),('holiday_status', '=', holidays_data.holiday_status_id.id)]) - for hdays in holidays_user_obj.browse(cr, uid, ids_user_hdays): - for req in hdays.holiday_ids: - list_holiday.append(req.id) - list_holiday.append(ids[0]) - holidays_user_obj.write(cr, uid, ids_user_hdays, {'holiday_ids': [(6, 0, list_holiday)]}) - return True def onchange_date_to(self, cr, uid, ids, date_from, date_to): result = {} @@ -249,7 +194,7 @@ class hr_holidays(osv.osv): warning = {} if status: brows_obj = self.pool.get('hr.holidays.status').browse(cr, uid, [status])[0] - if brows_obj.section_id and not brows_obj.section_id.allow_unlink: + if brows_obj.categ_id and brows_obj.categ_id.section_id and not brows_obj.categ_id.section_id.allow_unlink: warning = { 'title': "Warning for ", 'message': "You won\'t be able to cancel this leave request because the CRM Section of the leave type disallows." @@ -283,6 +228,7 @@ class hr_holidays(osv.osv): def holidays_confirm(self, cr, uid, ids, *args): for record in self.browse(cr, uid, ids): + user_id = False leave_asked = record.number_of_days_temp if record.type == 'remove': if record.employee_id and not record.holiday_status_id.limit: @@ -300,14 +246,14 @@ class hr_holidays(osv.osv): 'number_of_days': nb, 'user_id': user_id }) - vals= { - 'name':record.name, - 'date_from':record.date_from, - 'date_to':record.date_to, - 'calendar_id':record.employee_id.calendar_id.id, - 'company_id':record.employee_id.company_id.id, - 'resource_id':record.employee_id.resource_id.id - } + #vals= { + # 'name':record.name, + # 'date_from':record.date_from, + # 'date_to':record.date_to, + # 'calendar_id':record.employee_id.calendar_id.id, + # 'company_id':record.employee_id.company_id.id, + # 'resource_id':record.employee_id.resource_id.id + # } #self.pool.get('resource.calendar.leaves').create(cr,uid,vals) return True @@ -365,14 +311,17 @@ class hr_holidays(osv.osv): employee_ids = self.pool.get('hr.employee').search(cr, uid, []) for employee in employee_ids: vals['employee_id'] = employee + user_id = self.pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)]) + if user_id: + vals['user_id'] = user_id[0] holiday_ids.append(self.create(cr, uid, vals, context={})) self.holidays_confirm(cr, uid, holiday_ids) self.holidays_validate(cr, uid, holiday_ids) - if record.holiday_status_id.section_id and record.date_from and record.date_to and record.employee_id: + if record.holiday_status_id.categ_id and record.date_from and record.date_to and record.employee_id: vals={} vals['name']=record.name - vals['section_id']=record.holiday_status_id.section_id.id + vals['categ_id']=record.holiday_status_id.categ_id.id epoch_c = time.mktime(time.strptime(record.date_to,'%Y-%m-%d %H:%M:%S')) epoch_d = time.mktime(time.strptime(record.date_from,'%Y-%m-%d %H:%M:%S')) diff_day = (epoch_c - epoch_d)/(3600*24) @@ -380,7 +329,7 @@ class hr_holidays(osv.osv): vals['note'] = record.notes vals['user_id'] = record.user_id.id vals['date'] = record.date_from - case_id = self.pool.get('crm.case').create(cr,uid,vals) + case_id = self.pool.get('crm.meeting').create(cr,uid,vals) self.write(cr, uid, ids, {'case_id':case_id}) return True hr_holidays() diff --git a/addons/hr_holidays/hr_holidays_report.xml b/addons/hr_holidays/hr_holidays_report.xml index 23d1e1a75bf..66669bd2f51 100644 --- a/addons/hr_holidays/hr_holidays_report.xml +++ b/addons/hr_holidays/hr_holidays_report.xml @@ -9,6 +9,77 @@ auto="False" menu="False"/> + + + + + + + hr.holidays.report.tree + hr.holidays.report + tree + + + + + + + + + + + hr.holidays.report.form + hr.holidays.report + form + + + + + + + + + + hr.holiday.report.graph + hr.holidays.report + graph + + + + + + + + + + + Available Holidays + hr.holidays.report + form + tree,graph + + + + + + + tree + + + + + + + + graph + + + + + + + + diff --git a/addons/hr_holidays/hr_holidays_wizard.xml b/addons/hr_holidays/hr_holidays_wizard.xml index fb505d65497..fc433a181e7 100644 --- a/addons/hr_holidays/hr_holidays_wizard.xml +++ b/addons/hr_holidays/hr_holidays_wizard.xml @@ -11,10 +11,11 @@ name="Holidays" parent="hr.menu_hr_reporting" sequence="3" /> - - @@ -24,7 +25,7 @@ - diff --git a/addons/hr_holidays/hr_view.xml b/addons/hr_holidays/hr_view.xml index 146e06d25a3..d46898bd6bb 100644 --- a/addons/hr_holidays/hr_view.xml +++ b/addons/hr_holidays/hr_view.xml @@ -1,13 +1,6 @@ - - - hr.holidays.filter hr.holidays @@ -38,7 +31,6 @@ - Leave Request hr.holidays @@ -73,7 +65,6 @@ - Allocation Request hr.holidays @@ -104,18 +95,17 @@ - - + - Company Allocation + Leaves Management hr.holidays form -
+ - + @@ -153,13 +143,13 @@ - + - + - + + Leave Request(s) hr.holidays @@ -226,13 +195,8 @@ - @@ -256,16 +220,32 @@ form - + - + action="open_allocation_holidays"/> + + + hr.holidays + form + tree,form + + {'allocation_type':'company'} + + + @@ -276,7 +256,7 @@ - + @@ -303,10 +283,6 @@ - - - Holiday Per User - hr.holidays.per.user - form - tree,form - - - - - diff --git a/addons/hr_holidays/report/__init__.py b/addons/hr_holidays/report/__init__.py index bb1f6229bc0..00783721fe3 100644 --- a/addons/hr_holidays/report/__init__.py +++ b/addons/hr_holidays/report/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # @@ -15,11 +15,11 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# along with this program. If not, see . # ############################################################################## import holidays_summary_report -import hr_holidays_report +import available_holidays # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_holidays/report/available_holidays.py b/addons/hr_holidays/report/available_holidays.py new file mode 100644 index 00000000000..259aea0dec9 --- /dev/null +++ b/addons/hr_holidays/report/available_holidays.py @@ -0,0 +1,33 @@ +from osv import fields,osv +import tools + +class hr_holidays_report(osv.osv): + _name = "hr.holidays.report" + _auto = False + _columns = { + 'employee_id': fields.many2one ('hr.employee', 'Employee', readonly=True), + 'holiday_status_id': fields.many2one('hr.holidays.status', 'Leave Type', readonly=True), + # 'max_leave': fields.float('Allocated Leaves', readonly=True), +# 'taken_leaves': fields.float('Taken Leaves', readonly=True), + 'remaining_leave': fields.float('Remaining Leaves',readonly=True), + } + def init(self, cr): + tools.drop_view_if_exists(cr, 'hr_holidays_report') + cr.execute(""" + create or replace view hr_holidays_report as ( + select + min(h.id) as id, + h.employee_id as employee_id, + h.holiday_status_id as holiday_status_id, + sum(number_of_days) as remaining_leave + from + hr_holidays h + left join hr_holidays_status s on (s.id = h.holiday_status_id) + where h.state = 'validate' + and h.employee_id is not null + and s.active <> 'f' + group by h.holiday_status_id, h.employee_id + )""") +hr_holidays_report() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_holidays/report/holidays_summary_report.py b/addons/hr_holidays/report/holidays_summary_report.py index e0cb697b3b9..c61e8f733b4 100644 --- a/addons/hr_holidays/report/holidays_summary_report.py +++ b/addons/hr_holidays/report/holidays_summary_report.py @@ -44,7 +44,6 @@ def strToDate(dt): def emp_create_xml(self,cr,uid,dept,holiday_type,row_id,empid,name,som,eom): display={} - if dept==0: count=0 p_id=pooler.get_pool(cr.dbname).get('hr.holidays').search(cr,uid,[('employee_id','in',[empid,False]), ('type', '=', 'remove')]) diff --git a/addons/hr_holidays/security/ir.model.access.csv b/addons/hr_holidays/security/ir.model.access.csv index 236b6a8fb89..a7426724594 100644 --- a/addons/hr_holidays/security/ir.model.access.csv +++ b/addons/hr_holidays/security/ir.model.access.csv @@ -1,5 +1,5 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_hr_holydays_status_user","hr.holidays.status user","model_hr_holidays_status","hr.group_hr_user",1,1,1,1 -"access_hr_holidays_user","hr holidays user","model_hr_holidays","hr.group_hr_user",1,1,1,1 -"access_hr_holidays_per_user","hr.holidays.per.user","model_hr_holidays_per_user","hr.group_hr_user",1,1,1,1 -"access_hr_holidays_report","hr.holidays.report","model_hr_holidays_report","hr.group_hr_user",1,1,1,1 +"access_hr_holydays_status_user","hr.holidays.status user","model_hr_holidays_status","hr.group_hr_user",1,0,0,0 +"access_hr_holidays_user","hr holidays user","model_hr_holidays","hr.group_hr_user",1,1,1,0 +"access_hr_holydays_status_manager","hr.holidays.status manager","model_hr_holidays_status","hr.group_hr_manager",1,1,1,1 +"access_hr_holidays_manager","hr holidays manager","model_hr_holidays","hr.group_hr_manager",1,1,1,1