編輯導語:如今隨著互聯網的不斷發展,很多傳統的方式也逐漸朝著互聯網方向轉換,比如現在比較流行的電商行業,其中很多訂單狀態都是要通過各種系統實現;本文作者分享了關于自定義狀態流的訂單鎖定與釋放,我們一起來了解一下。
一、標準化狀態流與自定義狀態流
標準化狀態流是指類似于天貓,京東,蘇寧等主流電商的訂單狀態流,一個狀態要滿足一定的條件才能流轉到下一個狀態。
主流電商的訂單狀態流
之前在做海外倉的時候,無論是OMS還是WMS中,訂單的狀態流也都是提前預設好的,可以理解為也是一種“標準化”的狀態流,如下圖所示。
OMS和WMS的訂單狀態流
對于標準化的訂單狀態流,業內已經有很多很成熟的方案,所以在設計此類產品功能的時候,可以借鑒的資料和方案就比較多,也比較清晰。
例如電商后臺可配置“下單鎖庫存”還是“支付后鎖庫存”;OMS可以配置某些客戶“進單鎖庫存”,然后其他客戶“預報時所庫存”;WMS一般是“系統進單時鎖定庫存”,然后“分波時再鎖定一次庫存”……
而自定義訂單狀態流一般適用于SaaS類軟件,因為SaaS類軟件是做通用型產品,針對不同行業,不同玩法,需要提供比較靈活的自定義功能。
例如產品需求管理神器TAPD就是很典型的一款SaaS類軟件,不同行業,不同公司,不同團隊對于需求、迭代管理的方式都不一樣,所以TAPD提供了很靈活的自定義功能。
截圖自TAPD幫助文檔
TAPD自定義工作流
而對于SaaS類的外貿管理軟件(可以等同為B2B類進銷存軟件)來說,也會面臨相似的問題。不同的行業,對于訂單有不同的處理方式,也同樣需要靈活地自定義訂單的狀態流。
自定義訂單狀態流示意圖1
自定義訂單狀態流示意圖2
有一些公司是無庫存管理的,所有的訂單產品都需要采購后再發出(以銷定采)。有一些公司是有庫存管理的,部分產品需要采購,部分產品有庫存直接出庫。還有一些公司是工貿一體型,可以自己的工廠加工生產,然后交付相應的訂單……
所以不同的公司使用SaaS軟件的時候會配置出很多“獨具個性”的業務流程,而SaaS產品經理要做的就是抽象出其中最核心、最具代表性的業務特征,從而完成相應的“業務建模”和“系統建模”,以便于支撐復雜的、多變的業務場景。
二、自定義狀態流的特點
通過上面的簡單案例,我們會發現:對標準化狀態流的系統來說,訂單的一些操作或者邏輯的處理是可以通過狀態來控制的,在某個狀態可以做某些事情,然后會變換到另一個狀態。
而自定義狀態流的系統則比較難實現此功能,有可能A公司定義了5個狀態,而B公司則定義了8個狀態;A公司的狀態1可以直接切換到狀態3、狀態4、狀態5,B公司的狀態1,只能切換到狀態2,然后再從狀態2切換到狀態3,以此類推……
A公司的狀態切換示意圖
即使是A公司和B公司都定義了相同的狀態值,但是流轉的條件和邏輯不一樣,也會導致產生完全不同的工作流模式。
B公司的狀態切換示意圖
所以,自定義狀態流的訂單,很難在狀態這個字段上做一些判斷或者邏輯的處理,從而衍生出了一些跟標準化狀態流的不一樣的處理邏輯。
可以簡單理解為:時代變了,玩法不一樣的,不能直接套用2C電商的那一套玩法了。
三、標準化狀態流的訂單鎖定與釋放庫存
在剖析“自定義狀態流的訂單是如何鎖定和釋放庫存”這個問題之前,我們不妨先來看看標準化的狀態流的訂單鎖定與釋放庫存的過程是怎么樣的。
拿我最熟悉的跨境電商海外倉OMS和WMS這一套玩法來舉例,一般來說客戶從自己平臺或者ERP中推送訂單到OMS中,然后OMS預報面單之后再下推到WMS中,WMS作業出庫之后再將信息回傳到OMS,然后再推送給客戶自己的平臺或者ERP中。注意,這里的客戶是指電商賣家,而不是消費者。
其中OMS和WMS分別管理一套庫存,OMS的庫存可以理解為賬面庫存,WMS的庫存可以理解為實物庫存。對于外部的商家客戶來說,他們關注的是OMS端有多少庫存,因為這些庫存決定了在平臺上可以銷售的庫存是多少。
所以客戶的平臺訂單進入OMS之后,OMS需要鎖定這部分的庫存,避免客戶推送超量的訂單而庫存又不足,造成發貨延期的問題。這個時候鎖庫的節點是根據訂單的狀態來判斷的,例如可以設置進單鎖定,則意味著OMS接收到了客戶的平臺訂單之后,訂單狀態是「新建」,此刻就可以鎖定庫存,然后等到訂單狀態變成了「已出庫」之后,此時就可以釋放并扣減對應鎖定的庫存。
也可以設置預報之后再鎖定庫存,這里的預報指的是訂單狀態為「已預報」,釋放的邏輯也是「已出庫」之后再釋放……
無論是哪種鎖定方式,其實本質上都是根據訂單的狀態來判斷的,只要抓住這個核心邏輯,那么訂單的鎖定與釋放就不會很難。這也是市面上主流的解決方案,網絡上有很多相關的介紹資料,感興趣的朋友可以自行搜索。
四、自定義狀態流的訂單鎖定與釋放
1. 了解背景
有了前面鋪墊的知識背景之后,我們再來看看今天的主題:自定義狀態流的訂單如何鎖定與釋放庫存?
介紹一下背景,目前已有訂單模塊,但是沒有庫存模塊。訂單都是手動創建的合同訂單,類似于TAPD的需求一樣,可以任意的流轉狀態,但是不會產生實質性影響,例如影響庫存,影響外部系統的狀態等。
現在的需求是要引入庫存模塊,通過采購單可以創建入庫單,增加庫存;通過訂單可以創建出庫單,扣減庫存,而訂單除了要支持創建出庫單扣減庫存之外,還要考慮庫存鎖定和釋放的問題,因為可能會有多個業務員同時建單,但是出庫的時候發現庫存不足的情況,本文重點討論的就是關于訂單的庫存鎖定與釋放的問題。
起初接到這個任務的時候,我第一反應就是要么在創建訂單的時候就鎖定庫存,要么在創建出庫單的時候鎖定庫存,這樣是最簡單的。
但是隨著對業務場景的調研,我發現這兩種方案都存在一些問題:
- 由于外貿B2B訂單有很多都是通過郵件溝通的,而且訂單履約時間會特別長,為了管理整個銷售過程,業務員會提前創建好訂單,然后通過不同的狀態來判斷訂單進行到了哪一步。如果提前太久鎖定庫存,后期面臨訂單取消或者變更的時候,會造成庫存的積壓問題;
- 外貿商家有一些是無庫存經營的模式,有訂單了之后再去采購,然后采購了之后就直接發出了。通過創建訂單就鎖定庫存不具有普適性,可能還會引入負庫存的概念,比較麻煩;
- 如果是創建出庫單再鎖定庫存,那么在庫存鎖定之前可能會出現多個業務員爭搶庫存的情況,因為雙方在報價確認訂單的時候,看到系統的庫存都是有貨的,但是一旦誰先創建了出庫單鎖定了這部分的庫存,那么剩下的一方就會面臨庫存不足的情況;
2. 踩坑方案
在一開始的時候我并沒有清晰地認識到自定義狀態流有這么一些坑,所以我還是試著用2C電商的玩法來剖析這個問題。
狀態流雖然是自定義的,但是可以歸納為三個大類:
- 起始狀態;
- 中間狀態;
- 完結狀態;
訂單狀態歸納
只要是中間狀態,就可以鎖定訂單的庫存。如果訂單鎖定了庫存,在創建出庫單的時候,訂單鎖定的部分會流轉為出庫單鎖定;如果訂單沒有鎖定庫存,則創建出庫單時候,直接變成出庫單鎖定庫存。
訂單無鎖定庫存
訂單鎖定了庫存
通過上述的分析可以知道:
- 一個訂單可以創建多個出庫單,也就是多批次發貨;
- 訂單在創建出庫單的時候,可能已經鎖定了庫存,也可能沒有鎖定庫存;
- 訂單創建了出庫單之后,可以更改狀態(自定義工作流),可以從未鎖定轉為鎖定(因為配置了到某個狀態時則鎖定庫存),但是不可以從已鎖定轉為未鎖定,因為鎖定的釋放邏輯不是這樣的;
- 創建的出庫單可以刪除,也可以正常作業,直到出庫完成扣減庫存;
從未鎖定到鎖定狀態
以上方案看似沒有什么問題,但是最大的坑就隱藏在一旦后臺配置好了在某個節點鎖定庫存,那么接下來新增的后續的狀態也必須要配置鎖定,否則就會出現錯亂的問題。
如下圖所示,這個是一個理想的狀態流轉,除了起始和完結狀態之外,其他的中間狀態都可以鎖定庫存都可以創建出庫單,而且只能一步一步流轉,這樣就只會有“未鎖定轉為鎖定”的情況,當最后一步要轉為完結的時候,可以增加判斷條件“只有訂單明細的全部庫存都出庫釋放了”才能流轉到完結。
理想的自定義情況
其實這還是2C電商的玩法, 通過一些規則和配置,強制性的將自定義流轉的玩法變成了按要求流轉。
如下圖所示,當在狀態2沒有鎖定庫存的時候創建了1/3數量的出庫單,但是沒有出庫;等到了狀態3的時候,可以自動鎖定的數量就變成了2/3了;如果在狀態4的時候刪除了原來1/3數量的出庫單,那么流轉到狀態5的時候又要重新鎖定庫存,此刻可能會出現庫存不足情況,那么此訂單就不能及時完成發貨了。
不鎖庫的時候創建了出庫單,但是后續又刪除了
還是上面的例子,如果新增狀態5的時候忘記了配置自動鎖定庫存和支持創建出庫單,那么這個單到了狀態5的時候就不會鎖定庫存也不能支持創建出庫單了,訂單處于一個“死局”的狀態。
同時,如果配置狀態流的時候是支持一個狀態流轉到多個狀態,例如狀態1可以到狀態3、狀態4、狀態5這種,那么背后的判斷邏輯就會變得更加復雜。需要考量到每個狀態自身能做什么,然后考慮每個狀態能流轉到哪些狀態,會帶來什么樣的邏輯沖突等。
新增的狀態沒有配置鎖定和支持出庫
通過對自定義狀態流來配置不同的狀態節點可以支持鎖庫和創建出庫單,看似很美好,但是實操起來風險很大,加重了實施成本。而且一旦配置錯誤,后續一些鎖定的數據還需要手動運維解鎖處理,增加了很多運維的成本。
直覺告訴我,如果一個產品方案拆解到后面,越來越復雜,越來越亂,那么很有可能這個方案的源頭就出現了問題。因為好的產品解決方案應該是和寫出的代碼一樣,是“優雅的”,“簡潔的”,“清晰的”,如果不是,那么很有可能就是踩坑了。
3. 避坑方案
此方案是在研究了好幾個競品之后突然頓悟的,回過頭再想想其實解決方案也很簡單,類似于解數學題一樣:引入一個“中間變量”。
上面的踩坑方面看得大家一臉懵逼,腦子已經成了漿糊,現在我們跳出上面的解決方案來分析一下。我們面臨的問題是什么?我們應該去解決?
訂單要鎖定,是為了防止多個業務員同時需要某個庫存的時候產生爭議,所以鎖定的目的是為了提前分配,但是因為自定義狀態流的原因,系統不能根據狀態來自動鎖定相應的庫存從而分配給對應的訂單,那么系統不能做到的話,交給用戶自己手動去鎖定是否可行呢?當業務員覺得此訂單涉及的SKU庫存比較緊俏的時候,手動鎖定相應的數量,然后占為己有。此時,其他業務員也有類似的訂單的時候,可以看到可用庫存數量已經減少了,就知道自己的訂單可能庫存不足需要進行采購了。
此時,我們引入一個訂單鎖庫表的“中間變量”,也可以理解為手動占用表,這樣便于后續與出庫單鎖定作區分。當某個訂單快要談成的時候,業務員可以選擇手動鎖庫,提前占用相應的庫存,當然占用的數量不能大于訂單的數量,避免惡意占用。
訂單被提前占用了之后,當關聯了相應的出庫單時(創建銷售出庫單),出庫單會優先通過訂單號來查詢手動占用表,然后從手動占用表中讀取已占用的數量,將占用的數量轉為出庫單鎖定。然后出庫單正常出庫了之后,出庫單鎖定的庫存就會扣減掉,生成庫存流水。
訂單、鎖庫表、出庫單直接的關系銷售訂單可以創建鎖庫表(也可以叫做占用表),也可以創建出庫單。創建鎖庫表時可以手動輸入要鎖定的數量,鎖定類型為鎖庫表鎖定(手動占用)。創建出庫單時,出庫數量是多少則鎖定多少,如果直接出庫了則扣減庫存,這種形式的鎖定為出庫單鎖定。如果先創建鎖庫表,再創建出庫單,則出庫單的鎖定數量從鎖庫表中獲取。如果出庫數量大于鎖庫表,則鎖庫表自動釋放,鎖庫表鎖定的庫存全部轉為出庫鎖定;如果出庫數量小于鎖庫表,則鎖庫表部分釋放,釋放的部分轉為了出庫鎖定;剩余的鎖庫表鎖定數量可以手動釋放,也可以繼續創建出庫單,流轉到出庫鎖定。
如果先創建出庫單,再創建鎖庫表,則鎖庫表(手動占用)可鎖定的庫存數量也會相應的減少,因為出庫單占用了一部分的鎖定,不用擔心鎖庫表數量會超出出庫單的數量。
釋放有兩種方式:手動釋放和自動釋放。
手動釋放只能作用于鎖庫表,手動對鎖庫表的內容進行解鎖釋放。
自動釋放則可以針對鎖庫表和出庫單鎖定,鎖庫表可以通過出庫單來釋放鎖定,將鎖庫表鎖定轉為出庫單鎖定;出庫單鎖定則可以通過完成出庫扣減庫存的方式來釋放鎖定。
簡單概括上述方案就是:
- 手動對訂單創建一個“預占用池”,然后出庫單優先從這邊扣減庫存;
- 這個“預占用池”可以調整容量的大小,但是不能超出訂單數量;創建了出庫單就要從池子里放水,流到另一個池子;如果刪除了出庫單,則水又要裝回來;
從結論來看,此方案特別簡潔,而且便于理解和介紹;從分析過程看,沒有那么多彎彎繞繞,那么多異常分支;從操作體驗上看,讓用戶手動鎖庫,容易感知到庫存狀態的變化,也能減少實施的成本,而且也便于用戶理解鎖定的邏輯。
五、踩坑與總結
之前我一直在試著用2C電商的那一套玩法來設計產品方案,最后發現困難重重,邏輯復雜而且有很多漏洞。
我先入為主地將訂單的鎖定與釋放邏輯跟訂單的狀態掛鉤在一起,結果發現自定義狀態流的訂單中,狀態是一個完全不可控的東西,越是想要在這上面做文章,越會陷入一團亂麻中無法抽離。
直到后面試著引入一個鎖庫表之后,才發現原來這個方案缺了“一座橋”,一座跨在訂單和出庫單之間通過訂單號關聯的“橋”。有了這座“橋”,很多混亂的,交纏在一起的邏輯一下子就通了。
這個方案困擾了我很久,即使是我想出了鎖庫表這個方案之后,我還是不太死心,還是想試著重新推理一下,看看通過狀態是否真的無法解決這個問題。
不過遺憾的是,到目前為止我還是沒有找出比鎖庫表更好的解決方案。所以我決定將這個案例記錄下來,分享出來,看看是否有感興趣的朋友有其他更好的解法。
如果你對上述產品設計方案感興趣,歡迎留言更多業務背景和細節,討論更好的解決方案。
#專欄作家#
vitamin,也自稱“皮醬”,微信公眾號:皮醬叨逼叨。目前是一位外貿SaaS領域的供應鏈產品經理,曾做過3年半的跨境倉儲物流方向的產品,也是《人人都是產品經理》專欄作家。除了產品方面的知識,也會寫點工作總結,投資理財,讀書筆記和資料分享等……
本文原創發布于人人都是產品經理,未經作者許可,禁止轉載
題圖來自 Unsplash,基于CC0協議。