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

ใบแจ้งหนี้

เมนู ใบแจ้งหนี้ ใช้ติดตาม invoice ที่สร้างจาก flow ใบสั่งขาย, ตรวจรายละเอียดสินค้า/ลูกค้า/สถานะ, ส่งข้อมูลเข้า B1, จัดการคำขอยกเลิก, เปิดใบสั่งขายจาก invoice เดิม และดูสรุปยอดขายราย user/รายสาขาของวันปัจจุบัน

ในระบบนี้ invoice ไม่ใช่จุดเริ่มสร้างยอดขายหลัก จุดเริ่มหลักยังอยู่ที่ ใบสั่งขาย แล้วจึงสร้าง invoice และชำระเงิน จากนั้นหน้าใบแจ้งหนี้จะเป็นหน้าตรวจสอบและปิดงานต่อ

ดู workflow หลักดูปุ่มสรุปยอดขายดูสิทธิ์

ทางเข้าในแอป​

แอปทางเข้าไฟล์/จุดทำงานหลัก
MobileHome -> InvoiceList -> InvoiceDetailinvoiceListScreen, invoiceDetailScreen
POS Terminalเมนู invoice ใน split homeHomeScreen/index.tsx, ListPane.tsx, InvoicePanels.tsx

ทั้งสองฝั่งใช้ service boundary เดียวกัน:

งานEndpoint / service
โหลดรายการ invoiceGET /invoices ผ่าน getInvoices(...)
โหลดรายการรอส่ง B1GET /invoices?toB1=0 ผ่าน getInvoicesPendingB1(...)
โหลดรายละเอียดGET /invoices/{invoiceId}?includeLot=true
ส่งเข้า B1POST /payment/to-b1 payload เป็น array ของ invoiceId
retry ส่ง payment/incomingPOST /payment/{invoiceId}/retry
สรุปยอดขายGET /invoices/summary
อนุมัติคำขอยกเลิกPOST /invoices/{invoiceId}/approve
ไม่อนุมัติคำขอยกเลิกPOST /invoices/{invoiceId}/reject
ขอ cancel invoicePOST /invoices/{invoiceId}/cancel
ยกเลิกโดยไม่มีคำขอPOST /invoices/{invoiceId}/cancel-direct, payload { notes: string }

Workflow หลัก​

รายการใบแจ้งหนี้​

รายการหลักโหลดด้วย getInvoices(...) โดยส่ง context ของสาขาที่เลือก:

  • branchId จาก selected branch
  • whGrpCode จาก selected warehouse group
  • page, pageLimit, search
  • filter พื้นฐาน: shippingType, invoiceStatus, toB1, inStock, withOutCN, dateFrom, dateTo
  • sort เป็น DESC ตาม invoiceId

พฤติกรรมของรายการ:

  • search ใช้ค้นหาเลขใบแจ้งหนี้, ลูกค้า, หรือใบสั่งขาย
  • มีแท็บ ทั้งหมด และ รอส่ง B1
  • รายการถูก dedupe ด้วย invoiceId หรือ invoiceStorageId
  • กดรายการใน mode ปกติจะเปิดรายละเอียด invoice
  • ใน manual payment mode การกดรายการจะเป็นการเลือก/ยกเลิกเลือก invoice เพื่อส่งข้อมูล

รายละเอียดใบแจ้งหนี้​

หน้า detail โหลด GET /invoices/{invoiceId}?includeLot=true แล้วแสดง:

  • เลขใบแจ้งหนี้, ลูกค้า, เลขใบสั่งขาย, ผู้เปิดใบ, วันที่ invoice
  • summary ของ invoice ผ่าน InvoiceSummarySection
  • รายการสินค้าและจำนวนสินค้า
  • shipping invoices ที่ผูกกับ order พร้อมปุ่ม retry ส่งข้อมูลขนส่งหรือ incoming ถ้ามีสถานะที่ retry ได้
  • ปุ่ม เปิดใบสั่งขายจากใบแจ้งหนี้ เมื่อ user มีสิทธิ์สร้างใบสั่งขาย

การเปิดใบสั่งขายจาก invoice จะส่งค่าเข้า order detail:

{
duplicateInvoiceId,
duplicateInvoiceNo,
duplicateDocumentMove,
duplicateVehicleRelease,
}

ถ้าเป็นสาขาหลัก ระบบเปิด order draft ได้เลย ถ้าไม่ใช่สาขาหลัก จะมี modal ให้เลือก vehicleRelease และ documentMove ก่อนเปิด order

ส่งข้อมูลเข้า B1​

การส่ง B1 ทำได้จาก swipe/action รายใบ หรือเลือกหลายใบในโหมดปรับสถานะการชำระเงิน แล้วเรียก POST /payment/to-b1

ก่อนส่งจะตรวจ:

เงื่อนไขรายละเอียด
มี invoiceIdใช้ invoiceId หรือ fallback invoiceStorageId
มี refKeyถ้าไม่มีจะไม่ส่ง
toB1 = 0ถ้าส่งแล้ว action จะแสดงเป็น disabled
orderStatus = CONFIRMEDถ้า order ยังไม่ถูกชำระ จะขึ้นข้อความว่าใบสั่งขายยังไม่ได้ถูกชำระ
ไม่ติดคำขอยกเลิกถ้ามี canceledNote และยังไม่ถูก reject จะ block
วัน invoiceuser ทั่วไปส่งได้เฉพาะวันนี้/เมื่อวาน; dept 3 ยกเว้น date guard

ปุ่มสรุปยอดขาย​

ปุ่ม สรุปยอดขาย อยู่ในเมนู header ของหน้าใบแจ้งหนี้:

แอปตำแหน่งปุ่ม
Mobilemenu item ใน CustomerManagementListHeader ชื่อ สรุปยอดขาย
POS Terminalicon bar-chart-2 ข้างหัวเมนู ใบแจ้งหนี้ ใน ListPane

เมื่อกดปุ่ม ระบบเรียก /invoices/summary สองครั้งในช่วงวันปัจจุบัน:

ข้อมูลใน modal แยกบริษัท BFP และ NFF และมี block รวม:

ค่าวิธีคิด
จำนวน invoiceinvCount
ยอดขายamountTotal
ส่วนลดคะแนน/คูปองdiscount
ส่วนลดเครดิตuseCreditNote
คงเหลือamountTotal - discount - useCreditNote
น้ำหนักรวมweightTotal
จำนวนรายการ CODcodCount
ค่าธรรมเนียม COD รวมcodTotal

ในแต่ละบริษัทและยอดรวม ระบบแสดงข้อมูล COD เป็น ค่าธรรมเนียม COD : codCount @ codTotal โดยเป็นข้อมูลสรุปเพิ่มเติม และยังคำนวณคงเหลือด้วย amountTotal - discount - useCreditNote เหมือนเดิม ไม่หัก codTotal ออกจากคงเหลือ

โหมดปรับสถานะการชำระเงินด้วยตนเอง​

โหมดนี้เปิดจากเมนูเดียวกับสรุปยอดขาย:

  • Mobile ใช้ label ปรับสถานะการชำระเงินด้วยตนเอง
  • Terminal ใช้ icon check-square; เมื่อเปิดแล้ว icon เปลี่ยนเป็น x เพื่อยกเลิกโหมด
  • ระบบเปลี่ยนไปโหลดแท็บ รอส่ง B1
  • user ทั่วไปจะถูกจำกัดช่วงวันที่เป็นวันนี้และเมื่อวาน และส่ง userCode ของตัวเอง
  • dept 3 เห็นทุก user และไม่ใช้ date guard ใน manual mode
  • เลือกหลาย invoice แล้วกด ส่งข้อมูล
  • รายการที่เลือกต้องไม่ถูก cancel, ต้องมี refKey, และ order ต้อง CONFIRMED

คำขอยกเลิกใบแจ้งหนี้​

Mobile และ Terminal ใช้เงื่อนไขยกเลิกเดียวกันในหน้ารายการใบแจ้งหนี้โหมดปกติ ปัดการ์ดเพื่อแสดงปุ่ม โดยรองรับทั้งการจัดการคำขอที่มีอยู่และการยกเลิกโดยไม่มีคำขอ

การจัดการคำขอที่มีอยู่​

คำขอเริ่มจากหน้า จัดสินค้าหน้าร้าน หรือ สายส่ง เมื่อกรอกเหตุผลและส่ง POST /invoices/{invoiceId}/cancel พร้อม { notes }

คำขอค้างพิจารณาคือรายการที่มี canceledNote และ rejectCanceledAt ว่าง เจ้าของใบแจ้งหนี้ตาม userCode หรือผู้ใช้รหัส 012265, 999999 จะเปิด Modal อนุมัติได้ ปุ่มอนุมัติเรียก /approve และไม่อนุมัติเรียก /reject จากนั้นรีเฟรชรายการ ใบที่เป็น CANCELED ทำรายการยกเลิกซ้ำไม่ได้

หากมีคำขอค้างอยู่จะใช้ขั้นตอนนี้ก่อนการยกเลิกโดยไม่มีคำขอ การมี isAdmin = 1 เพียงอย่างเดียวไม่ได้ให้สิทธิ์จัดการคำขอเดิม

การยกเลิกโดยไม่มีคำขอ​

ต้องครบทุกเงื่อนไข:

  • ไม่มีคำขอยกเลิกค้างพิจารณา
  • API ส่ง hasDelivery: null มาโดยตรง หากไม่มีฟิลด์นี้ หรือเป็น 0 หรือ 1 จะไม่เข้าเงื่อนไข
  • ใบแจ้งหนี้ไม่เป็น CANCELED
  • ผู้ใช้เป็นเจ้าของใบแจ้งหนี้ หรือมี isAdmin = 1 จากข้อมูลล็อกอินสำเร็จ รองรับค่า string "1" ด้วย รหัสผู้ใช้พิเศษเพียงอย่างเดียวไม่ได้ให้สิทธิ์ยกเลิกแบบนี้

เมื่อกดปุ่ม จะเปิด Modal ยกเลิกใบแจ้งหนี้โดยไม่มีคำขอ แสดงเลขใบแจ้งหนี้ ช่อง หมายเหตุ หลายบรรทัด ปุ่ม ปิด และ ตกลง

  1. กรอกหมายเหตุแล้วกดตกลง ระบบตัดช่องว่างหัวและท้ายข้อความ
  2. หากไม่กรอกหรือกรอกเฉพาะช่องว่าง จะขึ้น กรุณาระบุหมายเหตุการยกเลิก ใน Sweet Alert global และไม่เรียก API
  3. เมื่อกรอกถูกต้องจะส่งข้อมูลดังนี้ โดยชื่อฟิลด์ต้องเป็น notes:
POST /api/invoices/{invoiceId}/cancel-direct
Content-Type: application/json
{
"notes": "ลูกค้าขอยกเลิกใบแจ้งหนี้"
}

ระหว่างส่งข้อมูล ระบบป้องกันการกดซ้ำ การแก้ไข และการปิด Modal เมื่อสำเร็จจะปิด Modal และรีเฟรชรายการปัจจุบัน หาก API แจ้งข้อผิดพลาดจะแสดง message ใน Sweet Alert global โดยมีข้อความสำรอง ไม่สามารถยกเลิกใบแจ้งหนี้ได้ และเก็บหมายเหตุไว้ให้แก้ไขหรือลองใหม่ ปุ่มปิดจะออกจากฟอร์มโดยไม่ส่งข้อมูล และเมื่อเปิดใหม่ช่องหมายเหตุจะเริ่มว่าง

การแสดงปุ่มเมื่อปัดการ์ด​

ตารางนี้ใช้ในโหมดรายการปกติ ไม่ใช่โหมดปรับสถานะการชำระเงินด้วยตนเอง:

เงื่อนไขปุ่มเมื่อปัดการ์ด
มีคำขอและผ่านสิทธิ์จัดการคำขอปุ่มยกเลิกที่เปิด Modal อนุมัติ/ไม่อนุมัติ
ผ่านเงื่อนไขและสิทธิ์ยกเลิกโดยไม่มีคำขอปุ่มยกเลิกที่เปิด Modal กรอกหมายเหตุ
ยังยกเลิกไม่ได้ และ hasDelivery !== nullแสดงปุ่ม ยังไม่มีคำขอยกเลิก ที่ไม่ทำรายการ เพื่อให้ปัดการ์ดดีบัคได้
ยังยกเลิกไม่ได้ และ hasDelivery === nullซ่อนปุ่มยกเลิก

ข้อความปุ่มที่ทำรายการไม่ได้เป็นข้อความสำรองเมื่อไม่ผ่านเงื่อนไขใดก็ตาม รวมถึงไม่มีสิทธิ์หรือใบถูกยกเลิกแล้ว จึงไม่ได้ยืนยันว่าไม่เคยมีคำขอ กรณี API ไม่ส่ง hasDelivery มาก็จะเข้าฝั่งแสดงปุ่มนี้ เพราะไม่ใช่ค่า null โดยตรง

สิทธิ์และเงื่อนไข​

ส่วนเงื่อนไข
เห็นเมนูใบแจ้งหนี้MENU_INVOICE.has(deptCode) หรือ admin/full non-main warehouse access
dept ที่เห็นเมนู-2, 1, 2, 3, 4, 7, 8, 9, 13, 16, 20, 21, 22, 23, 27, 29
ส่ง B1 รายใบเจ้าของ invoice, dept 3, หรือ override user 012265, 999999
ส่ง B1 เกินช่วงวันนี้/เมื่อวานเฉพาะ dept 3
จัดการคำขอยกเลิกเจ้าของ invoice หรือ override user 012265, 999999
ยกเลิกโดยไม่มีคำขอเจ้าของ invoice หรือ isAdmin = 1 จากล็อกอิน; ไม่มีคำขอค้าง, hasDelivery = null, ไม่เป็น CANCELED
เปิดใบสั่งขายจาก invoiceต้องผ่าน canCreateSalesOrder
canCreateSalesOrder หลักadmin, full non-main warehouse access, หรือ dept -2, 1, 2, 4, 7, 8, 9, 13, 16, 21, 29

ข้อควรระวัง: การ “เห็นเมนู” กับการ “ทำ action” ไม่ใช่สิทธิ์ชุดเดียวกัน เช่น dept 20 เห็นเมนูใบแจ้งหนี้และบาง shortcut ใน Home ได้ แต่ action เปิดใบสั่งขายจากใบแจ้งหนี้ยังต้องผ่าน canCreateSalesOrder ของหน้ารายละเอียด

ความต่าง Mobile และ POS Terminal​

เรื่องMobilePOS Terminal
รูปแบบ UIstack screen: list -> detailsplit pane: menu, list, detail
ปุ่มสรุปยอดขายheader menu itemicon ข้างหัวเมนู invoice
รายละเอียด invoiceInvoiceDetailScreenInvoicePanels ใน panel detail
manual payment moderail ล่างมี ยกเลิก / ส่งข้อมูลheader action + action ใน list pane
duplicate ordernavigate ไป OrderDetailสร้าง/open order detail ใน split panel พร้อม progress
summary modalInvoiceSummaryModal componentInvoiceSummaryModal ใน InvoicePanels.tsx

Developer Handoff Map​

งานMobilePOS Terminal
รายการ invoiceapps/mobile/src/screens/invoiceStack/invoiceListScreen/index.tsxapps/pos-terminal/src/screens/HomeScreen/index.tsx, ListPane.tsx
รายละเอียด invoiceapps/mobile/src/screens/invoiceStack/invoiceDetailScreen/index.tsxapps/pos-terminal/src/screens/HomeScreen/InvoicePanels.tsx
service invoiceapps/mobile/src/services/invoices.tsapps/pos-terminal/src/services/invoices.ts, splitMenuData.ts
summary modalapps/mobile/src/components/InvoiceSummaryModal/index.tsxInvoicePanels.tsx
สิทธิ์ Home menuapps/mobile/src/screens/homeScreen/index.tsxapps/pos-terminal/src/screens/HomeScreen/config.ts
สิทธิ์เปิด order จาก invoiceapps/mobile/src/utils/salesOrders.tscanCreateSalesOrder(...) ใน Terminal config

เงื่อนไขยกเลิกร่วมอยู่ที่ packages/utils/src/invoiceCancellation.ts ส่วน Modal ยกเลิกโดยไม่มีคำขอและการเรียก Sweet Alert global อยู่ที่ packages/rn-ui/src/InvoiceDirectCancelModal.tsx เทสต์ที่เกี่ยวข้องอยู่ใน apps/mobile/__tests__/invoiceCancelDirectService.test.ts, invoiceCancellation.test.ts และ invoiceDirectCancelModal.test.tsx

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