ขั้นตอนฝากเงิน
หน้านี้สรุป flow เมนู ฝากเงิน ตั้งแต่ list, detail/create form, แหล่งข้อมูลของแต่ละ input, query API, payload submit, และ action ที่เกี่ยวข้อง
ขอบเขต
เอกสารอ้างอิงจาก implementation หลัก:
apps/mobile/src/screens/depositStack/depositListScreen/index.tsxapps/mobile/src/screens/depositStack/depositDetailScreen/index.tsxapps/mobile/src/services/deposit.tsapps/mobile/src/services/common.tsapps/mobile/src/services/invoices.tsapps/pos-terminal/src/screens/HomeScreen/DepositDetailForm.tsxapps/pos-terminal/src/services/deposit.tsapps/pos-terminal/src/services/splitMenuData.ts
Mobile และ POS Terminal ใช้ contract ของ deposit เหมือนกันเกือบทั้งหมด ต่างกันหลัก ๆ คือ POS Terminal ใช้ split-pane form และ invoice picker ผ่าน getInvoiceSplitList() ก่อน normalize เป็น SplitListItem
สรุปเมนู
เมนู ฝากเงิน ใช้สำหรับสร้างรายการนำเงินสดไปฝากเข้าบัญชีธนาคาร โดยข้อมูลหลักประกอบด้วย:
- สาขา/คลังที่รับผิดชอบ
- พนักงานผู้รับผิดชอบ
- บริษัท
- วันที่ส่ง
- บัญชีเงินสดต้นทาง
- บัญชีธนาคารปลายทาง
- จำนวนเงิน
- หมายเหตุ หรือรายการใบแจ้งหนี้ที่เลือก
รายการฝากเงินมี state ที่แสดงใน list:
| เงื่อนไขข้อมูล | ความหมายใน UI |
|---|---|
status === "DELETED" | รายการถูกลบ |
RefKey == null | ส่งเข้า B1 ไม่สำเร็จ / รอส่งใหม่ |
RefKey != null | ส่งสำเร็จ |
ทางเข้า
จาก list ปกติ:
navigation.navigate('DepositDetail', {
depositId: null,
})
จากการกดรายการเดิม:
navigation.navigate('DepositDetail', {
depositId: item.depositId,
})
จาก transport flow จะเปิด create form พร้อม context เพิ่ม เช่น transportId, transportPayload, amount, driverName, depositDate, companyCode และข้อมูลสาขา/คลัง เพื่อให้สร้างฝากเงินแล้วกลับไป update transport ต่อ
API รายการฝากเงิน
List โหลดผ่าน getDeposits()
GET {POS_SERVER}{PATH}/deposits
Query:
| Query | ค่า |
|---|---|
page | page ปัจจุบัน เริ่มที่ 1 |
pageLimit | 200 |
sort | DESC |
sortBy | CreatedAt |
search | keyword จากช่องค้นหา |
whs | selectedBranch.whsGrpCode จาก getBranch() |
hasRetry | "true" เมื่อเลือก filter รอส่งใหม่, ไม่เช่นนั้นส่ง empty string |
ใช้ API นี้ในกรณี:
- เข้า list ครั้งแรก
- refresh
- search
- load more
- toggle filter
ทั้งหมด/รอส่งใหม่ - reload หลัง delete หรือ retry
การเริ่มต้นหน้าสร้างและรายละเอียด
เมื่อเข้า DepositDetail จะโหลดข้อมูลตั้งต้นพร้อมกัน:
getBranch()
getUser()
getCompanies()
getCompanies() ยิง:
GET {MST_API}{PATH}/companies
โหมดรายละเอียด
ถ้า depositId != null จะโหลด detail:
GET {POS_SERVER}{PATH}/deposits/{depositId}
แล้ว fill form จาก response:
| Field | Source |
|---|---|
branchId | detail.branchId |
branchName | `detail.branchName |
whGrpCode | detail.whGrpCode |
whGrpName | detail.whGrpName |
userCode | detail.userCode |
userResponsible | `detail.userResponsible |
companyCode | detail.companyCode |
amount | detail.amount |
depositDate | detail.depositDate |
cashAccount | detail.cashAccount |
cashName | detail.cashName |
bankName | detail.bankName |
bankAccount | detail.bankAccount |
notes | detail.notes |
depositNo | detail.depositNo |
createdAt | detail.createdAt |
หลัง fill detail จะโหลดบัญชีเงินสด/บัญชีธนาคารด้วย companyCode และ detail.whGrpCode
โหมดสร้างใหม่
ถ้า depositId == null จะสร้าง default form จาก route params ก่อน ถ้าไม่มีจึง fallback ไป branch/user:
| Field | Default |
|---|---|
companyCode | `route.params.companyCode |
companyName | `route.params.companyName |
whGrpCode | `route.params.whGrpCode |
whGrpName | `route.params.whGrpName |
branchId | `route.params.branchId |
branchName | whGrpName |
userCode | `route.params.userCode |
userResponsible | `route.params.driverName |
amount | `route.params.amount |
depositDate | `route.params.depositDate |
notes | `route.params.notes |
บัญชีถูกโหลดด้วย accountWhGrpCode:
accountWhGrpCode = branch.isMain === false
? branch.BranchCode
: whGrpCode
แหล่งข้อมูลสำหรับกรอกฟอร์ม
บริษัท
แหล่งข้อมูล:
GET {MST_API}{PATH}/companies
Search ใน modal เป็น client-side filter จาก companyCode และ companyName
เมื่อเลือกบริษัท:
companyCode = item.companyCode
companyName = item.companyName
cashAccount = ''
cashName = ''
bankName = ''
bankAccount = ''
จากนั้น reload บัญชีด้วย loadBankAndCashOptions(companyCode, form.whGrpCode)
บัญชีเงินสด
แหล่งข้อมูลผ่าน getCashAccounts(whGrpCode):
GET {MST_API}{PATH}/banks
?whGrpCode={whGrpCode}
&acctType=CASH
หลังได้ response จะ dedupe ด้วย acctCode
เมื่อเลือก:
cashAccount = item.acctCode || item.AcctCode
cashName = item.acctName || item.AcctName
Search ใน modal เป็น client-side filter จาก account code และ account name
บัญชีธนาคาร
แหล่งข้อมูลผ่าน getBankAccounts({ companyCode, whGrpCode }):
GET {MST_API}{PATH}/banks
?whGrpCode={whGrpCode}
&acctType=BANK
&companyCode={companyCode}
&ignoreBank={branch.isMain ? 1 : 0}
เมื่อเลือก:
bankAccount = item.acctCode || item.AcctCode
bankName = item.acctName || item.AcctName
Search ใน modal เป็น client-side filter จาก account code และ account name
วันที่ส่ง
Default:
depositDate = dayjs().format('YYYY-MM-DD')
ถ้าเปิด ส่งสลิปย้อนหลัง:
depositDate = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
ถ้าปิดกลับ:
depositDate = dayjs().format('YYYY-MM-DD')
Toggle นี้แสดงเฉพาะ create mode และไม่ใช่ transport flow
จำนวนเงิน
มี 2 mode:
| Mode | ที่มาของจำนวนเงิน | แก้ไขได้หรือไม่ |
|---|---|---|
| ไม่เลือกใบแจ้งหนี้ | ผู้ใช้กรอก form.amount หรือ route param จาก transport | แก้ได้เฉพาะ create mode |
| เลือกใบแจ้งหนี้ | sum ยอดสุทธิของ invoice ที่เลือก | แก้ไม่ได้ |
สูตรยอดสุทธิ invoice:
Math.floor(Number(invoice.amount))
- Number(invoice.discount)
- Number(invoice.useCreditNote)
ใบแจ้งหนี้
ปุ่มเลือกใบแจ้งหนี้แสดงเมื่อ:
isCreateMode && !isTransportFlow && deptCode !== 14 && deptCode !== 30
Mobile โหลดผ่าน getInvoices():
GET {POS_SERVER}{PATH}/invoices
Query:
| Query | ค่า |
|---|---|
page | page ปัจจุบัน |
pageLimit | PAGESIZE |
branchId | selectedBranch.branchId |
sort | DESC |
sortBy | invoiceId |
search | keyword ใน invoice modal |
shippingType | empty string |
invoiceStatus | empty string |
toB1 | empty string |
inStock | empty string |
withOutCN | empty string |
dateFrom | วันนี้ย้อนหลัง 300 วัน เวลา 00:00:00 |
dateTo | วันนี้ เวลา 23:59:59 |
whGrpCode | selectedBranch.whsGrpCode |
POS Terminal ใช้ getInvoiceSplitList({ tab: "all", dateFrom, dateTo }) ซึ่งสุดท้ายเรียก getInvoices() ด้วย filter ชุดเดียวกัน แล้ว normalize เป็น split-list item
เมื่อเลือก invoice:
- toggle เลือก/ยกเลิกด้วย
invoiceId || invoiceStorageId - amount ถูกคำนวณจาก invoice ที่เลือกทั้งหมด
- notes ถูกสร้างจาก
invoiceNoคั่นด้วย, - ช่องจำนวนเงินและหมายเหตุจะเป็น read-only
หมายเหตุ
ถ้าไม่เลือก invoice:
notes = form.notes.trim()
ถ้าเลือก invoice:
notes = selectedInvoices
.map(invoice => invoice.invoiceNo)
.filter(Boolean)
.join(', ')
การตรวจสอบก่อนบันทึก
ก่อน submit ต้องผ่านเงื่อนไข:
companyCode ต้องมี
bankAccount ต้องมี
cashAccount ต้องมี
ถ้าไม่ได้เลือก invoice ต้องเช็คเพิ่ม:
amount ต้องเป็นตัวเลขและมากกว่า 0
notes ต้องไม่ว่าง
เมื่อผ่าน validation จะขึ้น confirm dialog:
- ถ้าเลือก invoice:
ฝากเงินจำนวนหลายรายการ ไปที่บัญชีเงินสด... บัญชีธนาคาร... - ถ้าไม่เลือก invoice:
ฝากเงินจำนวน {amount} ไปที่บัญชีเงินสด... บัญชีธนาคาร...
กดยืนยันแล้วจึง submit จริง
Payload ที่ส่งบันทึก
Payload ถูกสร้างด้วย buildDepositPayload(form, selectedInvoices)
{
"branchId": 1,
"branchName": "คลังหลัก",
"whGrpCode": "01",
"whGrpName": "คลังหลัก",
"userCode": "012345",
"userResponsible": "ชื่อ พนักงาน",
"companyCode": "BFP",
"amount": 12000,
"depositDate": "2026-07-04",
"cashAccount": "111100",
"cashName": "เงินสดหน้าร้าน",
"bankName": "KBank",
"bankAccount": "111200",
"notes": "INV-001, INV-002"
}
Important normalization:
branchId = Number(form.branchId)
branchName = form.whGrpName || form.branchName
companyCode = form.companyCode.replace('DB', '')
amount = selectedInvoices.length > 0
? sum(invoice net amount)
: Math.floor(Number(form.amount))
notes = selectedInvoices.length > 0
? invoiceNo list
: form.notes.trim()
Submit API:
POST {POS_SERVER}{PATH}/deposits
Content-Type: application/json; charset=utf-8
Body คือ DepositPayload
หลังบันทึก
ขั้นตอนฝากเงินปกติ
เมื่อ create สำเร็จ:
navigation.replace('DepositList')
แล้ว list จะ reload เมื่อ focus
ขั้นตอนฝากเงินจากขนส่ง
เมื่อ create deposit สำเร็จและมี transportId + transportPayload:
updateTransport({
transportId,
payload: transportPayload,
})
Mobile จะ navigation.goBack() กลับไปหน้า transport detail
POS Terminal จะเรียก onTransportSaved, onCreated, และ onRefreshList
Action อื่น
ลบรายการฝากเงิน
แสดง action ลบเมื่อ user อยู่ใน department ที่อนุญาต:
-2, 1, 2, 3, 4, 9
API:
DELETE {POS_SERVER}{PATH}/deposits/{depositId}
หลังลบ reload list หน้าแรก
ส่งรายการฝากเงินอีกครั้ง
แสดง action retry เมื่อ item.RefKey == null
กด retry ได้เฉพาะ:
- เจ้าของรายการ:
item.userCode === currentUser.userCode - หรือ user override:
012265
API:
POST {POS_SERVER}{PATH}/deposits/{depositId}/retry
Body เป็น {}
หลัง retry reload list หน้าแรก
สิทธิ์
ปุ่มสร้าง
Mobile list แสดงปุ่มสร้างเมื่อ:
- user มี full access ของ non-main warehouse
- หรือ department อยู่ในชุด:
-2, 1, 2, 3, 4, 7, 29, 8, 9, 13, 14, 30, 16
ตัวเลือกใบแจ้งหนี้
ถึงแม้สร้าง deposit ได้ แต่ invoice picker จะไม่แสดงสำหรับ department:
14, 30
และไม่แสดงใน transport flow
สรุป API
| Purpose | Method | Endpoint |
|---|---|---|
| Deposit list | GET | {POS_SERVER}{PATH}/deposits?page=...&pageLimit=200&sort=DESC&sortBy=CreatedAt&search=...&whs=...&hasRetry=... |
| Deposit detail | GET | {POS_SERVER}{PATH}/deposits/{depositId} |
| Create deposit | POST | {POS_SERVER}{PATH}/deposits |
| Update deposit service exists | PATCH | {POS_SERVER}{PATH}/deposits/{depositId} |
| Delete deposit | DELETE | {POS_SERVER}{PATH}/deposits/{depositId} |
| Retry deposit | POST | {POS_SERVER}{PATH}/deposits/{depositId}/retry |
| Companies | GET | {MST_API}{PATH}/companies |
| Cash accounts | GET | {MST_API}{PATH}/banks?whGrpCode=...&acctType=CASH |
| Bank accounts | GET | {MST_API}{PATH}/banks?whGrpCode=...&acctType=BANK&companyCode=...&ignoreBank=... |
| Invoice picker | GET | {POS_SERVER}{PATH}/invoices?...dateFrom=...&dateTo=...&whGrpCode=... |
| Transport callback | transport service | updateTransport({ transportId, payload }) |