This is the documentation for older versions of Odoo (formerly OpenERP).
Рабочие и бизнес-процессы¶
Введение¶
Рабочие процессы в OpenERP это очень мощный механизм, который описывает эволюцию документа (модели) во времени.
Workflows are entirely customizable, they can be adapted to the flows and trade logic of almost any company. The workflow system makes OpenERP very flexible and allows it to easily support changing needs without having to program new functionalities.
Цели
описание эволюции документа во времени
автоматический вызов событий если соблюдены некоторые условия
управление ролями в компании и шагами проверки
управление взаимодействием между разными объектами/модулями
графический инструмент для визуализации потоков документа
Чтобы понять пользу от этого, посмотрите на три примера:
Пример 1: Скидка на заказ¶
Первая диаграмма The first diagram представляет очень простой рабочий процесс заказа:

Заказ начинается в стадии "черновик", когда он в процессе редактирования и еще не подтвержден. Когда пользователь нажимает на кнопку "Подтвердить", создается счет и заказ становится со статусом "Подтвержден".
Далее возможны две операции:
заказ выполнен (доставлен)
заказ отменен
Давайте предположим что компания имеет потребность, не реализованную в OpenERP. Например, предположим менеджеры по продажам могут только предлагать скидки 15% или меньше. Каждый заказ, имеющий скидку более 15% должен быть утвержден директором по продажам.
Это изменение в логике продаж не требует ни одной линии кода python! Простое изменение в рабочем процессе позволяет нам принять эту новую потребность во внимание и добавить дополнительный шаг проверки.

Этот рабочий процесс изменен таким образом как показано выше и заказы будут реагировать так как мы этого хотим. Далее нам только нужно изменить форму заказа и добавить кнопку проверки в нужное место.
Потом мы пожем улучшить этот рабочий процесс с помощью отсылки запроса директору по продажам когда заказ входит в состояние "Проверка". Узлы процесса могут инициализировать методы объекта; только две строки когда Python нужны для отправки запроса директору по продажам на утверждение или отмену этого заказа.
Пример 2: Заказ на продажу который генерирует счет и заказ на доставку.¶

WkfExample3: Acount invoice basic workflow

Определение рабочего процесса¶
Рабочие процессы определены в файле server/bin/addons/base/ir/workflow/workflow.py. Первые три класса, определенные в этом файле, это workflow, wkf_activity и wkf_transition. Они соответствуют трем типов ресурсов которые необходимы для описания рабочего процесса:
workflow : рабочий процесс,
wkf_activity : действия (узлы),
wkf_transition : переходы между действиями.
Общая структура XML файла рабочего процесса¶
Общая структура XML файла рабочего процесса выглядит следующим образом:
<?xml version="1.0"?>
<openerp>
<data>
<record model="workflow" id=workflow_id>
<field name="name">workflow.name</field>
<field name="osv">resource.model</field>
<field name="on_create">True | False</field>
</record>
</data>
</openerp>
Где
id (здесь "workflow_id") это идентификатор рабочего процесса. Каждый рабочий процесс должен иметь уникальный идентификатор.
name (здесь "workflow.name") это название рабочего процесса. Название должно придерживаться синтаксиса OpenERP "названия через точку".
osv (здесь "resource.model") это название объекта который мы используем в качестве модели [-(Помните, объект OpenERP наследуется от osv.osv, следовательно '<field name="osv">')-].
on_create принимает значение True если workflow.name должно создаваться автоматически при создании resource.model, и значение False в ином случае.
Пример
Рабочий процесс "sale.order.basic" определенный в addons/sale/sale_workflow.xml следует точно такой модели, вот код определения этого процесса:
<record model="workflow" id="wkf_sale">
<field name="name">sale.order.basic</field>
<field name="osv">sale.order</field>
<field name="on_create">True</field>
</record>
Действие¶
Введение¶
Класс wkf_activity представляет узлы рабочих процессов. Эти узлы являются действими, которые должны быть выполнены.
Source activity. When this activity is over, the condition is tested to determine if we can start the ACT_TO activity.¶
split_mode¶

XOR: Один необходимый переход, принимает первое найденное (по умолчанию).
OR : Принимает только допустимые переходы (0 или больше) в последовательном порядке.
AND: Все допустимые переходы выполняются одновременно.
В режимах OR и AND могут быть сгенерированы определенные рабочие элементы.
В режиме AND, действие ждет чтобы все переходы были допустимыми, даже если некоторые из них уже допустимы. Все они вызываются одновременно.
join_mode¶

XOR: Один переход необходим для продолжения к итоговому действию (по умолчанию).
AND: Ожидает чтобы все условия перехода были допустимы для выполнения итогового действия.
kind:¶
The type of the activity can take several values: | |
---|---|
|
Подпроцесс выполняется когда действие имеет тип SUBFLOW. Это действие завершается когда подпроцесс выполнен. Пока подпроцесс активен, рабочий элемент этого действия остается замороженным.
action:¶
Действие указывает метод, который выполняется когда рабочий элемент вступает в эту деятельность. Этот метод должен быть определен в объекте который принадлежит этому рабочему процессы и выглядит следующим образом:
def object_method(self, cr, uid, ids):
Хотя в действии они будут вызываться так:
object_method()
signal_send¶
flow_start¶
Указывает, является ли узел начальным узлом. Когда новый экземпляр рабочего процесса создан, рабочий элемент активируется для каждого действия помеченного как flow_start.
Предупреждение
Будьте осторожны, чтобы не использовать этот флаг, если ваше действие в действительности не является "flow start". Есть крошечные версии, которые не заботятся насчет содержания тегов как "true" или "false". Используя такие теги и крошечную версию, вы будете всегда в конечном счете с действием которое помечено как "flow start = true", оставляя вас охотиться в поисках где ваш рабочий процесс может быть неправилен.
Это происходит потому что содержимое тегов всегда определяется как строка. Прочитайте секцию eval attribute где это объяснено.
flow_stop¶
Указывает, является ли узел конечным узлом. Когда все активные рабочие элементы для данного экземпляра приходят в узел помеченный как flow_stop, рабочий процесс закончен.
Предупреждение
Be warned to not use this flag unless your activity really is a "flow stop". There are tiny versions that do not care about the tags contents like "true" or "false". Using such tag and tiny version, you will always end up with an activity which is tagged as "flow stop = true", leaving u with a nasty hunt to find out where your workflow design could be wrong.
Это происходит потому что содержимое тегов всегда определяется как строка. Прочитайте секцию eval attribute где это объяснено.
wkf_id¶
The workflow which this activity belongs to.
Defining activities using XML files¶
The general structure of an activity record is as follows
<record model="workflow.activity" id="''activity_id''">
<field name="wkf_id" ref="''workflow_id''"/>
<field name="name">''activity.name''</field>::
<field name="split_mode">XOR | OR | AND</field>
<field name="join_mode">XOR | AND</field>
<field name="kind">dummy | function | subflow | stopall</field>
<field name="action">''(...)''</field>
<field name="signal_send">''(...)''</field>
<field name="flow_start">True | False</field>
<field name="flow_stop">True | False</field>
</record>
The first two arguments wkf_id and name are mandatory.
Предупреждение
Be warned to not use flow_start and flow_stop unless your activity really is a flow start or flow_stop. There are tiny versions that do not care about the tags contents like "True" or "False".
Это происходит потому что содержимое тегов всегда определяется как строка. Прочитайте секцию eval attribute где это объяснено.
Examples¶
There are too many possibilities of activity definition to choose from using this definition. We recommend you to have a look at the file server/bin/addons/sale/sale_workflow.xml for several examples of activity definitions.
Transition¶
Введение¶
The conditions are of different types:
role to satisfy by the user
button pressed in the interface
end of a subflow through a selected activity of subflow
The roles and signals are evaluated before the expression. If a role or a signal is false, the expression will not be evaluated.
Transition tests may not write values in objects.
The fields
Source activity. When this activity is over, the condition is tested to determine if we can start the ACT_TO activity.¶
act_from
Expression to be satisfied if we want the transition done.
act_to
When the operation of transition comes from a button pressed in the client form, signal tests the name of the pressed button.
condition
If signal is NULL, no button is necessary to validate this transition.
signal
The role that a user must have to validate this transition.
Defining Transitions Using XML Files
role_id
The general structure of a transition record is as follows
Only the fields act_from and act_to are mandatory.¶
Expressions
<record model="workflow.transition" id="transition_id">
<field name="act_from" ref="activity_id'_1_'"/>
<field name="act_to" ref="activity_id'_2_'"/>
<field name="signal">(...)</field>
<field name="role_id" ref="role_id'_1_'"/>
<field name="condition">(...)</field>
<field name="trigger_model">(...)</field>
<field name="trigger_expr_id">(...)</field>
</record>
Expressions are written as in python:
True¶
1==1
'hello' in ['hello','bye']
Any field from the resource the workflow refers to can be used in these expressions. For example, if you were creating a workflow for partner addresses, you could use expressions like:
zip==1400
phone==mobile
User Role
Roles can be attached to transitions. If a role is given for a transition, that transition can only be executed if the user who triggered it possess the necessary role.
Each user can have one or several roles. Roles are defined in a tree of roles, parent roles having the rights of all their children.¶
Example:
CEO
Technical manager
Lead developer
Developers
Testers
Sales manager
Commercials
Sales manager
...
Error handling
As of this writing, there is no exception handling in workflows.
Workflows being made of several actions executed in batch, they can't trigger exceptions. In order to improve the execution efficiency and to release a maximum of locks, workflows commit at the end of each activity. This approach is reasonable because an activity is only started if the conditions of the transactions are satisfied.¶
The only problem comes from exceptions due to programming errors; in that case, only transactions belonging to the entirely completed activities are executed. Other transactions are "rolled back".
Creating a Workflow
Steps for creating a simple state-changing workflow for a custom module called mymod
Define the States of your object¶
The first step is to define the States your object can be in. We do this by adding a 'state' field to our object, in the _columns collection
Define the State-change Handling Methods¶
Add the following additional methods to your object. These will be called by our workflow buttons
_columns = {
...
'state': fields.selection([
('new','New'),
('assigned','Assigned'),
('negotiation','Negotiation'),
('won','Won'),
('lost','Lost')], 'Stage', readonly=True),
}
Obviously you would extend these methods in the future to do something more useful!¶
Create your Workflow XML file
def mymod_new(self, cr, uid, ids):
self.write(cr, uid, ids, { 'state' : 'new' })
return True
def mymod_assigned(self, cr, uid, ids):
self.write(cr, uid, ids, { 'state' : 'assigned' })
return True
def mymod_negotiation(self, cr, uid, ids):
self.write(cr, uid, ids, { 'state' : 'negotiation' })
return True
def mymod_won(self, cr, uid, ids):
self.write(cr, uid, ids, { 'state' : 'won' })
return True
def mymod_lost(self, cr, uid, ids):
self.write(cr, uid, ids, { 'state' : 'lost' })
return True
There are three types of records we need to define in a file called mymod_workflow.xml
Workflow header record (only one of these)¶
Workflow Activity records
These define the actions that should be executed when the workflow reaches a particular state
<record model="workflow" id="wkf_mymod"> <field name="name">mymod.wkf</field> <field name="osv">mymod.mymod</field> <field name="on_create">True</field> </record>
Workflow Transition records
These define the possible transitions between workflow states
<record model="workflow.activity" id="act_new"> <field name="wkf_id" ref="wkf_mymod" /> <field name="flow_start">True</field> <field name="name">new</field> <field name="kind">function</field> <field name="action">mymod_new()</field> </record> <record model="workflow.activity" id="act_assigned"> <field name="wkf_id" ref="wkf_mymod" /> <field name="name">assigned</field> <field name="kind">function</field> <field name="action">mymod_assigned()</field> </record> <record model="workflow.activity" id="act_negotiation"> <field name="wkf_id" ref="wkf_mymod" /> <field name="name">negotiation</field> <field name="kind">function</field> <field name="action">mymod_negotiation()</field> </record> <record model="workflow.activity" id="act_won"> <field name="wkf_id" ref="wkf_mymod" /> <field name="name">won</field> <field name="kind">function</field> <field name="action">mymod_won()</field> <field name="flow_stop">True</field> </record> <record model="workflow.activity" id="act_lost"> <field name="wkf_id" ref="wkf_mymod" /> <field name="name">lost</field> <field name="kind">function</field> <field name="action">mymod_lost()</field> <field name="flow_stop">True</field> </record>
Add mymod_workflow.xml to __openerp__.py
Edit your module's __openerp__.py and add mymod_workflow.xml to the "update_xml" array, so that OpenERP picks it up next time your module is loaded.
<record model="workflow.transition" id="t1"> <field name="act_from" ref="act_new" /> <field name="act_to" ref="act_assigned" /> <field name="signal">mymod_assigned</field> </record> <record model="workflow.transition" id="t2"> <field name="act_from" ref="act_assigned" /> <field name="act_to" ref="act_negotiation" /> <field name="signal">mymod_negotiation</field> </record> <record model="workflow.transition" id="t3"> <field name="act_from" ref="act_negotiation" /> <field name="act_to" ref="act_won" /> <field name="signal">mymod_won</field> </record> <record model="workflow.transition" id="t4"> <field name="act_from" ref="act_negotiation" /> <field name="act_to" ref="act_lost" /> <field name="signal">mymod_lost</field> </record>
Add mymod_workflow.xml to __terp__.py¶
Edit your module's __terp__.py and add mymod_workflow.xml to the "update_xml" array, so that OpenERP picks it up next time your module is loaded.
Add the following at the end of the <form> section of your object's view definition:¶
Testing
Now use the Module Manager to install or update your module. If you have done everything correctly you shouldn't get any errors. You can check if your workflow is installed in the menu Administration ‣ Customization ‣ Workflow Definitions.
<separator string="Workflow Actions" colspan="4"/> <group colspan="4" col="3"> <button name="mymod_assigned" string="Assigned" states="new" /> <button name="mymod_negotiation" string="In Negotiation" states="assigned" /> <button name="mymod_won" string="Won" states="negotiating" /> <button name="mymod_lost" string="Lost" states="negotiating" /> </group>
When you are testing, remember that the workflow will only apply to NEW records that you create.¶
Troubleshooting
If your buttons do not seem to be doing anything, one of the following two things are likely:
The record you are working on does not have a Workflow Instance record associated with it (it was probably created before you defined your workflow)¶
You have not set the "osv" field correctly in your workflow XML file
The record you are working on does not have a Workflow Instance record associated with it (it was probably created before you defined your workflow)
You have not set the "osv" field correctly in your workflow XML file