在前一篇我們已經成功地建立簽核表單及簽核節點並關聯回請假表單,而本篇會接著介紹如何管理簽核節點狀態並同步更新簽核表單狀態。
我們來回顧一下data model,簽核表單(sign form)與簽核節點(sign node)是一對多關係,每個節點的狀態是獨立的,節點本身無法得知自己是不是下一個要被簽核的對象。
但是從簽核表單(sign form)的視角來看,只要加上next_sign_node、last_sign_node兩個欄位,當節點狀態改變時同步更新這兩個欄位,就可以快速查找簽核進行到哪一關啦。
class SignForm(Model):
objects = Manager()
status = FSMField()
user = ForeignKey()
next_sign_node = ForeignKey()
last_sign_node = ForeignKey()
def update_sign_nodes(self):
nodes = self.sign_nodes.order_by('order')
last_sign_node = None
next_sign_node = None
for node in nodes:
if node.result in [SignResult.APPROVED, SignResult.REJECTED]:
last_sign_node = node
elif node.result == SignResult.NONE and not next_sign_node:
if last_sign_node or not last_sign_node:
next_sign_node = node
if self.last_sign_node != last_sign_node or self.next_sign_node != next_sign_node:
self.last_sign_node = last_sign_node
self.next_sign_node = next_sign_node
self.save(update_fields=['last_sign_node', 'next_sign_node'])
if all(node.result == SignResult.APPROVED for node in nodes):
self.approve()
elif any(node.result == SignResult.REJECTED for node in nodes):
self.reject()
在簽核表單(sign form)加上update_sign_nodes的函式,當簽核節點(sign node)被簽核人員同意或是退回時呼叫,這樣一來就可以非常方便的紀錄目前簽核到哪一個節點或是整張表單已經完成簽核了。
class SignNode(Model):
sign_form = ForeignKey()
work_flow_node = ForeignKey()
agent = ForeignKey()
signer = ForeignKey()
result = FSMField()
date = DateTimeField()
note = TextField()
order = PositiveIntegerField()
class Meta:
ordering = ['order']
@transition()
def approve(self, signer, note):
self.result = SignResult.APPROVED
self.save(update_fields=['result'])
self.sign_form.update_sign_nodes()
@transition()
def reject(self, signer, note):
self.result = SignResult.REJECTED
self.save(update_fields=['result'])
self.sign_form.update_sign_nodes()
使用簽核表單(sign form)紀錄next_sign_node額外的好處是,在搜尋時可以讓query減少一層。
ORM雖然效能不佳,但在實作中低流量的ERP系統時,開發效率還是蠻不錯的。
簽核表單真的是蠻複雜的功能,基本的順簽流程都已經如此費工,更大型的ERP甚至會導入更進階的多方會簽、逐層退回等等功能,非常考驗開發者的實作經驗。