ขนส่ง
เมนูขนส่งเป็นหน้าทำงานของรอบส่งสินค้าหลังจากมีใบจัดสายส่งแล้ว หน้านี้ใช้สร้างหัวรอบขนส่ง ดึงใบจัดสายส่งเข้ารอบ ยืนยันว่าใบจัดพร้อม เริ่มขั้นตอนขนสินค้าขึ้นรถ เริ่มออกขนส่ง จัดการลูกค้าแต่ละจุด รับชำระเงิน/ค่าขนส่ง เก็บหลักฐาน แล้วปิดรอบเมื่อทำฝากเงินและน้ำมันครบ
ให้ยึด Mobile เป็น behavior หลัก ส่วน POS Terminal ใช้ service contract เดียวกันและควรรักษากฎธุรกิจเดียวกัน ความต่างหลักคือ layout และทางเข้าหน้า
ทางเข้าในแอป
| App | Target | หมายเหตุ |
|---|---|---|
| Mobile | TransportList -> TransportDetail | หน้า list/detail ใน navigation stack |
| POS Terminal | Home menu transport -> TransportDetailForm | หน้า split-pane ใน Home screen |
Workflow ภาพรวม
หัวรอบขนส่ง
| Field / behavior | กฎการทำงาน |
|---|---|
| วันที่ขนส่ง | ค่าเริ่มต้นเป็นวันนี้เมื่อสร้างรายการใหม่ |
| สาขา / กลุ่มคลัง | อ่านจาก selected branch และส่งเป็น branchId, whGrpCode, whGrpName |
| คนขับ | ค่าเริ่มต้นมาจากชื่อ/นามสกุลผู้ใช้ปัจจุบันตอนสร้าง |
| รถ | ต้องเลือกก่อนบันทึกและก่อนเปิด modal ดึงใบจัดสายส่ง การเลือกรถเติมทะเบียนและ context คนขับ โดยใน modal จะแสดงรถทดสอบส่วนกลาง (dummy-001 ถึง dummy-003) ก่อนรถที่โหลดจาก API |
| บริษัท | ใช้ company code จาก selected branch ถ้ามีข้อมูล |
| สายส่ง / route | สามารถถูกเติมจากใบจัดใบแรกเมื่อผู้ใช้เลือกใบจัดก่อนบันทึกรอบ |
| flag ฝากเงิน/น้ำมัน | flagBank และ flagGas อยู่บนหัวรอบ และต้องครบก่อนจบรอบ |
Validation ก่อนบันทึก:
| เงื่อนไข | ผลลัพธ์ |
|---|---|
| ไม่มีทะเบียนรถ | ไม่ให้บันทึก/ไม่ให้เปิดดึงใบจัด |
| ไม่มีชื่อคนขับ | ไม่ให้บันทึก |
| ไม่มีวันที่ขนส่ง | ไม่ให้บันทึก |
รถทดสอบใน modal เลือกรถ
Mobile และ POS Terminal ใช้ข้อมูลรถทดสอบร่วมกัน 3 คัน คือ dummy-001, dummy-002 และ dummy-003 โดยทุกคันมีสถานะ READY เพื่อให้เลือกทดสอบ flow ได้ และจะแสดงอยู่ด้านบนก่อนทะเบียนรถจริง รถทดสอบใช้คำค้นเดียวกับรถจริง: หากคำค้นไม่ตรงกับข้อมูลของรถทดสอบ ระบบจะไม่แสดงรายการ Dummy
รถทดสอบไม่สามารถใช้เริ่มขนส่งจริงได้ หากกด เริ่มขนส่ง ขณะเลือกทะเบียน Dummy ระบบจะแจ้ง กรุณาเลือกทะเบียนรถจริงก่อน และไม่เปิดขั้นตอนเริ่มขนส่ง ทะเบียนรถยังแก้ไขได้จนกว่ารอบเริ่มขนส่ง (startedAt มีค่า) หลังจากนั้นระบบจะล็อกการเลือกทะเบียน
การดึงใบจัดสายส่ง
ใบจัดสายส่งมาจากเมนูใบจัดสินค้าสายส่ง แล้วเมนูขนส่งจะดึงมาเป็น workload ของรอบส่งสินค้า
กฎของใบจัด:
| Action | กฎ |
|---|---|
| เพิ่มใบจัด | ทำได้เฉพาะก่อนออกจาก draft ถ้าเป็นรายการใหม่ ระบบสร้างหัวรอบให้อัตโนมัติหลังเลือกใบจัด |
| ลบใบจัดออกจากรอบ | ทำได้เฉพาะ draft, ก่อนมี startedAt, และเฉพาะผู้ใช้ที่แก้รอบขนส่งนี้ได้ ดูกฎละเอียดด้านล่าง |
| ยืนยันใบจัดรายใบ | อัปเดต status ของใบจัดเป็น FINISHED |
| ยืนยันใบจัดทั้งหมด | วนทำใบจัดที่ status ว่างหรือ NULL ให้เป็น FINISHED; เจ้าของรอบหรือ user code 999999 กด action นี้ได้ |
| เริ่มขนสินค้าขึ้นรถ | เปิดได้เมื่อมีใบจัดอย่างน้อย 1 ใบ และทุกใบถูกยืนยันแล้ว |
การลบใบจัดออกจากขนส่ง
การลบในหน้านี้หมายถึงการถอดใบจัดสายส่งออกจากรอบขนส่ง ไม่ใช่การลบเอกสารใบจัดสายส่งต้นทางในเมนูใบจัดสินค้าสายส่ง
กฎการลบ:
| เงื่อนไข | พฤติกรรม |
|---|---|
| ผู้ใช้ไม่ใช่เจ้าของ/ไม่มีสิทธิ์แก้รอบ | ไม่แสดงปุ่มลบ การแก้รอบเดิมอิงเจ้าของรายการผ่าน userCode |
| รอบไม่ใช่ draft | ไม่แสดงปุ่มลบ และ handler บล็อกซ้ำด้วยข้อความว่าเริ่มขนสินค้าขึ้นรถ/เริ่มขนส่งแล้ว |
มี startedAt แล้ว | Terminal ซ่อนปุ่มลบ ส่วน Mobile ถูกบล็อกอยู่แล้วเพราะ workflow ไม่ใช่ draft |
ไม่มี transportId หรือ deliveryId | ไม่ยิงคำสั่ง delete |
| ผู้ใช้ยืนยันลบ | เรียก deleteTransportDelivery แล้ว refresh transport detail |
| ใบจัดถูกยืนยันแล้ว | ยังถอดออกได้เฉพาะตอนรอบยังเป็น draft และยังแก้ได้เท่านั้น หลัง prepare/start แล้วล็อก |
สถานะของรอบขนส่ง
| สถานะ | ความหมาย | Action หลักที่ทำได้ |
|---|---|---|
| New | ยังไม่บันทึกรอบ | บันทึก หรือเลือกใบจัดเพื่อให้ระบบสร้างหัวรอบก่อน |
| Draft | มีรอบขนส่งแล้ว แต่ยังไม่เริ่มขนขึ้นรถ | แก้หัวรอบ เพิ่ม/ลบใบจัด ยืนยันใบจัด เตรียมขนขึ้นรถ |
| Preparing | อยู่ขั้นตอนขนสินค้าขึ้นรถ | เริ่มขนส่ง หรือปลดล็อคกลับไปแก้ไขโดยผู้มีสิทธิ์ |
| In progress | มี startedAt แล้ว | จัดการลูกค้า เพิ่มค่าขนส่งก่อนจ่ายเงิน retry integration จัดการน้ำมัน/ฝากเงิน จบรอบ |
| Finished | มี finishedAt แล้ว | ดูประวัติ ไม่ควรแก้ action งาน |
| Canceled | รายการถูกยกเลิก | ดูประวัติ ไม่ควรแก้ action งาน |
เริ่มขนสินค้าขึ้นรถ และปลดล็อค
| Action | เงื่อนไข | ผลลัพธ์ |
|---|---|---|
| เริ่มขนสินค้าขึ้นรถ | เจ้าของรอบหรือ user code 999999, status เป็น draft, ใบจัดทุกใบยืนยันแล้ว, ไม่ถูกยกเลิก; ก่อนยืนยันระบบตรวจใบจัดที่ยังไม่เข้าขนส่งของเส้นทางรอบนี้ | เรียก POST /transports/{id}/prepare และเปลี่ยน workflow เป็น preparing |
| ปลดล็อค | รอบอยู่สถานะ preparing และไม่ถูกยกเลิก; deptCode อยู่ใน 8, 12, 22, 28, 29, 4, 2; ต้องใส่หมายเหตุ | เรียก /cancel-prepare พร้อม note และกลับไปแก้ไขในลักษณะ draft |
Flow ปลดล็อค
ปลดล็อคใช้ในกรณีรอบขนส่งเข้าสู่ขั้นตอนขนสินค้าขึ้นรถแล้ว แต่ทีมต้องกลับไปแก้ไขรอบก่อนเริ่มออกขนส่งจริง การปลดล็อคไม่ได้ยกเลิกรอบขนส่ง แต่เป็นการยกเลิกสถานะ prepare/loading
กฎการปลดล็อค:
| เงื่อนไข | พฤติกรรม |
|---|---|
รอบไม่ได้อยู่สถานะ PREPARING | ไม่เปิด action ปลดล็อค |
| รอบถูกยกเลิก | ไม่เปิด action ปลดล็อค |
| deptCode ไม่อยู่ในชุดที่อนุญาต | แจ้งว่าไม่มีสิทธิ์ปลดล็อค และไม่เปิด modal กรอกหมายเหตุ |
| ไม่กรอกหมายเหตุ | บล็อกการ submit และแจ้งให้ระบุหมายเหตุ |
| submit สำเร็จ | เรียก unprepareTransport(transportId, { note }), clear state ของ modal, refresh detail และให้เจ้าของรอบกลับไปแก้ไขได้ |
| submit fail | ไม่เปลี่ยนสถานะรอบ และแสดง error จาก API |
เริ่มขนส่ง
การเริ่มขนส่งแยกจากการเริ่มขนสินค้าขึ้นรถ ขั้นตอนนี้บันทึกเลขไมล์เริ่มต้น และสร้างรายการค่าขนส่ง/COD ของลูกค้าเพื่อส่งไป SAP ตาม config
ใน PRD ก่อนตรวจเลขไมล์เริ่มต้น ระบบจะเรียก GET https://api-checklistv2.boonsiri.co.th/api/v1/transportation โดยส่ง userCode ของผู้ใช้ที่ล็อกอิน, status=false และ header x-api-key ที่ตั้งค่าไว้ หาก data เป็น array ว่างจึงทำขั้นตอนเริ่มขนส่งต่อได้ แต่ถ้า data มีรายการ ระบบจะหยุดและแจ้งว่า ยังมีงานที่ไม่เสร็จบนระบบ Check List กรุณาไปดำเนินการให้เสร็จสิ้นทั้งหมดก่อน โดย UAT จะไม่เรียก API นี้ หากเรียก Checklist ไม่สำเร็จหรือ response มีรูปแบบไม่ถูกต้อง ระบบจะหยุดการเริ่มขนส่งและแสดง error จาก API
Payload ตอนเริ่มขนส่ง:
| Field | ที่มา |
|---|---|
transportId | รอบขนส่งปัจจุบัน |
licensePlate | หัวรอบขนส่ง |
mileage | modal เลขไมล์เริ่มต้น |
userCode | เจ้าของรอบ/ผู้ใช้ปัจจุบัน |
shippings | 1 row ต่อลูกค้า มี customerCode, lat/lng และ shippingAmount |
หมายเหตุ: Mobile ส่ง shippingAmount เมื่อเปิด COD เท่านั้น ถ้า COD ปิดจะส่ง 0; POS Terminal ปัจจุบันส่งยอด shippingAmount ที่คำนวณได้ ควรตั้งใจเลือกว่าจะคงความต่างนี้หรือปรับให้ตรง Mobile
ถ้า SAP คืน error บาง row รอบขนส่งยังเริ่มได้ แต่หน้าจอจะแสดงรายการที่ fail เพื่อให้ retry เฉพาะ row นั้นภายหลัง
Workflow จุดส่งลูกค้า
รายการลูกค้าโหลดจาก /transports/{id}/customers ตามใบจัดที่ผูกกับรอบ แสดงยอดชำระ สถานะจ่าย และหลังเริ่มขนส่งจะมี action พิเศษสำหรับน้ำมันกับฝากเงินท้ายรายการ การ์ดลูกค้าที่สถานะยังไม่เป็น FINISHED จะเป็นสีฟ้าอ่อนเสมอ และเมื่อเป็น FINISHED แล้วจึงแสดงสีตาม payment type ที่บันทึกไว้
เงื่อนไขสีการ์ด
| การ์ด | เงื่อนไข | สี |
|---|---|---|
| ลูกค้า | status ไม่ใช่ FINISHED | ฟ้าอ่อน (Draft) เสมอ โดยไม่ขึ้นกับ payment type |
| ลูกค้า | status เป็น FINISHED, paymentType: CASH | เขียวเงินสด |
| ลูกค้า | status เป็น FINISHED, paymentType: TRANSFER | ม่วงเงินโอน |
| ลูกค้า | status เป็น FINISHED, paymentType: BILL_PAYMENT | เหลืองบิลเพย์ |
| ลูกค้า | status เป็น FINISHED, paymentType: OTHER | แดง/ชมพูเงินอื่น ๆ |
| ลูกค้า | status เป็น FINISHED, paymentType: SHIPPING | ม่วงยืนยันส่งสินค้า |
| การ์ดท้ายรายการ เติมน้ำมัน / ฝากเงิน | flagGas / flagBank ที่เกี่ยวข้องเป็น 0 | ฟ้าอ่อน (Draft) |
| การ์ดท้ายรายการ เติมน้ำมัน / ฝากเงิน | flagGas / flagBank ที่เกี่ยวข้องเป็น 1 | เขียวยืนยันแล้ว |
สูตรยอดในการ์ดลูกค้า
การ์ดหนึ่งใบแทน จุดส่งหนึ่งจุด ของลูกค้า จึงอาจมีหลาย invoice ได้ แม้เป็น customerCode เดียวกัน ระบบคำนวณยอดสินค้าเริ่มต้นจาก invoice ทุกใบในจุดส่งนั้น:
ยอดสุทธิของ invoice = amount - discount - useCreditNote
ยอดสินค้าอ้างอิง = ผลรวมยอดสุทธิของ invoice ทุกใบ
ยอดค่าขนส่ง/ค่าธรรมเนียม = ผลรวม shippingAmount ของทุก shipping invoice
(ถ้ายังไม่มี shipping invoice ใช้ customer.shippingAmount)
ยอดรวมบนการ์ด = ยอดสินค้าอ้างอิง + ค่าขนส่ง
ในแต่ละบรรทัด shipping invoice ของการ์ดลูกค้า ระบบแสดงยอดจาก shippingAmount ของใบนั้นโดยตรง: docType: FEE แสดงป้าย ค่าธรรมเนียม และ docType: SHIPPING แสดงป้าย ค่าขนส่ง ทั้งสองประเภทต้องแสดงยอดแม้ปิด COD อยู่ หาก API รุ่นเก่าไม่ส่ง shippingAmount จะ fallback ไปใช้ amount ของใบนั้น
หาก API ส่ง grandAmount มา ยอดที่ต้องชำระและยอดที่ส่งให้ flow รับชำระจะใช้ grandAmount + ค่าขนส่ง แทนยอดสินค้าอ้างอิงข้างต้น โดย grandAmount: 0 เป็นยอดที่ถูกต้องและใช้สำหรับ flow ยืนยันส่งสินค้า/บิลเพย์ยอดศูนย์ ไม่ใช่ข้อมูลที่หายไป
ตัวเลขด้านขวาของการ์ดมีความหมายดังนี้:
| สิ่งที่เห็น | วิธีคิด/ความหมาย |
|---|---|
| ยอดจาง (เมื่อแสดง) | ยอดรวมบนการ์ดจาก invoice สุทธิ + ค่าขนส่ง |
ยอดเข้มสำหรับ CASH, TRANSFER, BILL_PAYMENT, OTHER | ยอดที่รับจริง cash + transfer โดยจำกัดไม่ให้เกินยอดรวม/ยอดที่ต้องชำระ |
| ยอดเข้มสำหรับสถานะอื่น | ยอดที่ต้องชำระ (grandAmount ถ้ามี) + ค่าขนส่ง |
| ยอดในบรรทัด invoice | แสดง invoice.amount ก่อนหัก discount และ useCreditNote เพื่ออ้างอิงเอกสารต้นทาง |
ดังนั้นผลรวมตัวเลขในบรรทัด invoice อาจไม่เท่ากับยอดบนการ์ดเมื่อมีส่วนลดหรือ Credit Note และไม่ควรใช้ยอดที่แสดงต่อ invoice เป็นยอดที่ต้องเก็บจริงโดยไม่หักรายการดังกล่าว
ข้อกำหนดข้อมูล: endpoint ลูกค้าขนส่งควรส่ง grandAmount ให้ชัดเจนเสมอเมื่อเป็นยอดที่ระบบต้นทางสรุปแล้ว และต้องแยกกรณี “ไม่มีค่า” ออกจาก 0 เพื่อไม่ให้ client ตีความยอดสินค้าเป็นศูนย์โดยไม่ตั้งใจ
ปุ่มใน customer modal:
| Action | รายละเอียด |
|---|---|
| โทรศัพท์ | log การโทรถ้ามี customer reference แล้วเปิด dialer |
| ยืนยันการส่ง / รับชำระ | เปิดขั้นเลือก payment type ซ่อนเมื่อเป็น limited access หรือ customer finished แล้ว |
| บันทึกตำแหน่ง | โหลดที่อยู่ลูกค้า แล้วให้เลือก/แก้พิกัดปลายทางที่ใช้ในรอบขนส่ง |
| นำทาง | เปิด navigation จากพิกัด/ที่อยู่ของลูกค้า |
| ดูรายการสินค้า | สรุปรายการสินค้าจาก invoice ของ customer stop |
| เพิ่มค่าขนส่ง | สร้าง shipping invoice เพิ่มให้ลูกค้าคนนั้น บล็อกถ้าลูกค้าจ่าย/finished แล้ว |
| สร้าง QR | สร้าง QR ค่าสินค้า และถ้ามีค่าขนส่งจะสร้าง QR ค่าขนส่งด้วย limited access ยังสร้าง QR ได้กับรายการโอนที่มียอด; บน UAT ต้องเปิด ENABLE_QR_PAYMENT = 1 |
โหลดข้อมูลการ์ดลูกค้าที่ยังไม่เสร็จ
เมื่อกดการ์ดลูกค้าที่ยังไม่ FINISHED แอปจะแสดง loading และเรียก GET /api/transports/{transportId}/customers?customerCode={customerCode} ก่อนเปิด customer modal โดยนำข้อมูลที่ตอบกลับมาจับคู่กับการ์ดจากทั้ง 3 ค่า คือ customerCode, U_BFP_Latitude และ U_BFP_Longitude เพื่อป้องกันการนำข้อมูลของลูกค้ารหัสเดียวกันแต่คนละจุดส่งมาอัปเดตผิดการ์ด จากนั้นจะอัปเดต grandAmount และ paymentType ล่าสุดของการ์ดที่ตรงกัน แล้วจึงทำ flow ใน modal ต่อเหมือนเดิม
เมื่อข้อมูลที่จับคู่ได้มี grandAmount เป็น 0:
paymentType ปัจจุบัน | ตัวเลือกใน Step 2 | paymentType ที่ส่งตอนบันทึก |
|---|---|---|
ว่าง / null | มีเฉพาะ ยืนยันการส่งสินค้า | SHIPPING |
BILL_PAYMENT | มีเฉพาะ ยืนยันการส่งสินค้า | BILL_PAYMENT |
กรณีอื่นยังใช้ตัวเลือกและ validation เดิมทั้งหมด โดยอนุญาตให้ BILL_PAYMENT ยอด 0 บันทึกได้เฉพาะกรณี grandAmount: 0 นี้
ปกติการเปิดการ์ดลูกค้าที่ยังไม่ชำระและรับชำระ ทำได้เฉพาะเจ้าของรอบขนส่งเท่านั้น ยกเว้น userCode 999999 ใช้เปิดการ์ดและทำ flow รับชำระได้หลังเริ่มขนส่ง เพื่อ debug โดยไม่ได้รับสิทธิแก้ไขหัวรอบ ใบจัด หรือการจัดเส้นทาง
รายการค่าขนส่ง
| Action | กฎ |
|---|---|
| สร้างอัตโนมัติตอนเริ่มขนส่ง | สร้างจากลูกค้าแต่ละรายและส่งพร้อม startTransport |
| เพิ่มค่าขนส่งเอง | ต้องมี customer code, ยอดเป็นจำนวนเต็มบวก, lat/lng, user code และ user name |
| ลบค่าขนส่ง | ทำได้ก่อนลูกค้าจ่าย/finished เท่านั้น |
| Retry ส่งค่าขนส่งไป SAP | เรียก /shipping/{shippingInvoiceId}/retry เฉพาะรายการที่ส่ง B1 fail |
| Retry incoming | service มี /retry-incoming สำหรับ retry ฝั่ง incoming เมื่อ UI/API เปิดใช้ |
| Retry cancel ไป SAP | เรียก /shipping/{shippingInvoiceId}/retry-cancel เมื่อส่งยกเลิกไป SAP fail |
การรับชำระและหลักฐาน
การบันทึกรับชำระเรียก /transports/{id}/payment และ endpoint นี้อัปเดตสถานะลูกค้าเองแล้ว หน้าจอไม่ได้เรียก update customer status แยกแบบเดิม
ประเภทรับชำระ:
| Type | พฤติกรรม |
|---|---|
CASH | เงินสดต้องมากกว่า 0 หน้าจอเติม/ล็อกยอดเงินสดสำหรับเคสปกติ ต้องตรวจสอบค่าธรรมเนียมก่อน |
TRANSFER | หลังสร้าง QR ต้องเลือกผลการรับชำระตาม flow ด้านล่างก่อนบันทึก |
BILL_PAYMENT | ใช้ validation แนวเดียวกับโอน ต้องมีสลิปบิลเพย์ |
OTHER | ต้องใส่หมายเหตุ ใช้กับรับบางรายการ ไม่รับสินค้า แบ่งชำระ หรือเคสผิดปกติ ต้องตรวจสอบค่าธรรมเนียมก่อนถ้า config เปิด |
SHIPPING | ใช้เมื่อยอด payable เป็น 0 และต้องยืนยันส่งสินค้าอย่างเดียว |
ทางเลือกหลังสร้าง QR Code
หาก BASE_API_ENV = 'uat' และ ENABLE_QR_PAYMENT = 0 ระบบจะไม่เรียก QR API และแจ้งว่ายังไม่เปิดใช้ QR Payment; การพิมพ์ใบค่าขนส่งยังทำต่อได้โดยเว้น QR Payment ไว้
เมื่อเลือก TRANSFER / QR Code ต้องเลือก Radio อย่างใดอย่างหนึ่งก่อนกดบันทึก:
| ตัวเลือก | พฤติกรรมหน้าจอ | ข้อมูลที่ส่งรับชำระ |
|---|---|---|
| ลูกค้าโอนชำระกับพนักงานขนส่งแล้ว | แสดงช่องระบุเงินโอนที่แก้ไขได้ ยอดต้องมากกว่า 0 และใช้ flow TRANSFER ปกติ | paymentType: TRANSFER พร้อมยอดเงินโอนที่ระบุ |
| ลูกค้าจะโอนชำระกับฝ่ายขายเองภายหลัง | ซ่อนช่องระบุเงินโอน และกำหนดยอด Transfer เป็นยอดที่ต้องชำระทั้งหมด | paymentType: OTHER, cash: 0, transfer เท่ากับยอดที่ต้องชำระทั้งหมด และ System note ลูกค้าจะโอนชำระกับฝ่ายขายเองภายหลัง |
หากยังไม่เลือก Radio ระบบจะไม่บันทึกและแจ้งให้เลือกสถานะการชำระเงินโอนก่อน โดยทุกตัวเลือกต้องมียอดโอนมากกว่า 0
หลักฐานและ validation:
| Requirement | กฎ |
|---|---|
| รูปส่งของ | ต้องมีทุกเคสก่อนบันทึก |
| สลิปค่าขนส่ง | ถ้ามีค่าขนส่งและมีสลิปค่าสินค้า/บิลเพย์ ต้องแนบสลิปค่าขนส่งด้วย |
| ลายเซ็น | ต้องมี ยกเว้น TRANSFER และ BILL_PAYMENT |
| GPS | ตอน submit จะดึงพิกัดปัจจุบันของเครื่อง และส่งพิกัดปลายทางลูกค้าไปด้วย |
| ค่าธรรมเนียม drop-off | ต้องตรวจสอบสำหรับ CASH และ OTHER; ถ้า config fee เปิด จะบวกเข้ายอดเงินสด |
| ยอดประเภท OTHER | เงินสด + เงินโอน ห้ามเกินยอด payable; ถ้าไม่ติ๊ก “รับสินค้าบางรายการ” ต้องชำระครบ |
| ไม่รับสินค้า | ใน OTHER จะทำให้เงินสด/โอนเป็น 0 แต่ยังบันทึกรายละเอียดและหลักฐาน |
| โอนกับฝ่ายขายภายหลัง | ทางเลือก QR พิเศษนี้ส่ง OTHER โดยเงินสดเป็น 0, ยอด Transfer เท่ากับยอดที่ต้องชำระทั้งหมด พร้อม System note ของฝ่ายขาย |
การอัปโหลดหลักฐาน
รูปส่งของ สลิปโอน/บิลเพย์ สลิปค่าขนส่ง และลายเซ็นลูกค้า ใช้ endpoint เดียวกัน:
POST /api/transports/{transportId}/files
Content-Type: multipart/form-data
Request body เป็น multipart/form-data และต้องส่งข้อมูลของลูกค้าที่กำลังเปิดอยู่ใน customer modal ด้วย:
| Field | ประเภท | แหล่งข้อมูล / ความหมาย |
|---|---|---|
files | File | ไฟล์หลักฐานที่กำลังแนบ; หน้าจอปัจจุบันแนบทีละไฟล์/รูป |
whGrpCode | string | กลุ่มคลังจากรายละเอียดรอบขนส่ง |
customerCode | string | customerCode ของลูกค้าที่เลือกอยู่ |
U_BFP_Latitude | string | พิกัดละติจูดของจุดส่งลูกค้าที่เลือกอยู่ |
U_BFP_Longitude | string | พิกัดลองจิจูดของจุดส่งลูกค้าที่เลือกอยู่ |
transportId อยู่ใน URL ไม่ได้อยู่ใน body โดย response จะคืน path ของไฟล์ที่อัปโหลดสำเร็จ เพื่อนำไปใส่ใน payment payload ช่อง photo, slipPath, shippingSlipPath หรือ signature ตามประเภทหลักฐาน
ข้อมูล customerCode, U_BFP_Latitude และ U_BFP_Longitude ต้องมีและต้องไม่เป็นค่าว่างทุกครั้ง หากข้อมูลใดข้อมูลหนึ่งหาย ระบบจะแสดง:
ไม่พบ customerCode หรือพิกัดลูกค้าสำหรับอัปโหลดไฟล์
จากนั้นจะหยุดการอัปโหลดและไม่เรียก API ส่วนกรณีไม่พบ whGrpCode ระบบจะแจ้งข้อมูลคลังไม่พร้อมสำหรับอัปโหลดเช่นกัน
Payment payload:
| Field | ความหมาย |
|---|---|
customerCode | ลูกค้าที่กำลังจัดการ |
paymentType | เงินสด โอน บิลเพย์ อื่นๆ หรือยืนยันส่งสินค้า |
amount | ยอดที่ต้องชำระรวมค่าขนส่ง |
cash, transfer | ยอดรับจริง เงินสดอาจรวมค่าธรรมเนียม |
notes | หมายเหตุผู้ใช้ และ system summary สำหรับ OTHER |
U_BFP_Latitude, U_BFP_Longitude | พิกัดปลายทางลูกค้า |
currentLatitude, currentLongitude | พิกัดเครื่องตอนรับชำระ |
signature, slipPath, shippingSlipPath, photo | path ไฟล์ที่อัปโหลด |
น้ำมันและฝากเงิน
หลังเริ่มขนส่งแล้ว รายการลูกค้าจะมี action พิเศษท้ายรายการ 2 รายการ คือเติมน้ำมันและฝากเงิน
| Action | รายละเอียด |
|---|---|
| เติมน้ำมัน | เปิดบิลน้ำมัน/Expense Bill โดย prefill ข้อมูลจากรอบขนส่ง หรือเลือก “ไม่เติมน้ำมัน” ทั้งสองทางทำให้ flagGas = 1 |
| ฝากเงิน | เปิด Deposit โดย prefill จากยอดเงินสดของรอบ หรือเลือก “ไม่ฝากเงิน” ทั้งสองทางทำให้ flagBank = 1 ปัจจุบัน Mobile modal ซ่อนปุ่มฝากเงินไว้และเปิดเฉพาะ “ไม่ฝากเงิน”; POS Terminal ยังมี context สำหรับเปิดฝากเงิน |
| เงื่อนไขจบรอบ | จบขนส่งไม่ได้จนกว่า flagGas และ flagBank เป็น 1 ทั้งคู่ |
สิ้นสุดขนส่ง
Payload ตอนจบขนส่งมี transportId, licensePlate และเลขไมล์สิ้นสุด mileage
สิทธิ์และ guard
| เรื่อง | Mobile | POS Terminal |
|---|---|---|
| เห็นเมนู | getTransportAccess().canOpenTransportMenu อนุญาต deptCode -2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 13, 16, 14, 22, 27, 28, 30 | MENU_TRANSPORT ใช้ department set เดียวกัน |
| สร้างรอบขนส่ง | deptCode -2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 13, 16, 14, 30 หรือมี full non-main warehouse access | canCreateTransport ใช้ create dept set และ full non-main warehouse access |
| เงื่อนไขสาขา | ใช้ selected branch context และ shared warehouse access helper | menu config ตั้ง requiresMainBranch = true สำหรับ transport; create helper ยังรองรับ full non-main access ในจุดที่ถูกเรียก |
| แก้รอบ | แก้รายการเดิมได้เฉพาะเจ้าของรอบ (userCode) | |
| ลบจาก list | เฉพาะ draft, เจ้าของรอบ, และต้องมีสิทธิ์สร้าง/สิทธิ์ warehouse | |
| ปลดล็อค loading | เฉพาะสถานะ preparing; deptCode 8, 12, 22, 28, 29, 4, 2; ต้องใส่หมายเหตุ | |
| action หลัง finished/canceled | ไม่ควรเปิด action งานต่อ ให้เป็น read-only |
Mobile vs POS Terminal
| Area | Mobile | POS Terminal |
|---|---|---|
| Layout | stack screen พร้อม list, detail, modal และ bottom action bar | split-pane detail form ใน Home screen |
| Core API | apps/mobile/src/services/transport.ts | apps/pos-terminal/src/services/transport.ts |
| Core behavior | ใช้เป็น behavior อ้างอิงของ guard และ customer workflow | ควร mirror กฎจาก Mobile |
| shipping amount ตอน start | ส่งยอดค่าขนส่งเมื่อเปิด COD ถ้าปิด COD ส่ง 0 | ปัจจุบันส่ง shippingAmount ที่คำนวณได้ ควรตั้งใจเลือกว่าจะคงไว้หรือปรับตาม Mobile |
| แสดง start error | แสดง SAP error modal หลังเริ่มขนส่ง | แสดง alert สรุปรายการ fail |
| ฝากเงิน | Mobile bank modal ปัจจุบันซ่อนปุ่มฝากเงิน เหลือ “ไม่ฝากเงิน” | Terminal ยังมี deposit context และปุ่ม skip flag |
Developer Handoff Map
| Area | Code |
|---|---|
| Mobile list | apps/mobile/src/screens/transportStack/transportListScreen/index.tsx |
| Mobile detail | apps/mobile/src/screens/transportStack/transportDetailScreen/index.tsx |
| Mobile customer modal | apps/mobile/src/screens/transportStack/transportDetailScreen/TransportCustomerActionModal.tsx |
| Mobile helpers | apps/mobile/src/screens/transportStack/transportDetailScreen/helpers.ts |
| POS Terminal detail | apps/pos-terminal/src/screens/HomeScreen/TransportDetailForm.tsx |
| POS Terminal helpers | apps/pos-terminal/src/screens/HomeScreen/TransportDetailHelpers.ts |
| Transport services | apps/mobile/src/services/transport.ts, apps/pos-terminal/src/services/transport.ts |
| Menu/permission config | apps/mobile/src/utils/transport.ts, apps/pos-terminal/src/screens/HomeScreen/config.ts |
| เอกสาร source ของใบจัด | ใบจัดส่งสินค้าสายส่ง |
| เอกสารที่เกี่ยวข้อง | ฝากเงิน, บิลน้ำมัน, ยานพาหนะ |