前面幾天,我們陸續建立了:
Machine
Process
Route
Customer
一路做到這裡,每一個功能看起來都還算獨立。
Machine 負責定義有哪些機台。
Process 負責定義有哪些製程。
Route 把多個 Process 按照 SEQ 串成生產流程。
Customer 則記錄客戶與聯絡人資料。
今天要開始建立下一個重要的 Master Data:
Product(產品)。
做到 Product 之後,前面建立的基礎資料也開始慢慢有「組合起來」的感覺。
因為後面建立 Work Order 時,我們不能只告訴 MES:
我要生產 1000 PCS。
還必須知道:
到底要生產什麼?
先從最簡單的 Product 開始。
例如:
PRODUCT
-------------------------
PRODUCT_UUID
PRODUCT_CODE
PRODUCT_NAME
ENABLE
CREATE_TIME
UPDATE_TIME
建立幾個產品:
PDT-001 Product A
PDT-002 Product B
PDT-003 Product C
到這裡,其實跟 Machine 一樣。
所以真正值得看的,是:
Product 接下來會怎麼被其他資料使用。
假設我們現在有兩個產品:
Product A
Product B
從 MES 的角度來看,它們不能只是兩個名稱不同的字串,因為不同產品可能有不同的生產方式。
例如:
Product A
↓
Route A (① → ② → ③ → ④)
另外一個產品:
Product B
↓
Route B (① → ③ → ⑤)
雖然工廠裡使用的是同一套 MES,但是不同產品不一定走完全相同的生產流程。
所以當我們建立 Product 時,產品資料就開始跟前面建立的:
Route
產生關係。
假設前面已經建立兩條 Route:
ROUTE-A
10 → Die Attach
20 → Wire Bond
30 → Molding
40 → Inspection
以及:
ROUTE-B
10 → Die Attach
20 → Molding
30 → Inspection
接下來建立:
Product A
並指定:
Product A
↓
Route A
另外:
Product B
↓
Route B
這樣 Product 就不再只是一筆:
PRODUCT_CODE + PRODUCT_NAME
而開始帶有一部分:
這個產品應該怎麼生產。
的資訊。
這裡可能會有一個問題。
既然 Product 最後就是要經過:
Die Attach
Wire Bond
Molding
Inspection
那為什麼 Product 不直接綁 Process?
例如:
Product A
├── Die Attach
├── Wire Bond
├── Molding
└── Inspection
問題是:
只有 Process 還不夠,我們還需要知道順序。
Die Attach → Wire Bond → Molding → Inspection
而這件事情其實Route已經處理好了。
所以我們不需要在 Product 再重新定義一次。
而是:
Process
↓
Route
↓
Product
Route 負責管理:
製程有哪些、順序怎麼走。
Product 則直接使用已經建立好的 Route。
這也是為什麼前面的開發順序很重要。
而是直接跑來做 Product。
Product 畫面上出現:
產品代碼:PDT-001
產品名稱:Product A
生產途程:[ 請選擇 ]
問題馬上就來了。
下拉選單裡要放什麼?
因為:
ROUTE
根本還不存在。
當然我們可以先:
route_list = [
"ROUTE-A",
"ROUTE-B",
"ROUTE-C"
]
直接寫死。
也可以先建立幾筆假的測試資料。
但如果今天是從零開始建立一套 MES,我會更傾向:
先完成底層資料
↓
再讓上層功能使用
所以前面的開發順序才會是:
Machine
↓
Process
↓
Route
↓
Product
現在做到 Product 時,畫面上的 Route 選單終於可以真的從 Database 取得。
從使用者的角度來看:
Route:[ ROUTE-A ▼ ]
只是一個很普通的下拉選單。
但從研發的角度來看,它其實代表:
Product
│
└── depends on Route
也就是:
Dependency(依賴)。
Product 需要 Route。
所以建立 Product 時,後端不能只相信前端送過來的:
{
"product_code": "PDT-001",
"route_uuid": "ROUTE-UUID-001"
}
我們還需要確認:
ROUTE-UUID-001
真的存在。
有些人可能會覺得:
Route 都是從下拉選單選的,使用者又不能亂輸入,後端還需要檢查嗎?
還是需要。
因為 API 不一定只能從這個畫面呼叫。
而且在使用者打開畫面到按下儲存之間,資料狀態也可能已經發生變化。
例如:
使用者打開 Product 頁面
↓
載入 ROUTE-A
↓
另外一個使用者停用 ROUTE-A
↓
第一個使用者按下儲存
這時候前端手上的:
ROUTE-A
已經是舊資料。
所以後端還是需要重新確認:
route = get_route(data["route_uuid"])
if route is None:
raise Exception("Route not found.")
if not route.enable:
raise Exception("Route is disabled.")
這也是我在開發這類系統時很常碰到的一個觀念:
前端 Validation 是為了使用體驗,後端 Validation 才是最後一道資料防線。
Product 還有另外一個很重要的欄位:
PRODUCT_CODE
例如:
PDT-001
一開始它可能只是一個普通的產品代碼。
但是後面建立 Work Order、Lot、報表之後,Product 會開始被大量使用。
例如:
WO-001
↓
PDT-001
接著:
LOT-001
↓
WO-001
↓
PDT-001
這時候 Product 已經不再是一筆可以隨便處理的資料。
尤其如果直接把 Product 刪掉:
DELETE PDT-001
過去的 Work Order、Lot History、Report 還要不要查得到它?
這跟前面 Machine 遇到的問題一樣。
所以對已經被使用的 Master Data,我通常會比較傾向:
PDT-001
ENABLE = False
而不是直接從 Database 消失。
接下來才是 Product 比較有意思的地方。
假設:
Product A
↓
Route A
Route A 是:
① → ② → ③ → ④
今天工程人員修改 Product:
Product A
↓
Route B
Route B:
① → ③ → ⑤
對還沒開始生產的新工單來說,好像沒什麼問題。
但是如果現在工廠裡已經有:
LOT-001
LOT-002
LOT-003
正在按照:
Route A
生產呢?
難道 Product 一修改,這些 Lot 就突然全部改走 Route B?
通常不應該這麼簡單。
這裡其實跟上一篇 Route Update 遇到的問題非常像。
假設:
10/01
Product A → Route A
當天建立:
WO-001
開始生產。
到了:
10/15
Product A 改成:
Product A → Route B
那我們在 10/20 查看 WO-001 時,到底應該看到:
Route A
還是:
Route B
如果只去查 Product 現在的設定:
Product A → Route B
我們可能會錯誤地以為:
WO-001 當初也是按照 Route B 生產。
但實際上:
WO-001
建立當下使用的是:
Route A
這就是 MES 很重要的一個問題:
Current Configuration 不一定等於 Historical Configuration。
這篇先不急著深入實作。
因為等做到:
Work Order
以及:
Lot
時,這個問題會變得更明顯。
但現在可以先留下一個概念。
Product 是:
現在這個產品的設定
Work Order / Lot 則需要考慮:
當時生產時使用的是什麼設定
這兩件事情不能永遠假設完全相同。
否則 Master Data 一修改,歷史資料就可能跟著改變解讀方式。
現在我們已經有:
Customer
以及:
Product
很容易會想:
那是不是 Product 要直接綁 Customer?
例如:
客戶 A
↓
Product A
這就要看實際系統需求。
如果你的業務規則明確規定:
某個 Product 就是屬於某個 Customer。
那建立關係很合理。
但如果:
同一個 Product 可能提供給不同 Customer。
那直接綁死反而可能限制系統。
所以我不會因為:
「反正後面 Work Order 都會用到。」
就把所有 Master Data 先全部互相綁起來。
關聯越多,不代表系統設計越完整。
真正應該問的是:
這個關係是不是業務上真的存在?
如果真正需要 Customer + Product 的地方是在 Work Order:
Work Order
├── Customer
├── Product
└── Route
那就等 Work Order 再建立這個關係。
這樣每個 Master Data 的責任會比較清楚。
一路寫到 Product,我們可以重新看一下目前建立的東西。
最底層建立機台:
Machine
接著建立製程並與機台資料綁定:
Process
↕
Machine
再來途程引用製程並為他排序:
Route
↓
Process + SEQ
現在根據產品別,套用正確的途程:
Product
↓
Route
↓
Process
↓
Machine
前面花時間建立的資料,開始真的被上層功能使用。
這也是我選擇按照 Dependency 往上開發的原因。
不是因為:
Machine 比較重要,所以先做 Machine。
而是因為:
先把被依賴的東西建立好,上層功能才有真正的資料可以使用。
現在我們已經有:
Customer
Product
Route
Process
Machine
如果把這些資料想成積木,我們已經準備得差不多了。
但在真正建立 Work Order 以前,還有一個資料需要先處理:
Warehouse(倉庫)。
因為 Lot 不會永遠停留在製程裡。
生產完成之後,它可能進入:
完工倉
等待出貨時可能進入:
包裝倉
生產過程中也可能需要:
暫存倉
如果產品報廢,則可能進入:
報廢倉
而且除了系統定義的用途之外,使用者也可能建立自己的 Warehouse。
所以接下來,我們要開始處理另外一個問題:
Lot 不在製程裡的時候,它在哪裡?
下一篇:
「Lot 離開製程之後要去哪裡?」