diff --git a/CHANGELOG b/CHANGELOG
index f20cbd57ed..015b1f865a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Add workflow graph on ir.model
* Add context model on ir.action.act_window
* Switch to WSGI API
* Limit the login size in LoginAttempt
diff --git a/trytond/ir/__init__.py b/trytond/ir/__init__.py
index ad5a5cc087..a000482ec6 100644
--- a/trytond/ir/__init__.py
+++ b/trytond/ir/__init__.py
@@ -98,4 +98,5 @@ def register():
module='ir', type_='wizard')
Pool.register(
ModelGraph,
+ ModelWorkflowGraph,
module='ir', type_='report')
diff --git a/trytond/ir/model.py b/trytond/ir/model.py
index 0fe55cce5d..9045980843 100644
--- a/trytond/ir/model.py
+++ b/trytond/ir/model.py
@@ -12,7 +12,7 @@ try:
except ImportError:
import json
-from ..model import ModelView, ModelSQL, fields, Unique
+from ..model import ModelView, ModelSQL, Workflow, fields, Unique
from ..report import Report
from ..wizard import Wizard, StateView, StateAction, Button
from ..transaction import Transaction
@@ -31,6 +31,7 @@ except ImportError:
__all__ = [
'Model', 'ModelField', 'ModelAccess', 'ModelFieldAccess', 'ModelButton',
'ModelData', 'PrintModelGraphStart', 'PrintModelGraph', 'ModelGraph',
+ 'ModelWorkflowGraph',
]
IDENTIFIER = re.compile(r'^[a-zA-z_][a-zA-Z0-9_]*$')
@@ -1095,3 +1096,58 @@ class ModelGraph(Report):
edge = pydot.Edge(str(tail), str(head), **args)
graph.add_edge(edge)
+
+
+class ModelWorkflowGraph(Report):
+ __name__ = 'ir.model.workflow_graph'
+
+ @classmethod
+ def execute(cls, ids, data):
+ import pydot
+ pool = Pool()
+ Model = pool.get('ir.model')
+ ActionReport = pool.get('ir.action.report')
+
+ action_report_ids = ActionReport.search([
+ ('report_name', '=', cls.__name__)
+ ])
+ if not action_report_ids:
+ raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
+ action_report = ActionReport(action_report_ids[0])
+
+ models = Model.browse(ids)
+
+ graph = pydot.Dot()
+ graph.set('center', '1')
+ graph.set('ratio', 'auto')
+ direction = Transaction().context.get('language_direction', 'ltr')
+ graph.set('rankdir', {'ltr': 'LR', 'rtl': 'RL'}[direction])
+ cls.fill_graph(models, graph)
+ data = graph.create(prog='dot', format='png')
+ return ('png', fields.Binary.cast(data), False, action_report.name)
+
+ @classmethod
+ def fill_graph(cls, models, graph):
+ 'Fills pydot graph with models wizard.'
+ import pydot
+ pool = Pool()
+
+ for record in models:
+ Model = pool.get(record.model)
+
+ if not issubclass(Model, Workflow):
+ continue
+
+ subgraph = pydot.Cluster('%s' % record.id, label=record.model)
+ graph.add_subgraph(subgraph)
+
+ state_field = getattr(Model, Model._transition_state)
+ for state, _ in state_field.selection:
+ node = pydot.Node(
+ '"%s"' % state, shape='octagon', label=state)
+ subgraph.add_node(node)
+
+ for from_, to in Model._transitions:
+ edge = pydot.Edge('"%s"' % from_, '"%s"' % to,
+ arrowhead='normal')
+ subgraph.add_edge(edge)
diff --git a/trytond/ir/model.xml b/trytond/ir/model.xml
index ae4d69d879..4983161869 100644
--- a/trytond/ir/model.xml
+++ b/trytond/ir/model.xml
@@ -143,6 +143,18 @@ this repository contains the full copyright notices and license terms. -->
+
+ Workflow Graph
+ ir.model
+ ir.model.workflow_graph
+
+
+ form_print
+ ir.model,-1
+
+
+
ir.model.button
tree