ข้ามไปยังเนื้อหาหลัก

ใบเบิกยืม

ใบเบิกยืมเป็น flow สำหรับเบิกสินค้า/ปลาออกไปก่อนเข้าสู่การขายปกติ จึงคล้ายใบสั่งขายตรงที่เริ่มจากลูกค้า สาขา/คลัง รายการสินค้า จำนวน และภายหลังสามารถแปลงเป็นใบสั่งขายได้ จุดที่ต่างคือประเภทการเบิก เงื่อนไขวันที่มาเบิก/ทะเบียนรถของ SPEC ในสาขาย่อย จำนวนที่คาดว่าจะซื้อ การรับงาน/ตัดสต๊อก การ retry เมื่อ sub-borrow ล้มเหลว และการ convert ไปใบสั่งขาย

จุดเข้าใช้งาน​

แอปหน้าจอ
MobileBorrowList -> BorrowDetail
POS TerminalborrowFish -> BorrowDetailForm ใน HomeScreen และรายการใบเบิก/workspace ของ StorefrontPosStack

ประเภทการเบิก​

Typeความหมาย
SPECให้ลูกค้าดู
MARKETเดินตลาด
VIDEOถ่ายวีดีโอ
SEMINARสัมมนา
DESTROYทำลาย
LABวิจัย

ต้องเลือกประเภทก่อนเพิ่มสินค้า ถ้าเป็นสาขาย่อยและจะเปลี่ยนเป็น SPEC แต่มีสินค้าอยู่แล้ว ระบบจะบล็อกและให้ลบสินค้าออกก่อน เพราะ SPEC มีเงื่อนไขเพิ่มเรื่องวันที่มาเบิกและทะเบียนรถลูกค้า

สถานะเอกสาร​

สถานะ APIข้อความใน UIความหมาย
NEWสร้างใหม่Draft แก้ไขได้ตามสิทธิ์เจ้าของเอกสาร
WAITINGรอดำเนินการรอการดำเนินการ
ACCEPTEDรับงานแล้วรับงานแล้ว โดย guard ของปุ่มตรวจ acceptedAt ด้วย
DONEเสร็จสิ้นดำเนินการเสร็จ แต่ยังมี retry ได้หาก sub-borrow ล้มเหลว
PARTIAL_CONVERTEDแปลงและรอคืนแปลงสินค้าบางรายการเป็นใบสั่งขายแล้ว และคืนรายการที่ยังมี lots ได้
CONVERTEDแปลงเป็นใบสั่งขายแล้วแปลงเป็นใบสั่งขายแล้ว
CANCELEDยกเลิกยกเลิกเอกสาร

หลังทำรายการสำเร็จ แอป refresh สถานะจาก backend การแปลงเป็นใบสั่งขายต้องรับงานแล้วและผ่านสิทธิ์ด้านล่าง โดยไม่บังคับสถานะ DONE หลังเรียก /return-items แอปใช้สถานะที่ backend ส่งกลับมา ไม่กำหนดสถานะปลายทางเอง.

Flow การทำงาน​

เงื่อนไขหัวเอกสาร​

หัวข้อพฤติกรรม
ลูกค้าใบเบิกเริ่มจากลูกค้าที่เลือก และเก็บ customer code/name
สาขาและกลุ่มคลังpayload ส่ง branchId, whGrpCode, whGrpName จากสาขา/เอกสาร
ประเภทการเบิกต้องเลือกก่อนเพิ่มสินค้าและก่อน confirm
หมายเหตุเก็บใน notes และแก้ได้เมื่อ draft ยัง editable
dateOfPickupส่งเฉพาะประเภท SPEC; ถ้าไม่ใช่ SPEC จะส่งเป็น null
ทะเบียนรถลูกค้าสำหรับสาขาย่อย SPEC ต้องกรอกก่อนเพิ่มสินค้า
การล็อกวันที่มาเบิกสาขาย่อย SPEC แก้วันที่ไม่ได้เมื่อมีสินค้าแล้ว
วันที่ย้อนหลังMobile บล็อกวันที่มาเบิกที่น้อยกว่าวันปัจจุบัน

เงื่อนไขสินค้าและจำนวน​

กฎสำคัญของสินค้า:

กฎสาขาหลักสาขาย่อย
จำนวนที่คาดว่าจะซื้อไม่บังคับ payload เป็น 0/undefinedบังคับ และต้องมากกว่า 0
จำนวนต่อบรรทัดเฉพาะ SPEC (ให้ลูกค้าดู) สูงสุด 3 ชิ้น; ประเภทอื่นไม่มี limit ต่อบรรทัดสูงสุด 5 ชิ้นทุกประเภท
จำนวนรายการสินค้าเฉพาะ SPEC สูงสุด 3 รายการ; ประเภทอื่นไม่มี limit จำนวนรายการไม่มี guard จำกัด 3 รายการใน UI
SPEC วันที่/ทะเบียนรถไม่ถูกบังคับด้วย branch guard นี้ต้องกรอกก่อนเพิ่มสินค้า
รูปแบบจำนวนจำนวนเต็มบวกเท่านั้นจำนวนเต็มบวกเท่านั้น
stock guardตอนเพิ่มตรวจ onHand - booking; ตอนแก้ใช้ stock ที่เหลือรวมจำนวนเดิมของ item นั้นเหมือนกัน

payload ของ item สร้างจาก product inventory เช่น product code/name, qty, expectedQty, price, amount, weight, warehouse, company, tax/VAT, UOM, weight-base flag, step-price flag และ editPriceBy = null

การแก้ไขสินค้าเดิม​

การแก้ item ทำได้เฉพาะตอนเอกสารยังเป็น draft ที่แก้ได้ เมื่อแก้ ระบบโหลด inventory ปัจจุบันจาก product code แล้วคำนวณ maxEditableStock = availableStock + originalQty หากเป็นสินค้าเดิม เพื่อให้ผู้ใช้คงจำนวนเดิมหรือลดจำนวนได้ แต่ยังกันไม่ให้จำนวนสุดท้ายเกิน stock

สำหรับสาขาย่อย expectedQty ยังบังคับมากกว่า 0 และ limit จำนวนต่อบรรทัดยังคงเป็นไม่เกิน 5 ชิ้นทุกประเภท ส่วนสาขาหลักจำกัดไม่เกิน 3 ชิ้นเฉพาะ SPEC; ประเภทอื่นไม่จำกัดจำนวนต่อบรรทัด ทุกประเภทยังต้องเป็นจำนวนเต็มบวกและไม่เกิน stock ที่ใช้ได้

Product Picker และการแสดงสินค้ารายการแรก​

  • ถ้าใบเบิกมี borrowId แล้ว การเพิ่มสินค้าจะไม่แสดง loading แบบเต็มหน้าจอที่บังหน้า detail และ product picker จะยังเปิดอยู่หลังเพิ่มสำเร็จ เพื่อให้เลือกเพิ่มได้ต่อเนื่อง
  • Draft ใหม่ใช้การ persist สองช่วง: สร้าง header ของใบเบิกก่อน ใช้ borrowId ที่ API คืนกลับมาเป็น identity สำหรับสร้าง item แล้วจึงสร้างสินค้ารายการแรก
  • borrowNo เป็นเลขเอกสารสำหรับแสดงผล ไม่ใช้แทน borrowId ในการสร้าง item
  • หลังสร้างสินค้ารายการแรก ระบบจะ refresh detail/list ของใบเบิก และ sync route ด้วย identity ที่ได้กลับมา ทำให้เห็น header และสินค้ารายการแรกทันที ไม่ต้องรอเพิ่มสินค้ารายการที่สอง
  • Mobile และ POS Terminal ใช้พฤติกรรมการ persist เดียวกัน

Confirm, Accept, Complete, Retry​

ตอน confirm ระบบบันทึกหัวเอกสารล่าสุดก่อนเรียก confirmBorrow แล้วล็อกการแก้ draft ตามสถานะ การรับงานจะบันทึก acceptedAt ปุ่มยืนยันคืน/ตัดสต๊อกยังต้องผ่านสิทธิ์ process รับงานแล้ว และมีรายการที่ดำเนินการได้

completeBorrowStock(borrowId, detail) เลือก API ตามลำดับเดียวกันบน Mobile, POS HomeScreen และ Storefront POS:

ลำดับข้อมูลปัจจุบันPOST endpointBody
1borrowStatus === "PARTIAL_CONVERTED"/api/borrow/{id}/return-itemsID ของทุกรายการที่ยังมี lots
2สถานะอื่นที่ดำเนินการได้ และ hasFailedSubBorrow/api/borrow/{id}/retryไม่มี body
3สถานะอื่นที่ดำเนินการได้ และไม่มี failed sub-borrow/api/borrow/{id}/approveไม่มี body

ตรวจ PARTIAL_CONVERTED ก่อนเสมอ แม้ hasFailedSubBorrow เป็น true และยังคืนได้แม้มี convertedOrderId/convertedAt หากเหลืออย่างน้อยหนึ่งรายการที่มี lots ส่วนสถานะอื่นใช้ guard เดิม: WAITING/ACCEPTED ที่รับงานแล้ว หรือ DONE ที่มี failed sub-borrow และต้องยังไม่ถูกแปลง

ตัวอย่าง: หลังแปลงรายการ 11 และ 12 จะมี lots ว่าง ส่วนรายการ 13 ยังมี lots เมื่อยืนยันคืนจะส่ง:

POST /api/borrow/{id}/return-items
{
"borrowItemIds": [13]
}

Lots มีอยู่แล้วในแต่ละรายการ จึงไม่มีขั้นตอนเลือก lots หรือเลือกรายการคืนเพิ่ม ระบบส่งทุกรายการที่ lots เป็น array ไม่ว่าง ตัดรายการที่แปลงแล้วออก และไม่ส่ง ID ซ้ำ หากไม่มีรายการเหลือหรือมีรายการที่จะคืนแต่ ID ไม่ถูกต้อง ระบบไม่ยิง API เมื่อ API ล้มเหลวจะแสดง error โดยไม่เปลี่ยนไปเรียก /approve หรือ /retry แทน เมื่อสำเร็จจะโหลดรายละเอียดและข้อมูลรายการ/workspace ที่เกี่ยวข้องใหม่.

แปลงเป็นใบสั่งขาย​

ปุ่มแปลงเป็นใบสั่งขายต้องผ่านเงื่อนไขครบ:

เงื่อนไขค่าที่ต้องเป็น
สาขาสาขาหลัก (isMainBranch = true)
ประเภทใบเบิกSPEC
บันทึกและรับงานแล้วมี borrowId และ acceptedAt โดยไม่บังคับสถานะ DONE
ยังไม่เคยแปลงไม่มี convertedOrderId หรือ convertedAt และไม่ใช่ CONVERTED หรือ PARTIAL_CONVERTED
ผู้ใช้เจ้าของเอกสาร หรือแผนก -2, 1, 2, 4, 7, 8, 9, 13, 16, 20, 21, 23, 29

เมื่อกดแปลง ระบบเปิด BorrowConversionModal ที่ใช้ร่วมกันบน Mobile, POS HomeScreen และ Storefront POS:

  1. ทุกครั้งที่เปิดเริ่มโดยยังไม่เลือกรายการ กดเลือกทีละรายการหรือ เลือกทั้งหมด ได้ กดเลือกทั้งหมดอีกครั้งเพื่อล้างการเลือก
  2. แต่ละรายการแสดงเฉพาะชื่อสินค้า รหัสสินค้า และจำนวน มี checkbox/เครื่องหมายถูกและขอบที่เปลี่ยนสีเพื่อบอกว่าเลือกแล้ว พร้อมจำนวนรายการที่เลือก
  3. ต้องเลือกอย่างน้อยหนึ่งรายการแล้วกด ยืนยัน ระบบตรวจว่ารายการที่เลือกยังอยู่ในข้อมูลปัจจุบัน และส่งเฉพาะ ID ของรายการใบเบิกที่เลือก:
POST /api/borrow/{id}/convert
{
"borrowItemIds": [11, 12]
}

Modal ใช้ขนาดตัวอักษรชื่อสินค้า 14.4 (ลด 10%), padding การ์ด 7 (ลด 50%), gap รายการ/การ์ด 6 และ gap ระหว่างบรรทัด 2 ระหว่างส่งข้อมูล ปุ่มแสดง กำลังแปลง… และล็อกการเลือก การปิด Modal และการส่งซ้ำ หาก API ล้มเหลวจะคงรายการที่เลือกไว้พร้อมแสดง error ใน Modal

เมื่อสำเร็จ ระบบโหลดรายละเอียดใบเบิกใหม่ รวมสถานะจาก backend และ convertedOrderId สถานะ PARTIAL_CONVERTED แสดงข้อความ แปลงและรอคืน ในหน้ารายการ ในหน้ารายละเอียด หากสถานะนี้และ item มี lots ว่าง ไม่ส่งมา หรือเป็น null จะถือว่าแปลงแล้วและแสดงพื้นหลังแดงอ่อน (rgba(239,68,68,0.16)) รายการที่ยังมี lots ใช้พื้นหลังปกติและเข้าสู่ flow คืนรายการที่เหลือ ส่วน lots ว่างในสถานะอื่นไม่เข้าเงื่อนไขพื้นหลังแดง เอกสารที่แปลงบางส่วนแล้วไม่สามารถกดแปลงซ้ำได้

ใน StorefrontPosStack ใบเบิกที่แปลงแล้วจะแสดงปุ่ม ไปที่ใบสั่งขาย ระบบโหลด getOrderDetail(convertedOrderId) ก่อนเปิด product workspace ใน order mode โดยยึดข้อมูลที่ persist ใน order เป็นหลักสำหรับสินค้า การรับ/จัดส่ง ที่อยู่ เส้นทาง พิกัด และ document flags จากนั้นจึงโหลด customer profile ด้วย customerCode เพื่อเติม tier, point, สิทธิประโยชน์ และที่อยู่ fallback หากโหลด profile ไม่ได้ ยังเปิด order จาก customer snapshot ที่บันทึกไว้ได้

ID ของรายการใบเบิก​

ตัวสร้าง payload ทั้งสองตัวเลือกค่าแรกที่ไม่ใช่ null/undefined ตามลำดับ itemId → borrowItemId → borrowDetailId → id แปลง string ตัวเลขเป็น number และรับเฉพาะจำนวนเต็มบวกที่เป็น safe integer ไม่ใช้รหัสสินค้าหรือ ID ของหัวใบเบิกแทน ID รายการ การแปลงใบสั่งขายจะปฏิเสธการเลือกว่าง/รายการที่ไม่อยู่ในข้อมูลปัจจุบัน และตัด ID ซ้ำออก.

การพิมพ์​

มี 2 เส้นทางพิมพ์:

เอกสารพฤติกรรม
Borrow packพิมพ์เอกสารใบเบิกฝั่งลูกค้า/แพ็กสินค้า
Borrow storageพิมพ์เอกสารใบเบิกฝั่งคลัง

ทั้งสองปุ่มต้องมีสินค้าอย่างน้อย 1 รายการ สถานะ PARTIAL_CONVERTED ยังพิมพ์ได้ การพิมพ์ไม่เปลี่ยนสถานะเอกสารเอง

สิทธิ์และ Guard​

สิทธิ์กฎ
Terminal menu keyborrowFish
deptCode ที่เห็นเมนู-2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 13, 14, 30, 16, 20, 22, 23, 27, 28
สร้างใบเบิกAdmin หรือ dept -2, 1, 2, 4, 7, 8, 9, 13, 16, 21, 29
ยกเลิก/ลบใบเบิกAdmin; เจ้าของเอกสารเมื่อสถานะ NEW; หรือแผนก/ผู้ใช้ที่อนุญาตเมื่อสถานะ NEW หรือ WAITING
แผนกยกเลิก-2, 1, 2, 4, 9; dept 8 และ 22 override ได้ใน NEW/WAITING
user code override ยกเลิก008045, 012521, 012508, 008051, 012388, 012428, 008060
B1 processingAdmin หรือ dept 2, 8, 12, 22, 28
Owner processingเจ้าของเอกสาร process ได้ถ้า borrow type ไม่ใช่ DESTROY
Processing guardสาขาหลักที่มีสิทธิ์ B1 หรือเจ้าของเอกสารตามเงื่อนไขด้านบน
แก้ draftสถานะต้องเป็น NEW และผู้ใช้เป็นเจ้าของ draft หรือเอกสารยังไม่ persist
แก้ประเภทการเบิกทำได้ก่อน persist หรือโดยเจ้าของเอกสาร

Mobile vs POS Terminal​

หัวข้อMobilePOS Terminal
การนำทางborrow stack list/detailHomeScreen split-pane menu borrowFish
Product flowborrow detail screen และ product-flow hookborrow detail form, panels, product picker
บันทึกหัวเอกสารบันทึกผ่าน borrow service เมื่อ detail เดิมเปลี่ยนบันทึก raw detail ผ่าน HomeScreen handlers
Validationbusiness rules เดียวกันใน mobile componentsbusiness rules เดียวกันใน HomeScreen handlers
พิมพ์printBorrowPack, printBorrowStorageใช้ print utilities เดียวกัน
ConvertModal เลือกสินค้าร่วมกันและส่ง borrowItemIds ที่เลือกHomeScreen และ Storefront POS ใช้ Modal/contract เดียวกัน; Storefront เปิด convertedOrderId ใน order mode ได้
คืนหลังแปลงบางส่วนcompleteBorrowStock ส่ง ID รายการที่เหลือไป /return-itemsทั้งสอง flow ของ Terminal ใช้การเลือก API และกรอง lots เหมือนกัน

API และ Data Map​

OperationService
ListgetBorrows
DetailgetBorrowDetail(borrowId)
Create/update/delete headercreateBorrow, updateBorrow, deleteBorrow
Item CRUDcreateBorrowItem, updateBorrowItem, deleteBorrowItem
ConfirmconfirmBorrow
AcceptacceptBorrow
Complete/cut stock dispatchercompleteBorrowStock(borrowId, detail)
Normal completionapproveBorrow
Return remaining itemsreturnBorrowItems(borrowId, payload)
Retry failed sub-borrowretryBorrow
Convert to sales orderconvertBorrow(borrowId, payload)

Developer Handoff Map​

ส่วนโค้ด
Mobile permissions/helpersapps/mobile/src/utils/borrow.ts
Mobile list/detailapps/mobile/src/screens/borrowStack/borrowListScreen, borrowDetailScreen
Mobile product flowapps/mobile/src/screens/borrowStack/borrowDetailScreen/useBorrowProductFlow.ts
Terminal permissions/panelsapps/pos-terminal/src/screens/HomeScreen/BorrowPanels.tsx
Terminal detailapps/pos-terminal/src/screens/HomeScreen/BorrowDetailForm.tsx
Terminal handlersapps/pos-terminal/src/screens/HomeScreen/index.tsx
Shared conversion UIpackages/rn-ui/src/BorrowConversionModal.tsx
Shared guards, status, item IDs and payloadspackages/utils/src/borrow.ts
Payload typespackages/types/src/borrow.ts
Storefront flowapps/pos-terminal/src/screens/StorefrontPosStack/borrowFlow.ts
Mobile/shared regression testsapps/mobile/__tests__/borrowConversionModal.test.tsx, borrowConversionService.test.ts, borrowPartialConversion.test.ts, borrowReturnItemsService.test.ts
Storefront regression testsapps/pos-terminal/src/screens/StorefrontPosStack/__tests__/borrowFlow.test.ts
Servicesapps/*/src/services/borrow.ts
Converted targetเอกสารใบสั่งขายและ order services

เอกสารที่เกี่ยวข้อง​