ใบจัดสินค้าสายส่งและขนส่ง
เอกสารนี้สรุป flow, action, status, API, เงื่อนไข และสิทธิ์ของ ใบจัดสินค้าสายส่ง และ ขนส่ง ตาม implementation ปัจจุบันของ Mobile และ POS Terminal
Scope
อ้างอิงจาก source หลัก:
apps/mobile/src/screens/shippingStack/shippingListScreenapps/mobile/src/screens/shippingStack/shippingDetailScreenapps/mobile/src/screens/transportStack/transportListScreenapps/mobile/src/screens/transportStack/transportDetailScreenapps/mobile/src/services/deliveries.tsapps/mobile/src/services/transport.tsapps/pos-terminal/src/screens/HomeScreen/ListPane.tsxapps/pos-terminal/src/screens/HomeScreen/ShippingRouteDetailForm.tsxapps/pos-terminal/src/screens/HomeScreen/TransportDetailForm.tsxapps/pos-terminal/src/screens/HomeScreen/helpers.tsapps/pos-terminal/src/services/splitMenuData.tsapps/pos-terminal/src/services/transport.ts
Mobile และ POS Terminal ใช้ rule เดียวกันสำหรับ flow หลัก ถ้าระบุว่า “ทั้งสองฝั่ง” หมายถึง Mobile และ POS Terminal
คำศัพท์หลัก
| คำ | ความหมาย |
|---|---|
| ใบจัดสินค้าสายส่ง | เอกสาร delivery route ที่รวม invoice/order สำหรับสายส่ง |
| ขนส่ง | transport trip ที่นำใบจัดหลายใบไปผูกกับทะเบียนรถและลูกค้า |
| เจ้าของใบจัด | userCode === detail.userCode |
| เจ้าของขนส่ง | userCode === transport.userCode |
| ยืนยันใบจัดสายส่ง | การตั้ง confirmedAt ผ่าน PATCH /deliveries/{id}/confirm |
| รับงานใบจัด | คลังรับงานผ่าน PATCH /deliveries/{id}/accept |
| อนุมัติใบจัดในขนส่ง | ขนส่งตั้ง deliveryStatus = FINISHED ให้ใบจัดรายใบ |
สถานะใบจัดสินค้าสายส่ง
| Field | ค่า | ความหมายที่ UI ใช้ |
|---|---|---|
status | DELETED | เอกสารถูกลบ/ยกเลิก ไม่ให้ action หลัก |
deliveryStatus | null, undefined, '' | กำลังเตรียมใบจัด |
deliveryStatus | FINISHED | ฝ่ายขายจัดใบสั่งขายเสร็จแล้ว หรือขนส่งอนุมัติใบจัดแล้ว |
deliveryStatus | CHECKLOCATION | คลังเช็คสินค้าแล้ว |
deliveryStatus | PENDING_FOR_B1 | รอ B1 ตัดสต๊อก |
deliveryStatus | SOME_DELIVERY_FAIL | มีข้อผิดพลาดในการตัดสต๊อก |
deliveryStatus | DONE | ตัดสต๊อกเรียบร้อย |
loadingStatus | empty | ยังไม่ยืนยันการโหลดสินค้า |
loadingStatus | LOADING_DOCK | จัดสินค้าไว้ที่ลานแล้ว |
loadingStatus | LOADED | จัดสินค้าขึ้นรถเรียบร้อย |
รายการใบจัดสินค้าสายส่ง
หน้ารายการแสดงข้อมูลหลัก:
- เลขที่ใบจัด:
deliveryNo - วันที่สร้าง:
createdAt - เส้นทาง:
routeName - พนักงานขาย:
createdBy - เวลา/สถานะยืนยันใบจัด:
- ถ้า
confirmedBy === "SYSTEM"แสดง(ข้อมูลเก่า {finishedAt}) - ถ้า
confirmedAtมีค่า แสดงเวลาconfirmedAt - ถ้า
confirmedAtเป็น'',null, หรือundefinedแสดง(ยังไม่ confirm)
- ถ้า
- พนักงานคลัง:
acceptedByName - เวลารับงานคลัง:
acceptedAt - พนักงานร่วมจัดสาย:
grpDriverName
Icon เพิ่มเติม:
loadingStatus === "LOADING_DOCK"แสดง icon ลานโหลดloadingStatus === "LOADED"แสดง icon รถ/โหลดขึ้นรถdeliveryStatus === "DONE"แสดง icon check
Key ของรายการ:
- Mobile ใช้
deliveryIdเป็น key หลัก - Mobile fallback เป็น
${deliveryNo ?? 'shipping'}-${index} - POS Terminal ใช้
SplitListItem.idที่ normalize จากdeliveryId - POS Terminal dedupe ด้วย
deliveryId; ถ้าไม่มี ใช้deliveryNo:{deliveryNo}
สิทธิ์เมนูใบจัดสายส่ง
เมนู ใบจัดส่งสินค้าสายส่ง แสดงเฉพาะ main branch และ MENU_SHIPPING.has(deptCode)
MENU_SHIPPING:
-2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 22, 27, 28, 13, 16, 14, 30
Shortcut สร้างใบจัดบน Mobile ใช้ CAN_CREATE_SHIPPING:
-2, 1, 2, 4, 7, 29, 21, 8, 9, 13, 16
สิทธิ์สร้าง/อนุมัติใบจัดใน helper ใช้ canFinalizeShippingSheet:
-2, 1, 2, 4, 7, 8, 9, 13, 16, 29, 30
Action ใบจัดสายส่ง
สร้างใบจัด
สร้างใบจัดด้วย invoice ที่เลือก:
POST /deliveries
payload เป็น array ของ invoice:
[
{ "invoiceId": 123 }
]
เพิ่ม invoice เข้าใบจัด
เพิ่ม invoice ได้เมื่อ:
- ผู้ใช้เป็นเจ้าของใบจัด:
userCode === detail.userCode detail.status !== "DELETED"confirmedAtยังว่าง
เมื่อมี confirmedAt แล้ว ห้ามเพิ่ม/แก้ไข/จัดเรียง invoice
ลบ invoice ออกจากใบจัด
ลบ invoice ได้เมื่อ:
- ผู้ใช้เป็นเจ้าของใบจัด:
userCode === detail.userCode detail.status !== "DELETED"confirmedAtยังว่าง- invoice นั้น
toB1 !== 1
API:
DELETE /deliveries/{deliveryId}/invoices/{invoiceId}
จัดเรียง invoice
จัดเรียงได้เมื่อ:
- ไม่ใช่ใบจัดที่ส่งเข้า transport แล้ว (
toTransportation !== 1) - ยังแก้ไข invoice ได้ตาม rule เจ้าของเอกสารและ
confirmedAtยังว่าง
API:
POST /deliveries/{deliveryId}
payload:
[123, 456, 789]
ยืนยันใบจัดสายส่ง
ปุ่ม ยืนยันใบจัดสายส่ง แสดงใน header เมื่อ:
- ผู้ใช้เป็นเจ้าของใบจัด
detail.status !== "DELETED"confirmedAtยังว่าง
ก่อนยืนยัน UI แสดง confirm alert:
หากยืนยันแล้ว จะไม่สามารถเพิ่ม แก้ไข หรือลบใบแจ้งหนี้ได้
API:
PATCH /deliveries/{deliveryId}/confirm
หลังสำเร็จ UI แจ้ง:
รอให้ขนส่งนำข้อมูลใบจัดเข้าขนส่ง และยืนยันใบ จึงจะดำเนินการต่อได้ หากต้องการแก้ไขใบจัดสายส่ง กรุณาให้คลัง เข้าไปปลดล็อคที่ขนส่ง
ผลของการ confirm:
- backend ตั้ง
confirmedAt,confirmedBy,confirmedByName - UI lock การเพิ่ม/แก้ไข/ลบ invoice
- ขนส่งสามารถดึงใบจัดนี้ไปเลือกได้ เพราะ transport picker filter
isConfirmed=1 - งานคลังสามารถรับงานได้ถ้าเงื่อนไขรับงานครบ
รับงานใบจัด
ปุ่มรับงานแสดง/auto prompt เมื่อ:
confirmedAtมีค่าtoTransportation === 1หรือ"1"- ยังไม่มี
acceptedBy - user อยู่ใน dept รับงาน
Dept รับงาน:
12, 22, 28
API:
PATCH /deliveries/{deliveryId}/accept
ยืนยันสินค้าที่ลานและขึ้นรถ
Dept ที่ทำ dock/loading ได้:
8, 12, 22, 28, 29
Action:
- ถ้ามี
deliveryStatusแล้วและยังไม่มีloadingStatusแสดงยืนยันการจัดสินค้าที่ลาน - เมื่อกด ส่ง
loadingStatus = LOADING_DOCK - ถ้า
loadingStatus === LOADING_DOCKแสดงยืนยันการจัดสินค้าขึ้นรถสำเร็จ - เมื่อกด ส่ง
loadingStatus = LOADED
API:
PATCH /deliveries/{deliveryId}/loading-dock
payload:
{ "loadingStatus": "LOADING_DOCK" }
หรือ:
{ "loadingStatus": "LOADED" }
ลงชื่อร่วมจัดสินค้า
ปุ่ม ลงชื่อร่วมจัดสินค้า แสดงเมื่อ:
loadingStatus === "LOADING_DOCK"- user อยู่ใน dept ลงชื่อร่วมจัด
Dept ลงชื่อร่วมจัด:
8, 12, 13, 14, 22, 28, 29, 30
API:
POST /deliveries/{deliveryId}/assign
payload:
{
"driverCode": "userCode",
"driverName": "ชื่อผู้ใช้"
}
ส่งข้อมูลไป B1 และแก้ lot
สิทธิ์ส่งไป B1:
2, 8, 12, 22, 28
แสดงเมื่อ:
detail.status !== "DELETED"deliveryStatusเป็นCHECKLOCATION,PENDING_FOR_B1, หรือSOME_DELIVERY_FAIL
ถ้า deliveryStatus เป็น PENDING_FOR_B1 หรือ SOME_DELIVERY_FAIL และมี invoice ที่ lot มีปัญหา UI แสดง action เลือก lot สินค้าที่มีปัญหาใหม่
ตรวจสินค้า
ตรวจสินค้าได้เมื่อ user เข้าเงื่อนไขใดเงื่อนไขหนึ่ง:
deptCode === 2deptCode === 8deptCode === 22userCode === "999997"acceptedByตรงกับ user ปัจจุบัน
ขอ cancel invoice
ทำได้เมื่อ:
deptCodeอยู่ใน12, 22, 28detail.deliveryStatus !== "DONE"- invoice ไม่มีส่วนลด:
discount <= 0 - invoice ไม่ใช้ credit note:
useCreditNote <= 0 - invoice ยังไม่ส่ง B1:
toB1 !== 1 - มี
invoiceId
ลบใบจัดจากหน้ารายการ
Mobile list และ POS list ใช้ rule ใกล้เคียงกัน:
- ถ้า
deliveryStatusเป็นnullหรือundefined: เจ้าของเอกสาร หรือ user ที่มีdelDelivery === 1ลบได้ - ถ้า
deliveryStatus === "FINISHED":deptCode === 16หรือ admin หรือเจ้าของเอกสารลบได้ - สถานะอื่นลบไม่ได้
สถานะขนส่ง
getTransportWorkflowStatus ใช้ค่า transportStatus หรือ status ก่อน ถ้าไม่มีจึง infer จาก timestamp:
| Workflow status | เงื่อนไข |
|---|---|
| empty | ยังไม่มี transportNo |
DRAFT | มี transportNo, ยังไม่ prepare/start |
PREPARING | เริ่มขนสินค้าขึ้นรถแล้ว |
IN_PROGRESS | มี startedAt |
FINISHED | มี finishedAt |
CANCELED / CANCELLED / CANCEL | ถือว่ายกเลิก |
Label ที่ UI ใช้:
| Status | Label |
|---|---|
DRAFT | เตรียมขนส่ง |
PREPARING | กำลังขนสินค้าขึ้นรถ |
IN_PROGRESS | กำลังขนส่ง |
FINISHED | ขนส่งเสร็จสิ้น |
| canceled | ยกเลิก |
สิทธิ์เมนูขนส่ง
เมนู ขนส่ง แสดงเฉพาะ main branch และ MENU_TRANSPORT.has(deptCode)
MENU_TRANSPORT:
-2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 13, 16, 14, 22, 27, 28, 30
Shortcut สร้างขนส่งบน Mobile ใช้ CAN_CREATE_TRANSPORT:
-2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 13, 16, 14, 30
Action ขนส่ง
สร้าง/แก้ไขขนส่ง
สร้าง:
POST /transports
แก้ไข:
PATCH /transports/{transportId}
field สำคัญ:
branchIdshipDatelicensePlatedriverNamewhGrpCode,whGrpNameuserCoderouteCode,routeNametransportStatusflagBank,flagGascompanyCode,companyName
Validation ขั้นต่ำ:
- ต้องมีทะเบียนรถ
- ต้องมีพนักงานขับรถ
- ต้องมีวันที่ขนส่ง
ผู้แก้ไขขนส่งได้:
- create mode: แก้ได้
- detail mode: ต้องเป็นเจ้าของขนส่ง (
userCode === transport.userCode)
เมื่อเริ่มงานแล้วหรือไม่ใช่ DRAFT step ข้อมูลหลักจะ lock
เลือกใบจัดเข้า transport
modal เลือกใบจัดเรียก:
GET /deliveries?page=1&pageLimit=32&sort=DESC&sortBy=CreatedAt&search={search}&branchId={branchId}&toTransportation=0&deliveryStatus=&status=ACTIVE&isConfirmed=1
เงื่อนไข:
- transport ยังเป็น create mode หรือ
DRAFT - ผู้ใช้แก้ไข transport ได้
- ต้องเลือกทะเบียนรถแล้ว
- ใบจัดต้องยังไม่ถูกผูกขนส่ง:
toTransportation=0 - ใบจัดต้อง active:
status=ACTIVE - ใบจัดต้องยืนยันแล้ว:
isConfirmed=1 - modal ค้นหาได้ทั้ง Mobile และ POS Terminal
ก่อนเปิด dialog ยืนยันการเริ่มขนสินค้าขึ้นรถ ระบบใช้ GET /deliveries ชุดเดียวกัน แต่ส่ง search='' และเพิ่ม routeCode={transportPayload.routeCode} เพื่อหาใบจัดของเส้นทางเดียวกันที่ยังไม่ถูกผูกกับรอบขนส่ง
หลังเลือกใบจัด:
POST /transports/{transportId}/deliveries
payload:
{ "deliveryId": 123 }
ลบใบจัดออกจาก transport
ลบได้เมื่อ:
- ผู้ใช้แก้ไข transport ได้
- transport ยังเป็น
DRAFT - ยังไม่
startedAt
API:
DELETE /transports/{transportId}/deliveries/{deliveryId}
อนุมัติใบจัดรายใบใน transport
ปุ่ม check รายใบแสดงเมื่อ:
- ผู้ใช้แก้ไข transport ได้
- transport เป็น
DRAFT - transport ไม่ถูกยกเลิก
- ใบจัดนั้น
deliveryStatusเป็น pending
pending delivery status หมายถึง:
nullundefined''- whitespace
- string
"null"หรือ"NULL"
API:
PATCH /deliveries/{deliveryId}/status
payload:
{ "deliveryStatus": "FINISHED" }
อนุมัติใบจัดทั้งหมด
ปุ่มล่าง อนุมัติใบจัดทั้งหมด แสดงเมื่อ:
- อยู่ step รายการใบจัด
- user แก้ไข transport ได้ หรือเป็น override user code
999999 - transport เป็น
DRAFT - transport ไม่ถูกยกเลิก
- มีใบจัดอย่างน้อย 1 ใบที่ยัง pending
เมื่อยืนยัน ระบบ loop ใบจัด pending ทุกใบ และเรียก API status เป็น FINISHED ทีละใบ
เริ่มขนสินค้าขึ้นรถ
ปุ่ม เริ่มขนสินค้าขึ้นรถ แสดงเมื่อ:
- transport ไม่ใช่ create mode
- user แก้ไข transport ได้ หรือเป็น override user code
999999 - transport เป็น
DRAFT - ใบจัดทุกใบมี
deliveryStatusแล้ว - transport ไม่ถูกยกเลิก
API:
POST /transports/{transportId}/prepare
หากไม่พบใบจัดที่เข้าเกณฑ์ จะแสดง dialog ยืนยันเดิม หากพบ จะแจ้งว่าเส้นทางนั้นยังมีใบจัดที่ยังไม่ถูกนำเข้ารอบ และผู้ใช้ต้องยืนยันอีกครั้งก่อนส่ง POST /transports/{transportId}/prepare ผลคือ transport เข้าสถานะ PREPARING
ปลดล็อคขนสินค้าขึ้นรถ
เมื่อ transport เป็น PREPARING และไม่ถูกยกเลิก UI แสดง action unlock
ผู้กดต้องมี deptCode:
8, 12, 22, 28, 29, 4, 2
ถ้าไม่มีสิทธิ์ UI แจ้ง:
คุณไม่มีสิทธิ์ปลดล็อค
ก่อน submit ต้องกรอกหมายเหตุ ถ้าไม่กรอก UI แจ้ง:
กรุณาระบุหมายเหตุ
API:
PATCH /transports/{transportId}/cancel-prepare
payload:
{ "note": "เหตุผลการปลดล็อค" }
ผลคือปลดจาก PREPARING กลับมาให้แก้ไข/จัดการก่อนเริ่มขนส่งต่อ
เริ่มการขนส่ง
ปุ่ม เริ่มการขนส่ง ใช้ได้เมื่อ:
- transport มี
transportId - มีทะเบียนรถ
- ใบจัดทุกใบมี
deliveryStatusแล้ว - transport เป็น
PREPARING - user แก้ไข transport ได้
ถ้าใบจัดยังไม่ครบสถานะ UI แจ้ง:
ใบจัดสายส่งทุกใบต้องถูกยืนยันก่อน จึงจะเริ่มขนส่งได้
ถ้ายังไม่ prepare UI แจ้ง:
กรุณาเริ่มขนสินค้าขึ้นรถก่อนเริ่มขนส่ง
API:
POST /transports/{transportId}/start
หรือ wrapper startTransport(...) ตาม payload ลูกค้า/พิกัด/ค่าขนส่งที่หน้าจอสร้างจากข้อมูล step ลูกค้า
รับชำระลูกค้า
ใน step ลูกค้า เมื่อเลือก action ชำระเงิน UI ใช้ payTransport
API:
POST /transports/{transportId}/pay
payload หลัก:
customerCodepaymentTypeamountcashtransfernotesU_BFP_Longitude,U_BFP_LatitudecurrentLatitude,currentLongitudesignatureslipPathshippingSlipPathphoto
ประเภท OTHER ต้องมี note, รูปส่งของ, และลายเซ็น ยกเว้น logic เฉพาะ transfer/bill payment ตามหน้าจอ
Before payTransport is called, evidence is uploaded separately through:
POST /api/transports/{transportId}/files
The multipart/form-data body contains files, whGrpCode, customerCode, U_BFP_Latitude, and U_BFP_Longitude. The last three customer fields come from the customer currently open in the modal and must all be present for every upload. If any is missing, the UI shows ไม่พบ customerCode หรือพิกัดลูกค้าสำหรับอัปโหลดไฟล์ and does not call the upload API. The returned path is then placed into photo, slipPath, shippingSlipPath, or signature in the payment payload.
เติมน้ำมัน
ใน step ลูกค้า footer มีรายการ เติมน้ำมัน
ถ้ายังไม่ completed และ user แก้ไข transport ได้:
- เปิด modal
จัดการเติมน้ำมัน - ปุ่ม
เติมน้ำมันเปิดฟอร์มบิลน้ำมัน - ปุ่ม
ไม่เติมน้ำมันupdateflagGas = 1
ฟอร์มบิลน้ำมันที่เปิดจากขนส่งจะตรวจ companyCode ก่อน submit โดยยอมรับเฉพาะ BFPDB, BFP, NFFDB และ NFF (ไม่สนตัวพิมพ์เล็ก/ใหญ่) เท่านั้น ค่าอื่น เช่น SSK จะถูกปฏิเสธก่อนเรียก API เพื่อไม่ให้ส่ง warehouse code เป็น company code
New transports always start with companyCode set to BFPDB. When a vehicle is selected, its companyCode replaces the transport value; if the vehicle has no company code, the transport keeps its existing value.
ก่อนจบ transport ถ้า flagGas === 0 UI แจ้ง:
คุณยังไม่จัดการเติมน้ำมัน
ฝากเงิน
ใน step ลูกค้า footer มีรายการ ฝากเงิน
ปัจจุบันซ่อนปุ่ม ฝากเงิน ไว้ แต่ comment โค้ดเดิมไว้เพื่อเปิดกลับได้ภายหลัง
เหลือ action ที่ใช้งาน:
- ปุ่ม
ไม่ฝากเงิน - เมื่อกด update
flagBank = 1
ก่อนจบ transport ถ้า flagBank === 0 UI แจ้ง:
คุณยังไม่จัดการฝากเงิน
สิ้นสุดการขนส่ง
ปุ่ม สิ้นสุดการขนส่ง แสดงเมื่อ:
- transport มี
startedAt - ยังไม่มี
finishedAt - user แก้ไข transport ได้
ก่อนจบต้องผ่าน guard:
- จำนวนงานที่ส่งเสร็จต้องครบ:
totalFinished === total flagBank !== 0flagGas !== 0- เลขไมล์สิ้นสุดต้องถูกต้อง
- เลขไมล์สิ้นสุดต้องมากกว่าเลขไมล์เดิม
- ถ้ามี mileage balance ห้ามเกินกำหนด
API:
PATCH /transports/{transportId}/finish
หลังสำเร็จ UI refresh แล้วกลับไปหน้า TransportList
ลบ transport จากรายการ
ลบ transport ได้เมื่อ:
- transport เป็น
DRAFT - เจ้าของ transport ตรงกับ user ปัจจุบัน
ถ้าไม่มี transportStatus จะถือว่า draft เมื่อยังไม่มี startedAt
รายการ API สำคัญ
Delivery
| Action | Method และ path |
|---|---|
| สร้างใบจัด | POST /deliveries |
| รายละเอียดใบจัด | GET /deliveries/{id} |
| ลบใบจัด | DELETE /deliveries/{id} |
| ยืนยันใบจัด | PATCH /deliveries/{id}/confirm |
| รับงานใบจัด | PATCH /deliveries/{id}/accept |
| update deliveryStatus | PATCH /deliveries/{id}/status |
| update loadingStatus | PATCH /deliveries/{id}/loading-dock |
| จัดเรียง invoice | POST /deliveries/{id} |
| ลบ invoice จากใบจัด | DELETE /deliveries/{id}/invoices/{invoiceId} |
| ลงชื่อร่วมจัด | POST /deliveries/{id}/assign |
| บันทึก lot | POST /deliveries/{id}/lots |
Transport
| Action | Method และ path |
|---|---|
| รายการขนส่ง | GET /transports |
| รายละเอียดขนส่ง | GET /transports/{id} |
| สร้างขนส่ง | POST /transports |
| แก้ไขขนส่ง | PATCH /transports/{id} |
| เลือกใบจัดเข้า transport | POST /transports/{id}/deliveries |
| ลบใบจัดออกจาก transport | DELETE /transports/{id}/deliveries/{deliveryId} |
| เริ่มขนสินค้าขึ้นรถ | PATCH /transports/{id}/prepare |
| ปลดล็อค prepare | PATCH /transports/{id}/cancel-prepare |
| เริ่มขนส่ง | POST /transports/{id}/start |
| รับชำระ | POST /transports/{id}/pay |
| จบขนส่ง | PATCH /transports/{id}/finish |
| invoice ทั้งหมดของ transport | GET /transports/{id}/invoices |
Parity ระหว่าง Mobile และ POS Terminal
สิ่งที่ต้องคงให้ตรงกัน:
- list card ใบจัดสายส่งใช้
confirmedAtสำหรับเวลายืนยัน - ถ้า
confirmedBy === "SYSTEM"แสดง(ข้อมูลเก่า {finishedAt}) - ถ้าไม่มี
confirmedAtแสดง(ยังไม่ confirm) - รับงานใบจัดต้องใช้
confirmedAtมีค่า ไม่ใช่confirmedAtว่าง - modal เลือกใบจัดในขนส่งต้องส่ง
isConfirmed=1 - modal เลือกใบจัดในขนส่งต้องค้นหาได้
- ปุ่มฝากเงินใน transport ถูกซ่อน เหลือ
ไม่ฝากเงิน - ปุ่ม unlock prepare ใช้ deptCode ชุดเดียวกัน
Maintenance checklist
เมื่อแก้ flow นี้ให้ตรวจ:
- Mobile shipping helper และ POS helper ให้ rule เท่ากัน
- Mobile transport detail และ POS
TransportDetailFormให้ action/guard เท่ากัน - service path ใน
deliveries.ts,transport.ts, และsplitMenuData.ts - list card ของ Mobile และ POS Terminal
- wiki หน้านี้ และ changelog ถ้าเปลี่ยน behavior ที่ผู้ใช้เห็น