簡介
工作流程系統在OpenERP裏是非常有用的機制,可以用於及時描述文件(模型)的演進過程。
工作流程是完全可以定製的,這些流程可以調整適用於幾乎所有公司的作業流程和交易邏輯。 這個工作流程系統使OpenERP非常有彈性, 而且可以不用編程增加新功能,就可以支持不斷變化的需要。
目標
- 及時描述文件的演進
- 符合某些條件時自動觸發動作
- 管理公司里人員的角色和驗證步驟
- 管理不同物件/模塊間的互動
- 爲文件流程可視化提供圖形化的工具
以下三個例子說明了工作流程系統的用途:
例子1 : 訂單折扣
第一個流程圖代表了一種非常基本的訂單流程:
訂單一開始在輸入但是還沒有被覈准前,是在’草稿’狀態。當使用者按下’確認’,系統會生成請款單,而且訂單會變成’已確認’狀態。
然後, 可能有2個動作:
- 訂單完成(已出貨)
- 訂單被取消
假設公司有一個需求還沒有在 OpenERP 裏實現,例如,銷售人員只能同意到最多 15% 的折扣,所有折扣大於 15% 的訂單必須由銷售經理覈准。
這種業務流程的修改需求不需要更改任何 Python 編碼! 只需要一個簡單的工作流程修改就可以讓我們把這個新的需求列入考慮,並且多增加一個驗證步驟。
工作流程依上面的圖裏修改以後,訂單就會依所要求的方式反應。然後我們只需要修改訂單的表單顯示方式,在想要的位置加上驗證的按鍵。
然後我們可以再深入強化這個工作流程-當訂單進入 ‘待驗證’ 狀態,主動通知銷售經理審覈訂單。工作流程的節點可以執行物件的程序;只要增加2行 Python 編碼,就可以通知銷售經理審覈或是取消訂單。
例子2: 業務訂單產生請款單及出貨通知
例子3: 客戶請款單基本工作流程
定義工作流程
工作流程是在以下檔案裏定義的 server/addons/base/ir/workflow/workflow.py。 在這個檔案裏定義的前3個級別(class)是 workflow,wkf_activity 和 wkf_transition。他們分別對應了工作流程裏3種需要的資源:
- 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" eval='True|False' />
</record>
</data>
</openerp>
這裏的
- id (就是 “workflow_id”) 是工作流程的識別碼。每個工作流程必須有一個唯一的識別碼。
- name (就是 “workflow.name”) 是工作流程的名稱。工作流程的名稱必須符合 OpenERP 文法的 “帶點名稱” 要求。
- osv (就是 “resource.model”) 是我們當做模型使用的物件名稱 [-(請記得 OpenERP 物件是從 osv.osv 繼承屬性,所以 ‘<字段名稱=”osv”>’)-]。
- on_create 如果爲 True,在創建 resource.model 時會自動將 workflow.name 實體化,如果是 False 則相反。
範例
定義在 addons/sale/sale_workflow.xml 裏的工作流程 sale.order.basic 完全依照這個模式,工作流程標籤的編碼如下:
<record model="workflow" id="wkf_sale">
<field name="name">sale.order.basic</field>
<field name="osv">sale.order</field>
<field name="on_create" eval='True' />
</record>
Activity(活動)
簡介
wkf_activity 這個級別(class)代表工作流程的節點。這些節點就是要被執行的行動。
字段
split_mode(拆分模式)
可能的值:
- XOR: 一個必須的轉變, 取找到的第一個轉變(預設值)。
- OR: 只依順序取有效的轉變(可能是0個或是多個)。
- AND: 所有有效的轉變都會同時出現(分叉)。
在 OR 和 AND 分離模式,可能會產生一些工作時間。
在 AND 模式,活動會等到所有轉變都生效纔會開始進行;即使是有小部分轉變還沒有生效,活動也不會開始進行。所有活動是同時被觸發的。
join_mode(結合模式)
可能的值:
- XOR: 進行目標的活動前,還必須繼續一個轉變(預設值)。
- AND: 等待所有轉變都生效,才能執行活動。
kind(種類)
可能的值:
- dummy: 不作任何事(預設值)。
- function: 執行一個行爲選擇的功能。
- subflow: 執行一個子工作流程 SUBFLOW_ID(子工作流程代碼)。這個行爲程序必須回覆子工作流程所需要的資源代碼。如果這個行爲程序回覆 False,這個工作項目就會消失。
- stopall: 全部停止
當某一個活動是 SUBFLOW 形態時,就會執行子工作流程。當子工作流程結束時活動也會結束。當子工作流程在作用中,這個活動的工作項目會被凍結。
action(行動)
行動是指當某一個工作項目來到這個活動時,所要執行的程序方法。這些程序方法必須在這個工作流程裏的物件裏有定義,而且具有以下特徵:
def object_method(self, cr, uid, ids):
在實際行動裏,這些程序方法會被以下的敘述方式呼叫:
object_method()
signal_send(送出信號)
這個字段是用於指定一個信號,當活動在作用中,這個信號就會被送往上一層的工作流程。如果要送出信號,將字段值設定爲信號的名字 (去掉開頭的 signal. )。
flow_start(流程開始)
標記這個節點是否爲開始節點。當創建一個工作流程的實例(instance)時,每一個活動都會啓動一個標記爲 n``flow_start``n (流程開始)的工作項目。
Warning
- 對所有的布爾型字段來說,當在你的XML資料裏寫入 <field> 標記時,務必使用 eval 屬性,
- 不可以使用文字節點屬性。詳細說明請參考:ref:eval attribute <eval-attribute-link>。
flow_stop(流程停止)
標記這個節點是否爲結束的節點。當一個實例(instance)裏所有作用中的工作項目來到標記爲``flow_stop``(流程停止)的節點,工作流程將會結束。
Warning
參考上面關於 flow_start (流程開始)的字段說明
- ::
- wkf_id(工作流程識別碼)
表示這個活動所屬的工作流程。
使用XML檔案定義活動
活動記錄的一般結構如下
<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" eval='True | False' />
<field name="flow_stop" eval='True | False' />
</record>
開始的**wkf_id**和**name**是強制要求要有的兩個參數。
範例
有太多時候可以從這個定義檔案裏選擇其中一項作爲活動的定義,所以我們建議大家看看以下檔案裏的幾個活動定義範例。server/addons/sale/sale_workflow.xml
Transition(轉變)
簡介
工作流程的轉變是指,一個活動要進行到下一個活動前,必須滿足的條件。轉變是用單向的箭頭代表,通常會連接前後兩個活動。
條件有以下幾種:
- 使用者必須符合某種角色要求
- 在使用界面裏按下某個按鈕
- 經由某個指定的活動達到這個子流程的結束點
系統是在表達式之前先判斷任務或信號是否成立,所以如果任務或信號爲僞(false),系統不會進行表達式的判斷。
轉變的判斷可能不會在物件裏寫入任何值。
字段
act_from(來源活動)
轉變的來源活動。當這個(來源)活動結束後,系統會檢查這個字段的狀態,來確認是不是可以開始進行 ACT_TO 活動。
act_to(目標活動)
轉變要進行到的目標活動
condition(狀態)
要滿足**表達式(Expression)** 才能完成轉變。
signal(信號)
當轉變的運作是來自於在使用者界面裏按下一個按鈕,信號會檢查被按下的按鈕的名稱。
如果信號爲空值(NULL),表示不需要任何按鈕來啓動這個轉變。
role_id(角色識別碼)
使用者必須符合某個**角色**才能啓動這個轉變
用 XML 檔案定義轉變
轉變記錄的一般結構如下
<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>
只有**act_from**和**act_to**這兩個字段是強制要求要有的。
Expressions(表達式)
表達式是以 Python 寫成的:
- True
- 1==1
- ‘hello’ in [‘hello’,’bye’]
工作流程指向的資源裏,任何字段都可以用在表達式裏。例如,如果想要爲夥伴地址建立一個工作流程,可以用類似以下的表達式:
- zip==1400
- phone==mobile
使用者角色
轉變可以附加角色要求。如果轉變指定角色要求,只有符合角色要求的使用者啓動轉變,轉變纔會進行。
每個使用者可以有一個或多個角色。角色會被定義在一個角色樹狀圖裏,上層(父層)的角色擁有所有下層(子層)的權力。
範例:
執行長
技術經理
- 研發組長
- 研發員
- 測試員
- 銷售經理
- 廣告人員
- ...
假設我們要查找數據庫裏的錯誤,需要測試員的角色才能在找到的錯誤上做標示,在上述範例的角色樹裏,有以下角色的人都可以在找到的錯誤上做標示:測試員,研發組長,技術經理,執行長。
錯誤處理
在以下的敘述中,工作流程裏沒有包含錯誤處理。
如果工作流程是批量執行的動作組成的,就不會觸發例外狀況。爲了提升執行效率和儘量不被鎖住,工作流程在每一個活動結束時才提交一個結果。這個策略是合理的,因爲在每一個動作要求的條件被滿足後,活動纔會被執行。
唯一可能出現例外狀況的問題是編程上的錯誤;這種狀況下,只有屬於整個已經執行完成的活動的動作纔會被執行,其他的活動會被回退到上個檢查點。
創建一個工作流程
以下步驟是用於創建一個名爲**mymod**的定製模塊,是一個簡單的改變狀態的工作流程
定義你的物件的狀態
第一步是定義你的物件可以有那些狀態。我們在物件的欄目(_columns)集合里加上一個 ‘state’ 字段,用於定義物件的狀態。
_columns = {
...
'state': fields.selection([
('new','New'),
('assigned','Assigned'),
('negotiation','Negotiation'),
('won','Won'),
('lost','Lost')], 'Stage', readonly=True),
}
定義狀態改變的處理方式
在你的物件裏增加以下額外的處理方法,我們的工作流程裏的按鈕會呼叫這些方法。
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
顯然你以後會想把這些方法擴充改成執行更有用的事項!
創建你的工作流程XML檔案
我們在 mymod_workflow.xml 這個檔案裏需要定義3個種類的記錄。
- 工作流程標題記錄
<record model="workflow" id="wkf_mymod"> <field name="name">mymod.wkf</field> <field name="osv">mymod.mymod</field> <field name="on_create" eval='True' /> </record>
- 工作流程活動記錄
這種記錄是在定義工作流程到達某個特定狀態時,必須執行的動作
<record model="workflow.activity" id="act_new"> <field name="wkf_id" ref="wkf_mymod" /> <field name="flow_start" eval='True' /> <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" eval='True' /> </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" eval='True' /> </record>
- 工作流程轉變記錄
這種記錄是在定義工作流程的狀態間,可能的轉變
<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>
把 mymod_workflow.xml 加到 __openerp__.py 裏
修改你的模塊裏的 __openerp__.py ;把 "mymod_workflow.xml" 加到 update_xml 陣列,這樣下一次 OpenERP 載入你的模塊時,就會抓取這些修改。
在你的視圖裏加上工作流程按鈕
最後一個步驟是把需要的按鈕加到 mymod_views.xml 檔案裏。
在你的物件的視圖定義裏, <form> 部分的最後一段,加上以下程序:
<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>
測試
現在可以用模塊管理員(Module Manager)來安裝或更新你的模塊。如果你有正確地完成所有事項,應該不會出現任何錯誤。你可以檢查你的工作流程是不是有被安裝在菜單裏:選擇菜單(menuselection):管理(Administration) –> 定製(Customization) –> 工作流程定義(Workflow Definitions).
當你進行測試時,記得新加入的工作流程只會被應用在新創建的記錄上。
故障排除
如果你的按鈕看起來沒有任何作用,或許是因爲以下兩個問題的其中一個:
- 你正在處理的記錄沒有連接到工作流程實例(Instance)記錄(也許是這筆記錄是在你定義你的工作流程以前創建的)
- 你在你的工作流程 XML 檔案裏沒有正確設定 osv 這個字段