Workflow: การปฏิบัติงานด้วย License Plate (LPN)
ตัวอย่าง workflow ตั้งแต่รับของจนถึงส่งของ ที่ใช้ LPN เป็นหน่วยติดตาม
1. รับเข้าเป็นพาเลท (Putaway → LPN)
ASN → GRN → Putaway Task
↓
[Operator scan LPN-... ที่ติดบนพาเลท]
↓
Putaway complete พร้อม lpnCode
↓
✓ Stock ใหม่ถูก tag ด้วย lpnId
✓ LPN.locationId = ตำแหน่งที่เก็บ
✓ LPN.status = activeEndpoint:
POST /putaway/:id/complete
{
"actualTo": "<location-uuid>",
"lpnCode": "LPN-20260601-000123",
"lpnType": "pallet"
}ถ้า lpnCode ยังไม่มีในระบบ จะถูกสร้างใหม่อัตโนมัติ
2. ย้ายพาเลทระหว่างโซน (Move)
LPN-...000123 อยู่ที่ STAGE-IN-01
↓
/lpn/:id/move toLocationId=RACK-A-01-02
↓
✓ LPN.locationId = RACK-A-01-02
✓ ทุก stock บน LPN ย้าย locationId พร้อมกัน
✓ ถ้ามี LPN ลูก (case บน pallet) ก็ cascade ย้ายตามไปด้วย
✓ บันทึก LpnMovement type='move'3. แยกพาเลทเพื่อ Replenish pick face (Split)
Bulk LPN มี SKU-A 100 ชิ้น ที่ BULK-01
↓
[Replenishment rule trigger]
↓
ระบบสร้าง task: ย้าย SKU-A 30 ชิ้น → PICK-A
↓
/replenishment/tasks/:id/complete
↓
✓ ต้นทาง LPN ยังเหลือ 70 ที่ BULK-01
✓ ปลายทาง PICK-A มีสต๊อกหลวม 30 (ไม่ผูก LPN)
✓ บันทึก LpnMovement type='split'หรือถ้า rule กำหนดว่าต้องย้ายทั้งพาเลท (qty = qty ทั้งใบ) ระบบจะ ย้าย LPN ทั้งใบ แทนการ split
4. Mixed Pallet (พาเลทผสม)
POST /lpn/build
{
"warehouseId": "...",
"type": "pallet",
"locationId": "STAGE-IN-01",
"items": [
{ "stockId": "stock-A", "qty": 50 },
{ "stockId": "stock-B", "qty": 30 },
{ "stockId": "stock-C", "qty": 20 }
]
}
↓
✓ LPN ใหม่ถูกสร้างพร้อมรหัส LPN-YYYYMMDD-NNNNNN
✓ 3 stock rows ใหม่ถูก tag ด้วย lpnId เดียวกัน
✓ น้ำหนัก/ปริมาตรถูกคำนวณรวมและบันทึก snapshot5. Pallet Hierarchy (Case บน Pallet)
Pallet LPN-P-001 ← parent
├── Case LPN-C-010 ← child (set parentLpnId=LPN-P-001's id)
├── Case LPN-C-011
└── Case LPN-C-012Endpoint:
POST /lpn/:childId/nest
{ "parentLpnId": "<parent-uuid>" }ผลที่ได้:
- เวลา move parent → ทุก child ตามไปด้วย
- เวลาดู detail ของ parent → แสดง list ของ child
- ระบบป้องกัน cycle (ไม่ให้ A nest ใต้ B ถ้า B เป็นลูกของ A อยู่แล้ว)
6. หยิบทั้งพาเลทแล้วส่ง (Pick + Ship)
Order → Wave → Pick Task (auto-allocate จาก LPN)
↓
Operator pick complete
↓
[ระบบเช็คว่า stock บน LPN เหลือ 0?]
↓
✓ ถ้าใช่ → LPN.status = 'empty'
↓
Shipment dispatch
↓
[ปิด LPN]
POST /lpn/:id/close → status='shipped'7. ดู Audit Trail
GET /lpn/:idResponse มี field movements (50 รายการล่าสุด เรียงใหม่ → เก่า) — แสดงทุก:
- build
- move (พร้อม from/to location)
- split (อ้างอิง LPN ปลายทางถ้าสร้างใหม่)
- merge (อ้างอิง LPN ปลายทาง)
- pick (อ้างอิง pick_task)
- replenish
- breakdown
- close
Best practices
- ออก LPN code ก่อน ถ้าเป็นไปได้ พิมพ์ label ล่วงหน้า แล้วใช้ Push API (
POST /integration/v1/lpn) ส่งเข้าระบบ — เมื่อ scan ที่ Putaway จะเจอ LPN ที่ pre-registered - ใช้ pallet standard (EUR-1200x800 เป็นต้น) เพื่อให้ระบบคำนวณ capacity ของ location ได้ถูก
- อย่า breakdown โดยไม่จำเป็น — Breakdown เป็น terminal state เปลี่ยนกลับไม่ได้ ถ้าแค่จะย้ายของไป LPN ใหม่ให้ใช้ Split + Merge แทน
- Mixed pallet ทำได้ แต่ทำให้ pick optimization ทำงานยากขึ้น — ใช้เมื่อจำเป็นจริงๆ