การเลือก Lot สายส่งใหม่
Mobile และ POS Terminal ใช้ helper คืนสิทธิยอดจองและจัดสรร Lot ร่วมกันใน packages/utils/src/shippingLots.ts สำหรับการกดเลือกสินค้าในใบจัดสายส่ง รวมถึงการเลือก Lot ที่มีปัญหาใหม่ก่อนส่ง B1 อีกครั้ง
ทำไมใช้ยอดว่างอย่างเดียวไม่ได้
GET /api/products/lot?itemCode=...&whsCode=...&company=... ส่งยอดหลังหักการจอง เช่น สต๊อกจริง quantity = 15, booking = 8, available = 7 ยังจัดสินค้า 8 ชิ้นได้ ถ้ายอดที่จอง 8 นั้นเป็นของ Invoice ที่กำลังแก้จริง
แหล่งข้อมูล Lot เดิมคือ detail.invoices[].products[].lots จำนวนสินค้าใน Invoice เพียงอย่างเดียวยืนยันการจองไม่ได้ ต้องจับคู่ invoice ID, สินค้า, บริษัท, คลัง และ lotNo/batchNumber โดยไม่นับ Invoice ที่ยกเลิก, b1Status = 1 หรืออยู่นอก invoiceAllocations ของสินค้าที่เลือก และไม่คืนยอดซ้ำเมื่อพบ Invoice เดิมหลายครั้ง
“คืนยอด” ในขั้นนี้หมายถึงคืนสิทธิให้เลือกใช้สินค้าที่ตนจองอยู่ในหน้าจอ ไม่ได้ปล่อยจอง แก้ Invoice หรือเพิ่มสต๊อกจริงบน server การบันทึกยังใช้ POST /api/deliveries/{id}/lots และกระจายยอดกลับไปแต่ละ Invoice ตาม flow เดิม
ยอดคงเหลือและ Lot ที่ API ไม่ส่งกลับ
service getProductLots เก็บสต๊อกดิบไว้ใน lotQuantity และใช้ available เป็น quantity ที่เลือกได้ หน้าสายส่งส่ง includeUnavailable: true เพื่อเก็บ Batch ที่ว่างศูนย์ไว้คำนวณคืนยอดก่อน flag นี้ใช้ภายใน service ไม่ใช่ query parameter ของ API ส่วนหน้าจออื่นยังกรองเฉพาะยอดว่างมากกว่าศูนย์ตามเดิม
เมื่อ API ส่ง Batch กลับมา restoreShippingLotOptions บวกคืนเฉพาะยอดจองของตัวเองที่ตรงกัน โดยจำกัดไม่ให้เกินยอดจองและสต๊อกจริงล่าสุดเมื่อมีข้อมูลนั้น หาก API ไม่ส่ง Batch เดิมกลับมา helper จะสร้างตัวเลือกจาก Lot ที่บันทึกอยู่ใน Invoice ที่เข้าเงื่อนไข จำกัดจำนวนเท่ายอดที่ Invoice จองไว้ วันที่ใช้เฉพาะข้อมูลของ Lot เดิม ถ้าไม่มีให้เป็นค่าว่าง (dueDate เป็น null) ไม่คัดวันที่จาก Batch อื่น
| กรณี | จำนวนที่เลือกได้หลังคืนสิทธิ |
|---|---|
| Batch เดียวกัน: ว่าง 7 + ยอดจองตัวเอง 5 + 1 + 2 | เลือกได้ 15 แต่จัดเพียง 8 ตามที่ต้องการ |
| Batch ถูกจองเต็ม: ว่าง 0 + ยอดจองตัวเอง 8 | เลือกได้ 8 |
API มี 260812-NMA8034 ว่าง 15 แต่ Invoice จอง 260812-NMA8033 ไว้ 8 และ API ไม่ส่งกลับ | 8033 เลือกได้ 8 ส่วน 8034 ยังเลือกได้ 15 |
Invoice ต้องการ 8 แต่ lots ว่าง | ไม่มีสิทธิยอดจองให้คืน |
| ยอดจองเป็นของ Invoice อื่น บริษัทอื่น คลังอื่น หรือสินค้าอื่น | ไม่บวกยอดนั้นคืนให้สินค้านี้ |
buildShippingLotSelection ใช้ยอดจองเดิมก่อน แล้วเติมส่วนที่ขาดตามลำดับตัวเลือกที่เหลือ โดย service เรียงตัวเลือกจาก API ตามวันหมดอายุ หากเลือกเพิ่มจากตัวเลือกเดิมจะรวมจำนวน ไม่สร้างแถวเลือกซ้ำ ตัวอย่างมี Lot เดิม 8033 × 8 และ Lot ใหม่ 8034 × 15: ต้องการ 8 ใช้ 8033 × 8 อย่างเดียว; ต้องการ 9 ใช้ 8033 × 8 และ 8034 × 1
เงื่อนไข Error และ Modal
เงื่อนไขต่อไปนี้ใช้ตอนกดสินค้าครั้งแรกที่ยังไม่มีรายการ Batch ใน local state ผู้ใช้ยังเปิดสินค้าที่เลือกแล้วเพื่อแก้ Batch ได้
| หลังคืนสิทธิยอดจอง | ผลลัพธ์ |
|---|---|
| ยอดที่เลือกได้รวมต่ำกว่าจำนวนที่ยังต้องจัด รวมถึงไม่มีตัวเลือกเลย | แสดง error ทันที ระบุยอดที่ต้องการ ยอดหลังคืนสิทธิ และยอดที่ขาด ไม่เลือกบางส่วนและไม่เปิด Batch Modal |
| ใช้ยอดจองเดิม จำนวนครบ และตัวเลือกที่รวมแล้วมี Batch ไม่ซ้ำไม่เกิน 2 | เลือกให้ทันที ไม่เปิด Modal |
| ใช้ยอดจองเดิม จำนวนครบ แต่ตัวเลือกที่รวมแล้วมี Batch ไม่ซ้ำเกิน 2 | เติมรายการที่เสนอให้ แล้วเปิดหน้าจอแก้ไขเพื่อตรวจสอบ |
| ไม่มีสิทธิยอดจองเดิม มีตัวเลือกเดียวและจำนวนพอ | เลือก Lot เดียวอัตโนมัติตามเดิม |
| ไม่มีสิทธิยอดจองเดิม มีหลายตัวเลือกและจำนวนรวมพอ | เปิดตัวเลือก/ตัวแก้ไขให้เลือกเองตามเดิม |
เกณฑ์ 2 Batch นับ batchNumber ไม่ซ้ำทั้งหมดในตัวเลือกที่รวมแล้วและเลือกใช้ได้ ไม่ใช่นับเฉพาะ Batch ที่ถูกจัดสรรจนยอดครบ การเลือกอัตโนมัติปรับจำนวนตรวจใน local state เท่านั้น ไม่ส่งบันทึกใบจัดอัตโนมัติ
การ Debug
ใน development build ค้น Console ด้วย [ShippingProductLots] /products/lot invoices จะมี JSON snapshot ของ itemCode, whsCode, company และรายการ Invoice (invoiceId, invoiceNo, customerName, qty) ก่อนเรียก API ทั้งตอนกดสินค้าและตอนโหลดตัวเลือกเพิ่มเติม
ต้องอ่าน lots ของสินค้าที่ตรงกันใน Invoice เพื่อยืนยันเจ้าของยอดจอง จำนวน Invoice ใน log และ booking รวมจาก endpoint อย่างเดียวยืนยันไม่ได้ หากพบ warning FiberNode ซ้ำหรือ Debugger หน่วยความจำเต็ม ดู Worklet ของแถวปัด
การตรวจสอบและจุดอ้างอิงโค้ด
รันจาก repository root:
yarn typecheck
yarn mobile:test --watchman=false --runInBand shippingLots.test.ts productLotAvailability.test.ts swipeableWorklets.test.tsx
ผล merge เข้า UAT ที่ 25c9f884 ผ่าน typecheck ทั้งสองแอปและเทสต์เฉพาะส่วน 54 กรณี (Lot/service 48 และ swipe 6) ครอบคลุมเจ้าของยอดจอง, Lot หาย/ว่างศูนย์, ใช้ยอดเดิมก่อนยอดใหม่, เพดานจำนวน, การกระจาย payload และ closure ที่คอมไพล์จาก Worklet ผลนี้ยังไม่ใช่การยืนยัน Modal/error UI บนอุปกรณ์จริง
รายการทดสอบด้วยมือบน ทั้งสองแอป:
- คืนยอด Batch เดียวกัน 7 + 8 และจัดจาก Lot เดิมที่หายไป 8 + Lot ใหม่ 1 ได้โดยไม่เปิด Modal เมื่อตัวเลือกที่รวมแล้วไม่เกิน 2 Batch
- มีตัวเลือกไม่ซ้ำเกิน 2 Batch ต้องเปิดหน้าจอแก้ไข แม้รายการที่จัดสรรจริงใช้เพียง 1 หรือ 2 Batch
- ยอดรวมไม่พอ รวมถึง API ว่างและไม่มี Lot เดิม ต้องแจ้ง error โดยไม่เปลี่ยนจำนวนตรวจ
- สินค้าที่ไม่มียอดจองเดิมยังเลือก Lot เดียวอัตโนมัติ และหลาย Lot ให้เลือกเอง
- เปิดสินค้าเดิม แก้/ลบ Batch บันทึกยอดแยก Invoice แล้วโหลดใหม่ ตรวจว่าไม่คืนยอดของใบยกเลิกหรือใบที่ B1 สำเร็จ
- ปัดแถว Invoice/สินค้าขณะเปิด DevTools ตรวจว่าปุ่มทำงานและไม่มี warning FiberNode ซ้ำ
| หน้าที่ | ไฟล์ |
|---|---|
| คืนยอดและจัดสรรร่วม | packages/utils/src/shippingLots.ts |
| UI/เงื่อนไข Mobile | apps/mobile/src/screens/shippingStack/shippingDetailScreen/index.tsx |
| UI/เงื่อนไข Terminal | apps/pos-terminal/src/screens/HomeScreen/ShippingRouteDetailForm.tsx |
| แปลงยอด API | apps/mobile/src/services/common.ts, apps/pos-terminal/src/services/common.ts |
| Regression tests | apps/mobile/__tests__/shippingLots.test.ts, productLotAvailability.test.ts, swipeableWorklets.test.tsx |
ดูเพิ่มเติมที่ เมนูใบจัดสายส่ง และ workflow สายส่ง/ขนส่ง