summaryrefslogtreecommitdiff
path: root/odoo/addons/base
diff options
context:
space:
mode:
authorYannick Tivisse <yti@odoo.com>2016-11-22 15:05:22 +0100
committerYannick Tivisse <yti@odoo.com>2016-11-23 11:52:39 +0100
commit98cb4719dbe90dbe980be4ad98f22fe155c2dae1 (patch)
treef6c9a9294908207116782c6f8513f91d7aed6403 /odoo/addons/base
parent94d2b97da4af28c551d246f14347f7674c4d50f5 (diff)
[REM] workflow: Remove workflow engine, documentation and tests
Diffstat (limited to 'odoo/addons/base')
-rw-r--r--odoo/addons/base/__init__.py1
-rw-r--r--odoo/addons/base/__manifest__.py1
-rw-r--r--odoo/addons/base/ir/ir_actions.py57
-rw-r--r--odoo/addons/base/ir/ir_actions.xml19
-rw-r--r--odoo/addons/base/ir/ir_model.py18
-rw-r--r--odoo/addons/base/ir/ir_ui_view.py26
-rw-r--r--odoo/addons/base/security/ir.model.access.csv10
-rw-r--r--odoo/addons/base/tests/test_ir_actions.py104
-rw-r--r--odoo/addons/base/workflow/__init__.py4
-rw-r--r--odoo/addons/base/workflow/workflow.py185
-rw-r--r--odoo/addons/base/workflow/workflow_report.py204
-rw-r--r--odoo/addons/base/workflow/workflow_view.xml370
12 files changed, 2 insertions, 997 deletions
diff --git a/odoo/addons/base/__init__.py b/odoo/addons/base/__init__.py
index 73f0b4625b1..ee62dc5ce3d 100644
--- a/odoo/addons/base/__init__.py
+++ b/odoo/addons/base/__init__.py
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import ir
-import workflow
import module
import res
import report
diff --git a/odoo/addons/base/__manifest__.py b/odoo/addons/base/__manifest__.py
index 4cd21782039..6510f6c1987 100644
--- a/odoo/addons/base/__manifest__.py
+++ b/odoo/addons/base/__manifest__.py
@@ -38,7 +38,6 @@ The kernel of Odoo, needed for all installation.
'ir/report_ir_model.xml',
'ir/ir_logging_view.xml',
'ir/ir_qweb.xml',
- 'workflow/workflow_view.xml',
'module/module_view.xml',
'module/module_data.xml',
'module/module_report.xml',
diff --git a/odoo/addons/base/ir/ir_actions.py b/odoo/addons/base/ir/ir_actions.py
index 7eb30d3f126..55da335d7e2 100644
--- a/odoo/addons/base/ir/ir_actions.py
+++ b/odoo/addons/base/ir/ir_actions.py
@@ -428,7 +428,6 @@ class IrActionsServer(models.Model):
The available actions are :
- 'Execute Python Code': a block of python code that will be executed
- - 'Trigger a Workflow Signal': send a signal to a workflow
- 'Run a Client Action': choose a client action to launch
- 'Create or Copy a new Record': create a new record with new values, or
copy an existing record in your database
@@ -463,7 +462,6 @@ class IrActionsServer(models.Model):
note that the added key length should not be higher than already-existing
ones. """
return [('code', 'Execute Python Code'),
- ('trigger', 'Trigger a Workflow Signal'),
('client_action', 'Run a Client Action'),
('object_create', 'Create or Copy a new Record'),
('object_write', 'Write on a Record'),
@@ -482,7 +480,6 @@ class IrActionsServer(models.Model):
string='Action To Do', default='code', required=True,
help="Type of server action. The following values are available:\n"
"- 'Execute Python Code': a block of python code that will be executed\n"
- "- 'Trigger a Workflow Signal': send a signal to a workflow\n"
"- 'Run a Client Action': choose a client action to launch\n"
"- 'Create or Copy a new Record': create a new record with new values, or copy an existing record in your database\n"
"- 'Write on a Record': update the values of a record\n"
@@ -505,17 +502,6 @@ class IrActionsServer(models.Model):
default=DEFAULT_PYTHON_CODE,
help="Write Python code that the action will execute. Some variables are "
"available for use; help about pyhon expression is given in the help tab.")
- # Workflow signal
- use_relational_model = fields.Selection([('base', 'Use the base model of the action'),
- ('relational', 'Use a relation field on the base model')],
- string='Relational Target Model', default='base', required=True)
- wkf_transition_id = fields.Many2one('workflow.transition', string='Signal to Trigger',
- help="Select the workflow signal to trigger.")
- wkf_model_id = fields.Many2one('ir.model', string='Target Model',
- help="The model that will receive the workflow signal. Note that it should have a workflow associated with it.")
- wkf_model_name = fields.Char(string='Target Model Name', related='wkf_model_id.model', store=True, readonly=True)
- wkf_field_id = fields.Many2one('ir.model.fields', string='Relation Field',
- oldname='trigger_obj_id', help="The field on the current object that links to the target object record (must be a many2one, or an integer field with the record ID)")
# Multi
child_ids = fields.Many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id',
string='Child Actions', help='Child server actions that will be executed. Note that the last return returned action value will be used as global return value.')
@@ -548,7 +534,7 @@ class IrActionsServer(models.Model):
"If it is a relationship field you will be able to select "
"a target field at the destination of the relationship.")
sub_object = fields.Many2one('ir.model', string='Sub-model', readonly=True,
- help="When a relationship field is selected as first field, "
+ help="fWhen a relationship field is selected as first field, "
"this field shows the document model the relationship goes to.")
sub_model_object_field = fields.Many2one('ir.model.fields', string='Sub-field',
help="When a relationship field is selected as first field, "
@@ -615,34 +601,12 @@ class IrActionsServer(models.Model):
@api.onchange('model_id')
def _onchange_model_id(self):
- """ When changing the action base model, reset workflow and crud config
+ """ When changing the action base model, reset crud config
to ease value coherence. """
self.use_create = 'new'
self.use_write = 'current'
- self.use_relational_model = 'base'
- self.wkf_model_id = self.model_id
- self.wkf_field_id = False
self.crud_model_id = self.model_id
- @api.onchange('use_relational_model', 'wkf_field_id')
- def _onchange_wkf_config(self):
- """ Update workflow type configuration
-
- - update the workflow model (for base (model_id) /relational (field.relation))
- - update wkf_transition_id to False if workflow model changes, to force
- the user to choose a new one
- """
- if self.use_relational_model == 'relational' and self.wkf_field_id:
- field = self.wkf_field_id
- self.wkf_model_id = self.env['ir.model'].search([('model', '=', field.relation)])
- else:
- self.wkf_model_id = self.model_id
-
- @api.onchange('wkf_model_id')
- def _onchange_wkf_model_id(self):
- """ When changing the workflow model, update its stored name also """
- self.wkf_transition_id = False
-
@api.onchange('use_create', 'use_write', 'ref_object')
def _onchange_crud_config(self):
""" Wrapper on CRUD-type (create or write) on_change """
@@ -797,23 +761,6 @@ class IrActionsServer(models.Model):
return eval_context['action']
@api.model
- def run_action_trigger(self, action, eval_context=None):
- """ Trigger a workflow signal, depending on the use_relational_model:
-
- - `base`: base_model_pool.signal_workflow(cr, uid, context.get('active_id'), <TRIGGER_NAME>)
- - `relational`: find the related model and object, using the relational
- field, then target_model_pool.signal_workflow(cr, uid, target_id, <TRIGGER_NAME>)
- """
- # weird signature and calling -> no self.env, use action param's
- record = action.env[action.model_id.model].browse(self._context['active_id'])
- if action.use_relational_model == 'relational':
- record = getattr(record, action.wkf_field_id.name)
- if not isinstance(record, models.BaseModel):
- record = action.env[action.wkf_model_id.model].browse(record)
-
- record.signal_workflow(action.wkf_transition_id.signal)
-
- @api.model
def run_action_multi(self, action, eval_context=None):
res = False
for act in action.child_ids:
diff --git a/odoo/addons/base/ir/ir_actions.xml b/odoo/addons/base/ir/ir_actions.xml
index 15f517d075b..0560e95dd59 100644
--- a/odoo/addons/base/ir/ir_actions.xml
+++ b/odoo/addons/base/ir/ir_actions.xml
@@ -295,25 +295,6 @@
<field name="code" widget="ace" options="{'mode': 'python'}" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
</page>
- <page string="Worflow Signal" autofocus="autofocus"
- attrs="{'invisible': [('state', '!=', 'trigger')]}">
- <p attrs="{'invisible': [('model_id', '!=', False)]}">
- Please set the Base Model before setting the action details.
- </p>
- <group attrs="{'invisible': [('model_id', '=', False)]}">
- <field name="use_relational_model" widget="radio"
- attrs="{'readonly': [('model_id', '=', False)]}"/>
- <field name="wkf_field_id" context="{'key': ''}"
- attrs="{'required': [('state', '=', 'trigger'), ('use_relational_model', '=', 'relational')],
- 'invisible': [('use_relational_model', '=', 'base')]}"
- domain="[('model_id', '=', model_id), ('ttype', 'in', ['many2one'])]"/>
- <field name="wkf_model_id" invisible="1"/>
- <field name="wkf_model_name" invisible="1"/>
- <field name="wkf_transition_id" attrs="{'required': [('state', '=', 'trigger')]}"
- domain="[('wkf_id.osv', '=', wkf_model_name)]"/>
- </group>
- </page>
-
<page string="Client" autofocus="autofocus"
attrs="{'invisible': [('state', '!=', 'client_action')]}">
<group>
diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py
index c989dd4efc5..7259c0bbc1d 100644
--- a/odoo/addons/base/ir/ir_model.py
+++ b/odoo/addons/base/ir/ir_model.py
@@ -1227,32 +1227,14 @@ class IrModelData(models.Model):
self = self.with_context(**{MODULE_UNINSTALL_FLAG: True})
datas = self.search([('module', 'in', modules_to_remove)])
- wkf_todo = []
to_unlink = tools.OrderedSet()
undeletable = self.browse([])
for data in datas.sorted(key='id', reverse=True):
model = data.model
res_id = data.res_id
-
to_unlink.add((model, res_id))
- if model == 'workflow.activity':
- # Special treatment for workflow activities: temporarily revert their
- # incoming transition and trigger an update to force all workflow items
- # to move out before deleting them
- self._cr.execute('SELECT res_type, res_id FROM wkf_instance WHERE id IN (SELECT inst_id FROM wkf_workitem WHERE act_id=%s)', (res_id,))
- wkf_todo.extend(self._cr.fetchall())
- self._cr.execute("UPDATE wkf_transition SET condition='True', group_id=NULL, signal=NULL, act_to=act_from, act_from=%s WHERE act_to=%s", (res_id, res_id))
- self.invalidate_cache()
-
- for model, res_id in wkf_todo:
- try:
- record = self.env[model].browse(res_id)
- record.step_workflow()
- except Exception:
- _logger.info('Unable to force processing of workflow for item %s@%s in order to leave activity to be deleted', res_id, model, exc_info=True)
-
def unlink_if_refcount(to_unlink):
undeletable = self.browse()
for model, res_id in to_unlink:
diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py
index 856ac56cb80..3d89e50896d 100644
--- a/odoo/addons/base/ir/ir_ui_view.py
+++ b/odoo/addons/base/ir/ir_ui_view.py
@@ -804,31 +804,6 @@ actual arch.
return arch
@api.model
- def _disable_workflow_buttons(self, model, node):
- """ Set the buttons in node to readonly if the user can't activate them. """
- if model is None or self.env.user.id == SUPERUSER_ID:
- # admin user can always activate workflow buttons
- return node
-
- # TODO handle the case of more than one workflow for a model or multiple
- # transitions with different groups and same signal
- user_group_ids = set(self.env.user.groups_id.ids)
- buttons = (n for n in node.getiterator('button') if n.get('type') != 'object')
- for button in buttons:
- query = """SELECT DISTINCT t.group_id
- FROM wkf
- INNER JOIN wkf_activity a ON a.wkf_id = wkf.id
- INNER JOIN wkf_transition t ON (t.act_to = a.id)
- WHERE wkf.osv = %s
- AND t.signal = %s
- AND t.group_id is NOT NULL"""
- self._cr.execute(query, (model, button.get('name')))
- group_ids = set(row[0] for row in self._cr.fetchall() if row[0])
- can_click = not group_ids or bool(user_group_ids & group_ids)
- button.set('readonly', str(int(not can_click)))
- return node
-
- @api.model
def postprocess_and_fields(self, model, node, view_id):
""" Return an architecture and a description of all the fields.
@@ -864,7 +839,6 @@ actual arch.
node = self.add_on_change(model, node)
fields_def = self.postprocess(model, node, view_id, False, fields)
- node = self._disable_workflow_buttons(model, node)
if node.tag in ('kanban', 'tree', 'form', 'gantt'):
for action, operation in (('create', 'create'), ('delete', 'unlink'), ('edit', 'write')):
if (not node.get(action) and
diff --git a/odoo/addons/base/security/ir.model.access.csv b/odoo/addons/base/security/ir.model.access.csv
index 1736580b147..06f7b3fc690 100644
--- a/odoo/addons/base/security/ir.model.access.csv
+++ b/odoo/addons/base/security/ir.model.access.csv
@@ -76,16 +76,6 @@
"access_ir_actions_report_xml_all","ir_actions_report_xml","model_ir_actions_report_xml",,1,0,0,0
"access_ir_actions_report_xml_group_system","ir_actions_report_xml_group_system","model_ir_actions_report_xml","group_system",1,1,1,1
"access_ir_actions_todo_group_system","ir_actions_todo group system","model_ir_actions_todo","group_system",1,1,1,1
-"access_workflow_all","workflow_all","model_workflow",,1,0,0,0
-"access_workflow_group_system","workflow_group_system","model_workflow","group_system",1,1,1,1
-"access_workflow_activity_all","workflow_activity_all","model_workflow_activity",,1,0,0,0
-"access_workflow_activity_group_system","workflow_activity_group_system","model_workflow_activity","group_system",1,1,1,1
-"access_workflow_instance_all","workflow_instance_all","model_workflow_instance",,1,0,0,0
-"access_workflow_transition_all","workflow_transition_all","model_workflow_transition",,1,0,0,0
-"access_workflow_transition_group_system","workflow_transition_group_system","model_workflow_transition","group_system",1,1,1,1
-"access_workflow_triggers_all","workflow_triggers_all","model_workflow_triggers",,1,0,0,0
-"access_workflow_triggers_group_system","workflow_triggers_group_system","model_workflow_triggers","group_system",1,1,1,1
-"access_workflow_workitem_all","workflow_workitem_all","model_workflow_workitem",,1,1,1,1
"access_ir_actions_act_window_view_all","ir_actions_act_window_view_all","model_ir_actions_act_window_view",,1,0,0,0
"access_ir_actions_act_window_view_group_system","ir_actions_act_window_view_group_system","model_ir_actions_act_window_view","group_system",1,1,1,1
"access_ir_actions_act_url_all","ir_actions_act_url_all","model_ir_actions_act_url",,1,0,0,0
diff --git a/odoo/addons/base/tests/test_ir_actions.py b/odoo/addons/base/tests/test_ir_actions.py
index 7f63fd99c60..0d5174b179c 100644
--- a/odoo/addons/base/tests/test_ir_actions.py
+++ b/odoo/addons/base/tests/test_ir_actions.py
@@ -4,7 +4,6 @@
from odoo.exceptions import ValidationError
from odoo.tools import mute_logger
import odoo.tests.common as common
-import odoo.workflow
class TestServerActionsBase(common.TransactionCase):
@@ -91,109 +90,6 @@ class TestServerActions(TestServerActionsBase):
partners = self.test_partner.search([('name', 'ilike', 'TestingPartner_code')])
self.assertEqual(len(partners), 1, 'ir_actions_server: 1 new partner should have been created')
- def test_20_trigger(self):
- Workflow = self.env['workflow']
- WorkflowActivity = self.env['workflow.activity']
- WorkflowTransition = self.env['workflow.transition']
-
- # Data: code server action (at this point code-based actions should work)
- action2 = self.action.create({
- 'name': 'TestAction2',
- 'type': 'ir.actions.server',
- 'condition': 'True',
- 'model_id': self.res_partner_model.id,
- 'state': 'code',
- 'code': 'record.write({"comment": "MyComment"})',
- })
- action3 = self.action.create({
- 'name': 'TestAction3',
- 'type': 'ir.actions.server',
- 'condition': 'True',
- 'model_id': self.res_country_model.id,
- 'state': 'code',
- 'code': 'record.write({"code": "ZZ"})',
- })
-
- # Data: create workflows
- partner_workflow = Workflow.create({
- 'name': 'TestWorkflow',
- 'osv': 'res.partner',
- 'on_create': True,
- })
- partner_activity1 = WorkflowActivity.create({
- 'name': 'PartnerStart',
- 'wkf_id': partner_workflow.id,
- 'flow_start': True,
- })
- partner_activity2 = WorkflowActivity.create({
- 'name': 'PartnerTwo',
- 'wkf_id': partner_workflow.id,
- 'kind': 'function',
- 'action': 'True',
- 'action_id': action2.id,
- })
- partner_transition1 = WorkflowTransition.create({
- 'signal': 'partner_trans',
- 'act_from': partner_activity1.id,
- 'act_to': partner_activity2.id,
- })
- country_workflow = Workflow.create({
- 'name': 'TestWorkflow',
- 'osv': 'res.country',
- 'on_create': True,
- })
- country_activity1 = WorkflowActivity.create({
- 'name': 'CountryStart',
- 'wkf_id': country_workflow.id,
- 'flow_start': True,
- })
- country_activity2 = WorkflowActivity.create({
- 'name': 'CountryTwo',
- 'wkf_id': country_workflow.id,
- 'kind': 'function',
- 'action': 'True',
- 'action_id': action3.id,
- })
- country_transition1 = WorkflowTransition.create({
- 'signal': 'country_trans',
- 'act_from': country_activity1.id,
- 'act_to': country_activity2.id,
- })
-
- # Data: re-create country and partner to benefit from the workflows
- country = self.test_country.create({
- 'name': 'TestingCountry2',
- 'code': 'T2',
- })
- partner = self.test_partner.create({
- 'name': 'TestingPartner2',
- 'country_id': country.id,
- })
- context = dict(self.context, active_id=partner.id)
-
- # Run the action on partner object itself ('base')
- self.action.write({
- 'state': 'trigger',
- 'use_relational_model': 'base',
- 'wkf_model_id': self.res_partner_model.id,
- 'wkf_transition_id': partner_transition1.id,
- })
- self.action.with_context(context).run()
- self.assertEqual(partner.comment, 'MyComment', 'ir_actions_server: incorrect signal trigger')
-
- # Run the action on related country object ('relational')
- self.action.write({
- 'use_relational_model': 'relational',
- 'wkf_model_id': self.res_country_model.id,
- 'wkf_field_id': self.res_partner_country_field.id,
- 'wkf_transition_id': country_transition1.id,
- })
- self.action.with_context(context).run()
- self.assertEqual(country.code, 'ZZ', 'ir_actions_server: incorrect signal trigger')
-
- # Clear workflow cache, otherwise odoo will try to create workflows even if it has been deleted
- odoo.workflow.clear_cache(self.cr, self.uid)
-
def test_30_client(self):
client_action = self.env['ir.actions.client'].create({
'name': 'TestAction2',
diff --git a/odoo/addons/base/workflow/__init__.py b/odoo/addons/base/workflow/__init__.py
deleted file mode 100644
index d213caf0f1a..00000000000
--- a/odoo/addons/base/workflow/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import workflow
-import workflow_report
diff --git a/odoo/addons/base/workflow/workflow.py b/odoo/addons/base/workflow/workflow.py
deleted file mode 100644
index 18eeb5002eb..00000000000
--- a/odoo/addons/base/workflow/workflow.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models, _
-from odoo.exceptions import UserError
-import odoo.workflow
-
-
-class Workflow(models.Model):
- _name = "workflow"
- _table = "wkf"
- _order = "name"
-
- name = fields.Char('Name', required=True)
- osv = fields.Char('Resource Object', required=True, index=True)
- on_create = fields.Boolean('On Create', default=True, index=True)
- activities = fields.One2many('workflow.activity', 'wkf_id', string='Activities')
-
- @api.multi
- def copy(self, values):
- raise UserError(_("Duplicating workflows is not possible, please create a new workflow"))
-
- @api.multi
- def write(self, vals):
- odoo.workflow.clear_cache(self._cr, self._uid)
- return super(Workflow, self).write(vals)
-
- @api.model
- def get_active_workitems(self, res_model, res_id):
- cr = self._cr
- cr.execute("SELECT * FROM wkf WHERE osv=%s LIMIT 1", (res_model,))
- wkfinfo = cr.dictfetchone()
- workitems = []
- if wkfinfo:
- query = """ SELECT id FROM wkf_instance
- WHERE res_id=%s AND wkf_id=%s
- ORDER BY state LIMIT 1 """
- cr.execute(query, (res_id, wkfinfo['id']))
- inst_id = cr.fetchone()
-
- query = """ SELECT act_id, COUNT(*) FROM wkf_workitem
- WHERE inst_id=%s GROUP BY act_id """
- cr.execute(query, (inst_id,))
- workitems = dict(cr.fetchall())
- return {'wkf': wkfinfo, 'workitems': workitems}
-
- @api.model
- def create(self, vals):
- odoo.workflow.clear_cache(self._cr, self._uid)
- return super(Workflow, self).create(vals)
-
-
-class WorkflowActivity(models.Model):
- _name = "workflow.activity"
- _table = "wkf_activity"
- _order = "name"
-
- name = fields.Char('Name', required=True)
- wkf_id = fields.Many2one('workflow', string='Workflow', ondelete='cascade',
- required=True, index=True)
- split_mode = fields.Selection([('XOR', 'Xor'), ('OR','Or'), ('AND','And')],
- string='Split Mode', size=3, required=True, default='XOR')
- join_mode = fields.Selection([('XOR', 'Xor'), ('AND', 'And')],
- string='Join Mode', size=3, required=True, default='XOR')
- kind = fields.Selection([('dummy', 'Dummy'), ('function', 'Function'),
- ('subflow', 'Subflow'), ('stopall', 'Stop All')],
- string='Kind', required=True, default='dummy')
- action = fields.Text('Python Action')
- action_id = fields.Many2one('ir.actions.server', string='Server Action', ondelete='set null')
- flow_start = fields.Boolean('Flow Start')
- flow_stop = fields.Boolean('Flow Stop')
- subflow_id = fields.Many2one('workflow', string='Subflow')
- signal_send = fields.Char('Signal (subflow.*)')
- out_transitions = fields.One2many('workflow.transition', 'act_from', string='Outgoing Transitions')
- in_transitions = fields.One2many('workflow.transition', 'act_to', string='Incoming Transitions')
-
- @api.multi
- def unlink(self):
- if not self._context.get('_force_unlink') and \
- self.env['workflow.workitem'].search([('act_id', 'in', self.ids)]):
- raise UserError(_('Please make sure no workitems refer to an activity before deleting it!'))
- super(WorkflowActivity, self).unlink()
-
-
-class WorkflowTransition(models.Model):
- _name = "workflow.transition"
- _table = "wkf_transition"
- _rec_name = 'signal'
- _order = 'sequence,id'
-
- trigger_model = fields.Char('Trigger Object')
- trigger_expr_id = fields.Char('Trigger Expression')
- sequence = fields.Integer('Sequence', default=10)
- signal = fields.Char('Signal (Button Name)',
- help="When the operation of transition comes from a button pressed in the client form, "
- "signal tests the name of the pressed button. If signal is NULL, no button is necessary to validate this transition.")
- group_id = fields.Many2one('res.groups', string='Group Required',
- help="The group that a user must have to be authorized to validate this transition.")
- condition = fields.Char('Condition', required=True, default='True',
- help="Expression to be satisfied if we want the transition done.")
- act_from = fields.Many2one('workflow.activity', string='Source Activity',
- ondelete='cascade', required=True, index=True,
- help="Source activity. When this activity is over, the condition is tested to determine if we can start the ACT_TO activity.")
- act_to = fields.Many2one('workflow.activity', string='Destination Activity',
- ondelete='cascade', required=True, index=True,
- help="The destination activity.")
- wkf_id = fields.Many2one('workflow', related='act_from.wkf_id', string='Workflow')
-
- @api.multi
- def name_get(self):
- return [
- (line.id, line.signal or "%s+%s" % (line.act_from.name, line.act_to.name))
- for line in self
- ]
-
- @api.model
- def name_search(self, name, args=None, operator='ilike', limit=100):
- if name:
- domain = ['|', ('act_from', operator, name), ('act_to', operator, name)] + (args or [])
- return self.search(domain, limit=limit).name_get()
- return super(WorkflowTransition, self).name_search(name, args, operator, limit=limit)
-
-
-class WorkflowInstance(models.Model):
- _name = "workflow.instance"
- _table = "wkf_instance"
- _rec_name = 'res_type'
- _log_access = False
-
- uid = fields.Integer('User') # FIXME no constraint??
- wkf_id = fields.Many2one('workflow', string='Workflow', ondelete='cascade', index=True)
- res_id = fields.Integer('Resource ID')
- res_type = fields.Char('Resource Object')
- state = fields.Char('Status')
- transition_ids = fields.Many2many('workflow.transition', 'wkf_witm_trans', 'inst_id', 'trans_id')
-
- @api.model_cr_context
- def _auto_init(self):
- res = super(WorkflowInstance, self)._auto_init()
- cr = self._cr
- cr.execute('SELECT indexname FROM pg_indexes WHERE indexname=%s', ['wkf_instance_res_type_res_id_state_index'])
- if not cr.fetchone():
- cr.execute('CREATE INDEX wkf_instance_res_type_res_id_state_index ON wkf_instance (res_type, res_id, state)')
- cr.execute('SELECT indexname FROM pg_indexes WHERE indexname=%s', ['wkf_instance_res_id_wkf_id_index'])
- if not cr.fetchone():
- cr.execute('CREATE INDEX wkf_instance_res_id_wkf_id_index ON wkf_instance (res_id, wkf_id)')
- return res
-
-
-class WorkflowWorkitem(models.Model):
- _name = "workflow.workitem"
- _table = "wkf_workitem"
- _rec_name = 'state'
- _log_access = False
-
- act_id = fields.Many2one('workflow.activity', string='Activity',
- ondelete="cascade", required=True, index=True)
- wkf_id = fields.Many2one('workflow', related='act_id.wkf_id', string='Workflow')
- subflow_id = fields.Many2one('workflow.instance', string='Subflow',
- ondelete="set null", index=True)
- inst_id = fields.Many2one('workflow.instance', string='Instance',
- ondelete="cascade", required=True, index=True)
- state = fields.Char('Status', index=True)
-
-
-class WorkflowTriggers(models.Model):
- _name = "workflow.triggers"
- _table = "wkf_triggers"
- _log_access = False
-
- res_id = fields.Integer('Resource ID', size=128)
- model = fields.Char('Object')
- instance_id = fields.Many2one('workflow.instance', string='Destination Instance',
- ondelete="cascade")
- workitem_id = fields.Many2one('workflow.workitem', string='Workitem',
- ondelete="cascade", required=True)
-
- @api.model_cr_context
- def _auto_init(self):
- res = super(WorkflowTriggers, self)._auto_init()
- cr = self._cr
- cr.execute('SELECT indexname FROM pg_indexes WHERE indexname=%s', ['wkf_triggers_res_id_model_index'])
- if not cr.fetchone():
- cr.execute('CREATE INDEX wkf_triggers_res_id_model_index ON wkf_triggers (res_id, model)')
- return res
diff --git a/odoo/addons/base/workflow/workflow_report.py b/odoo/addons/base/workflow/workflow_report.py
deleted file mode 100644
index 8462d9c8ac0..00000000000
--- a/odoo/addons/base/workflow/workflow_report.py
+++ /dev/null
@@ -1,204 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-import logging
-from operator import itemgetter
-import os
-
-from odoo import report, tools
-
-_logger = logging.getLogger(__name__)
-
-def graph_get(cr, graph, wkf_ids, nested, workitem, witm_trans, processed_subflows):
- import pydot
- cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
- nodes = cr.dictfetchall()
- activities = {}
- actfrom = {}
- actto = {}
- for n in nodes:
- activities[n['id']] = n
- if n['subflow_id'] and nested and n['subflow_id'] not in processed_subflows:
- processed_subflows.add(n['subflow_id']) # don't create multiple times the same cluster.
- cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
- wkfinfo = cr.dictfetchone()
- graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "\"Subflow: %s\\nOSV: %s\"" % ( n['name'], wkfinfo['osv']) )
- (s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, witm_trans, processed_subflows)
- graph.add_subgraph(graph2)
- actfrom[n['id']] = s2
- actto[n['id']] = s1
- else:
- args = {}
- if n['flow_start'] or n['flow_stop']:
- args['style']='filled'
- args['color']='lightgrey'
- args['label']=n['name']
- workitems = ''
- if n['id'] in workitem:
- workitems = '\\nx ' + str(workitem[n['id']])
- args['label'] += workitems
- args['color'] = "red"
- args['style']='filled'
- if n['subflow_id']:
- args['shape'] = 'box'
- if nested and n['subflow_id'] in processed_subflows:
- cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
- wkfinfo = cr.dictfetchone()
- args['label'] = \
- '\"Subflow: %s\\nOSV: %s\\n(already expanded)%s\"' % \
- (n['name'], wkfinfo['osv'], workitems)
- args['color'] = 'green'
- args['style'] ='filled'
- graph.add_node(pydot.Node(n['id'], **args))
- actfrom[n['id']] = (n['id'],{})
- actto[n['id']] = (n['id'],{})
- node_ids = tuple(map(itemgetter('id'), nodes))
- cr.execute('select * from wkf_transition where act_from IN %s ORDER BY sequence,id', (node_ids,))
- transitions = cr.dictfetchall()
- for t in transitions:
- if not t['act_to'] in activities:
- continue
- args = {
- 'label': str(t['condition']).replace(' or ', '\\nor ')
- .replace(' and ','\\nand ')
- }
- if t['signal']:
- args['label'] += '\\n'+str(t['signal'])
- args['style'] = 'bold'
-
- if activities[t['act_from']]['split_mode']=='AND':
- args['arrowtail']='box'
- elif str(activities[t['act_from']]['split_mode'])=='OR ':
- args['arrowtail']='inv'
-
- if activities[t['act_to']]['join_mode']=='AND':
- args['arrowhead']='crow'
- if t['id'] in witm_trans:
- args['color'] = 'red'
-
- activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
- activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
- graph.add_edge(pydot.Edge( str(activity_from) ,str(activity_to), fontsize='10', **args))
-
- cr.execute('select * from wkf_activity where flow_start=True and wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
- start = cr.fetchone()[0]
- cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id in ("+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
- stop = cr.fetchall()
- if stop:
- stop = (stop[0][1], dict(stop))
- else:
- stop = ("stop",{})
- return (start, {}), stop
-
-
-def graph_instance_get(cr, graph, inst_id, nested=False):
- cr.execute('select wkf_id from wkf_instance where id=%s', (inst_id,))
- inst = cr.fetchall()
-
- def workitem_get(instance):
- cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%s group by act_id', (instance,))
- workitems = dict(cr.fetchall())
-
- cr.execute('select subflow_id from wkf_workitem where inst_id=%s', (instance,))
- for (subflow_id,) in cr.fetchall():
- workitems.update(workitem_get(subflow_id))
- return workitems
-
- def witm_get(instance):
- cr.execute("select trans_id from wkf_witm_trans where inst_id=%s", (instance,))
- return set(t[0] for t in cr.fetchall())
-
- processed_subflows = set()
- graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), witm_get(inst_id), processed_subflows)
-
-#
-# TODO: pas clean: concurrent !!!
-#
-
-class report_graph_instance(object):
- def __init__(self, cr, uid, ids, data):
- try:
- import pydot
- except Exception,e:
- _logger.warning(
- 'Import Error for pydot, you will not be able to render workflows.\n'
- 'Consider Installing PyDot or dependencies: http://dkbza.org/pydot.html.')
- raise e
- self.done = False
-
- try:
- cr.execute('select * from wkf where osv=%s limit 1',
- (data['model'],))
- wkfinfo = cr.dictfetchone()
- if not wkfinfo:
- ps_string = '''%PS-Adobe-3.0
-/inch {72 mul} def
-/Times-Roman findfont 50 scalefont setfont
-1.5 inch 15 inch moveto
-(No workflow defined) show
-showpage'''
- else:
- cr.execute('select i.id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where res_id=%s and osv=%s',(data['id'],data['model']))
- inst_ids = cr.fetchall()
- if not inst_ids:
- ps_string = '''%PS-Adobe-3.0
-/inch {72 mul} def
-/Times-Roman findfont 50 scalefont setfont
-1.5 inch 15 inch moveto
-(No workflow instance defined) show
-showpage'''
- else:
- graph = pydot.Dot(graph_name=data['model'].replace('.','_'),
- fontsize='16',
- label="""\\\n\\nWorkflow: %s\\n OSV: %s""" % (wkfinfo['name'],wkfinfo['osv']),
- size='7.3, 10.1', center='1', ratio='auto', rotate='0', rankdir='TB',
- )
- for inst_id in inst_ids:
- inst_id = inst_id[0]
- graph_instance_get(cr, graph, inst_id, data.get('nested', False))
- ps_string = graph.create(prog='dot', format='ps')
- except Exception:
- _logger.exception('Exception in call:')
- # string is in PS, like the success message would have been
- ps_string = '''%PS-Adobe-3.0
-/inch {72 mul} def
-/Times-Roman findfont 50 scalefont setfont
-1.5 inch 15 inch moveto
-(No workflow available) show
-showpage'''
- if os.name == "nt":
- prog = 'ps2pdf.bat'
- else:
- prog = 'ps2pdf'
- args = (prog, '-', '-')
- input, output = tools.exec_command_pipe(*args)
- input.write(ps_string)
- input.close()
- self.result = output.read()
- output.close()
- self.done = True
-
- def is_done(self):
- return self.done
-
- def get(self):
- if self.done:
- return self.result
- else:
- return None
-
-class report_graph(report.interface.report_int):
- def __init__(self, name, table):
- report.interface.report_int.__init__(self, name)
- self.table = table
-
- def result(self):
- if self.obj.is_done():
- return True, self.obj.get(), 'pdf'
- else:
- return False, False, False
-
- def create(self, cr, uid, ids, data, context=None):
- self.obj = report_graph_instance(cr, uid, ids, data)
- return self.obj.get(), 'pdf'
-
-report_graph('report.workflow.instance.graph', 'ir.workflow')
diff --git a/odoo/addons/base/workflow/workflow_view.xml b/odoo/addons/base/workflow/workflow_view.xml
deleted file mode 100644
index 22d1fb450c5..00000000000
--- a/odoo/addons/base/workflow/workflow_view.xml
+++ /dev/null
@@ -1,370 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <data>
- <menuitem id="menu_workflow_root" name="Workflows" parent="base.menu_custom" groups="base.group_no_one"/>
-
- <!--
- ================================
- Workflows
- ================================
- -->
-
- <record id="view_workflow_form" model="ir.ui.view">
- <field name="name">workflow.form</field>
- <field name="model">workflow</field>
- <field name="arch" type="xml">
- <form string="Workflow">
- <group col="4">
- <field name="name"/>
- <field name="osv"/>
- <field name="on_create"/>
- </group>
- <field name="activities"/>
- </form>
- </field>
- </record>
-
- <record id="view_workflow_search" model="ir.ui.view">
- <field name="name">workflow.search</field>
- <field name="model">workflow</field>
- <field name="arch" type="xml">
- <search string="Workflow">
- <field name="name" filter_domain="['|', ('name','ilike',self), ('osv','ilike',self)]" string="Workflow"/>
- </search>
- </field>
- </record>
-
- <record id="view_workflow_diagram" model="ir.ui.view">
- <field name="name">workflow.diagram</field>
- <field name="model">workflow</field>
- <field name="arch" type="xml">
- <diagram string="Workflow Editor">
- <node object="workflow.activity" shape="rectangle:subflow_id!=False" bgcolor="gray:flow_start==True;grey:flow_stop==True">
- <field name="name"/>
- <field name="kind"/>
- <field name="action"/>
- <field name="flow_start" invisible="1"/>
- <field name="flow_stop" invisible="1"/>
- <field name="subflow_id" invisible="1"/>
- </node>
- <arrow object="workflow.transition" source="act_from" destination="act_to" label="['signal','condition']">
- <field name="act_from"/>
- <field name="act_to"/>
- <field name="signal"/>
- </arrow>
- <label string="When customizing a workflow, be sure you do not modify an existing node or arrow, but rather add new nodes or arrows. If you absolutly need to modify a node or arrow, you can only change fields that are empty or set to the default value. If you don't do that, your customization will be overwrited at the next update or upgrade to a future version of Odoo."/>
- </diagram>
- </field>
- </record>
- <record id="view_workflow_tree" model="ir.ui.view">
- <field name="name">workflow.tree</field>
- <field name="model">workflow</field>
- <field name="arch" type="xml">
- <tree string="Workflow">
- <field name="name"/>
- <field name="osv"/>
- <field name="on_create"/>
- </tree>
- </field>
- </record>
-
- <record id="action_workflow_form" model="ir.actions.act_window">
- <field name="name">Workflows</field>
- <field name="res_model">workflow</field>
- <field name="view_type">form</field>
- <field name="view_id" ref="view_workflow_tree"/>
- <field name="view_mode">tree,form,diagram</field>
- </record>
- <menuitem action="action_workflow_form" id="menu_workflow" parent="base.menu_workflow_root"/>
-
- <!--
- ================================
- Activities
- ================================
- -->
-
- <record id="view_workflow_activity_form" model="ir.ui.view">
- <field name="name">workflow.activity.form</field>
- <field name="model">workflow.activity</field>
- <field name="arch" type="xml">
- <form string="Activity">
- <sheet>
- <group>
- <group>
- <field name="name"/>
- <field name="wkf_id"/>
- <field name="kind"/>
- </group>
- <group>
- <field name="flow_start"/>
- <field name="flow_stop"/>
- </group>
- </group>
- <notebook>
- <page string="Properties">
- <group>
- <group string="Subflow">
- <field name="subflow_id" attrs="{'readonly':[('kind','&lt;&gt;','subflow')]}"/>
- <field name="signal_send"/>
- </group>
- <group string="Conditions">
- <field name="split_mode"/>
- <field name="join_mode"/>
- </group>
- <group string="Actions">
- <field name="action_id"/>
- <field name="action" attrs="{'readonly':[('kind','=','dummy')]}"/>
- </group>
- </group>
- </page>
- <page string="Transitions">
- <field name="in_transitions">
- <tree string="Incoming Transitions">
- <field name="sequence" widget="handle" />
- <field name="act_from"/>
- <field name="signal"/>
- <field name="condition"/>
- </tree>
- </field>
- <field name="out_transitions">
- <tree string="Outgoing Transitions">
- <field name="sequence" widget="handle" />
- <field name="act_to"/>
- <field name="signal"/>
- <field name="condition"/>
- </tree>
- </field>
- </page>
- </notebook>
- </sheet>
- </form>
- </field>
- </record>
- <record id="view_workflow_activity_tree" model="ir.ui.view">
- <field name="name">workflow.activity.tree</field>
- <field name="model">workflow.activity</field>
- <field name="arch" type="xml">
- <tree string="Activity">
- <field name="name"/>
- <field name="wkf_id"/>
- <field name="kind"/>
- <field name="flow_start"/>
- <field name="flow_stop"/>
- </tree>
- </field>
- </record>
-
- <record id="view_workflow_activity_search" model="ir.ui.view">
- <field name="name">workflow.activity.search</field>
- <field name="model">workflow.activity</field>
- <field name="arch" type="xml">
- <search string="Workflow Activity">
- <field name="name" string="Workflow Activity"/>
- <filter string="Flow Start" domain="[('flow_start', '=',True)]"/>
- <filter string="Flow Stop" domain="[('flow_stop', '=',True)]"/>
- <field name="wkf_id"/>
- <field name="action_id"/>
- <field name="kind"/>
- <group expand="0" string="Group By">
- <filter string="Workflow" domain="[]" context="{'group_by':'wkf_id'}"/>
- </group>
- </search>
- </field>
- </record>
-
- <record id="action_workflow_activity_form" model="ir.actions.act_window">
- <field name="name">Activities</field>
- <field name="res_model">workflow.activity</field>
- <field name="view_type">form</field>
- <field name="view_id" ref="view_workflow_activity_tree"/>
- <field name="search_view_id" ref="view_workflow_activity_search"/>
- </record>
- <menuitem action="action_workflow_activity_form" id="menu_workflow_activity" parent="base.menu_workflow_root"/>
-
-
- <!--
- ================================
- Transitions
- ================================
- -->
-
- <record id="view_workflow_transition_form" model="ir.ui.view">
- <field name="name">workflow.transition.form</field>
- <field name="model">workflow.transition</field>
- <field name="arch" type="xml">
- <form string="Transition">
- <sheet>
- <group>
- <group>
- <field name="sequence" />
- <field name="act_from"/>
- <field name="act_to"/>
- <field name="signal"/>
- <field name="condition"/>
- </group>
- <group>
- <field name="group_id"/>
- <field name="trigger_model"/>
- <field name="trigger_expr_id"/>
- </group>
- </group>
- </sheet>
- </form>
- </field>
- </record>
- <record id="view_workflow_transition_tree" model="ir.ui.view">
- <field name="name">workflow.transition.tree</field>
- <field name="model">workflow.transition</field>
- <field name="arch" type="xml">
- <tree string="Transition">
- <field name="sequence" widget="handle" />
- <field name="act_from"/>
- <field name="act_to"/>
- <field name="signal"/>
- <field name="condition"/>
- </tree>
- </field>
- </record>
-
- <record id="view_workflow_transition_search" model="ir.ui.view">
- <field name="name">workflow.transition.search</field>
- <field name="model">workflow.transition</field>
- <field name="arch" type="xml">
- <search string="Transition">
- <field name="signal" filter_domain="['|', ('signal','ilike',self), ('condition','ilike',self)]" string="Workflow Transition"/>
- <field name="act_from"/>
- <field name="act_to"/>
- </search>
- </field>
- </record>
-
- <record id="action_workflow_transition_form" model="ir.actions.act_window">
- <field name="name">Transitions</field>
- <field name="res_model">workflow.transition</field>
- <field name="view_type">form</field>
- <field name="view_id" ref="view_workflow_transition_tree"/>
- <field name="search_view_id" ref="view_workflow_transition_search"/>
- </record>
- <menuitem action="action_workflow_transition_form" id="menu_workflow_transition" parent="base.menu_workflow_root"/>
-
- <!--
- ================================
- Instances
- ================================
- -->
-
- <record id="view_workflow_instance_form" model="ir.ui.view">
- <field name="name">workflow.instance.form</field>
- <field name="model">workflow.instance</field>
- <field name="arch" type="xml">
- <form string="Workflow Instances">
- <sheet>
- <group col="4">
- <field name="wkf_id"/>
- <field name="res_id"/>
- <field name="res_type"/>
- <field name="state"/>
- </group>
- </sheet>
- </form>
- </field>
- </record>
- <record id="view_workflow_instance_tree" model="ir.ui.view">
- <field name="name">workflow.instance.tree</field>
- <field name="model">workflow.instance</field>
- <field name="arch" type="xml">
- <tree string="Workflow Instances">
- <field name="wkf_id"/>
- <field name="res_id"/>
- <field name="res_type"/>
- <field name="state"/>
- </tree>
- </field>
- </record>
- <record id="view_workflow_instance_search" model="ir.ui.view">
- <field name="name">workflow.instance.search</field>
- <field name="model">workflow.instance</field>
- <field name="arch" type="xml">
- <search string="Workflow Instances">
- <field name="res_type" string="Resource Object"/>
- <filter string="Active" domain="[('state','=','active')]" name="active"/>
- <field name="wkf_id"/>
- <field name="res_id"/>
- <field name="state"/>
- </search>
- </field>
- </record>
-
- <record id="action_workflow_instance_form" model="ir.actions.act_window">
- <field name="name">Instances</field>
- <field name="res_model">workflow.instance</field>
- <field name="view_type">form</field>
- <field name="view_id" ref="view_workflow_instance_tree"/>
- <field name="context">{'search_default_active':1}</field>
- <field name="search_view_id" ref="view_workflow_instance_search"/>
- </record>
- <menuitem action="action_workflow_instance_form" id="menu_workflow_instance" parent="base.menu_workflow_root"/>
-
- <!--
- ================================
- Workitems
- ================================
- -->
-
- <record id="view_workflow_workitem_form" model="ir.ui.view">
- <field name="name">workflow.workitem.form</field>
- <field name="model">workflow.workitem</field>
- <field name="arch" type="xml">
- <form string="Workflow Workitems">
- <sheet>
- <group col="4">
- <field name="wkf_id"/>
- <field name="act_id"/>
- <field name="subflow_id"/>
- <field name="inst_id"/>
- <field name="state"/>
- </group>
- </sheet>
- </form>
- </field>
- </record>
- <record id="view_workflow_workitem_tree" model="ir.ui.view">
- <field name="name">workflow.workitem.tree</field>
- <field name="model">workflow.workitem</field>
- <field name="arch" type="xml">
- <tree string="Workflow Workitems">
- <field name="wkf_id"/>
- <field name="act_id"/>
- <field name="subflow_id"/>
- <field name="inst_id"/>
- <field name="state"/>
- </tree>
- </field>
- </record>
- <record id="view_workflow_workitem_search" model="ir.ui.view">
- <field name="name">workflow.workitem.search</field>
- <field name="model">workflow.workitem</field>
- <field name="arch" type="xml">
- <search string="Workflow Workitems">
- <field name="state" string="Status"/>
- <filter string="Active" name="active" domain="[('state','=','active')]"/>
- <field name="wkf_id"/>
- <field name="act_id"/>
- <field name="subflow_id"/>
- <field name="inst_id"/>
- </search>
- </field>
- </record>
-
- <record id="action_workflow_workitem_form" model="ir.actions.act_window">
- <field name="name">Workitems</field>
- <field name="res_model">workflow.workitem</field>
- <field name="view_type">form</field>
- <field name="view_id" ref="view_workflow_workitem_tree"/>
- <field name="context">{'search_default_active':1}</field>
- <field name="search_view_id" ref="view_workflow_workitem_search"/>
- </record>
- <menuitem action="action_workflow_workitem_form" id="menu_workflow_workitem" parent="base.menu_workflow_root"/>
-
- </data>
-</odoo>