เรื่องเบิกค่าใช้จ่าย
ตั้งเรื่องเบิกและอ่านรายการเบิก พร้อมหมวดค่าใช้จ่ายที่ต้องอ้างอิง
เรื่องเบิกค่าใช้จ่ายคือชุดรายการที่พนักงานจ่ายเงินไปก่อนแล้วขอเบิกคืน หนึ่งเรื่องมีชื่อเรื่องและรายการย่อยหนึ่งรายการขึ้นไป แต่ละรายการต้องอ้างอิงหมวดค่าใช้จ่าย
ทุก response ในกลุ่มนี้ห่อด้วย data และวันที่เป็น Unix seconds
อ่านหมวดค่าใช้จ่ายก่อน
GET /expense-categories → อาร์เรย์ใน data ไม่แบ่งหน้า
categoryId ของรายการต้องเป็นหมวดที่มีอยู่จริงในองค์กรนี้ จึงควรดึงมาครั้งเดียวแล้วเก็บไว้ใช้
curl -X GET "$ABSERP_API_URL/openapi/v1/expense-categories?isActive=true" \
-H "Authorization: Bearer $ABSERP_API_KEY"{
"success": true,
"data": [
{
"id": 3,
"nameTh": "ค่าเดินทาง",
"nameEn": "Travel",
"icon": "car",
"isSystem": true,
"isOther": false,
"isActive": true,
"sortOrder": 1,
"requiredFields": ["tripDestination", "tripPurpose"],
"accountCode": "5210"
}
]
}requiredFields เป็นส่วนที่มีประโยชน์ที่สุด เพราะบอกว่าหมวดนี้บังคับให้กรอกฟิลด์ที่ปกติไม่บังคับข้อใด หมวดค่าเดินทางมักต้องการ tripDestination และ tripPurpose ส่วนหมวดค่าที่พักต้องการ checkinDate และ checkoutDate ควรอ่านค่านี้แล้วปรับฟอร์มตาม ไม่ควรเขียนกฎฝังไว้ในโค้ด
isActive เป็นตัวกรองเดียวที่ endpoint นี้รับ
สร้างเรื่องเบิก
POST /expense-claims → 201 คืนเรื่องเบิกใน data
ต้องระบุ title และควรส่งรายการย่อยมาพร้อมกันในคำขอเดียว เพราะ endpoint ระดับรายการย่อยไม่ได้เปิดใน Open API เรื่องที่สร้างโดยให้ items ว่างไว้จึงเพิ่มรายการทีหลังจากระบบที่เชื่อมต่อไม่ได้
curl -X POST "$ABSERP_API_URL/openapi/v1/expense-claims" \
-H "Authorization: Bearer $ABSERP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "เดินทางพบลูกค้า กรุงเทพฯ เดือนกันยายน",
"note": "ปฏิบัติงานที่หน้างานสองวัน",
"reimbursementMethod": "bank_transfer",
"items": [
{
"categoryId": 3,
"expenseDate": 1757289600,
"description": "ค่าแท็กซี่จากสนามบิน",
"amount": 420,
"currencyCode": "THB",
"paidWith": "cash",
"tripDestination": "กรุงเทพฯ",
"tripPurpose": "พบลูกค้า",
"hasTaxInvoice": false
}
]
}'ฟิลด์ระดับเรื่อง
| ฟิลด์ | ชนิด | จำเป็น | หมายเหตุ |
|---|---|---|---|
title | string | ใช่ | ไม่เกิน 255 ตัวอักษร |
note | string | ไม่ | ไม่เกิน 2000 ตัวอักษร |
reimbursementMethod | string | ไม่ | bank_transfer, promptpay หรือ cash |
claimantUserId | integer | ไม่ | ตั้งเรื่องแทนผู้อื่น ถ้าไม่ส่ง เรื่องจะเป็นของผู้ใช้เจ้าของคีย์ |
items | array | ไม่ | ดูด้านล่าง |
ฟิลด์รายการย่อย
| ฟิลด์ | ชนิด | จำเป็น | หมายเหตุ |
|---|---|---|---|
categoryId | integer | ใช่ | จาก GET /expense-categories |
expenseDate | integer | ใช่ | Unix seconds วันที่จ่ายเงิน |
amount | number | ใช่ | มากกว่าศูนย์ |
currencyCode | string | ใช่ | 3 ตัวอักษรพอดี เช่น THB |
paidWith | string | ใช่ | cash, personal_card, personal_transfer หรือ company_card |
description | string | ไม่ | ไม่เกิน 2000 ตัวอักษร |
vendorName | string | ไม่ | ชื่อร้านหรือผู้รับเงิน |
tripDestination | string | ไม่ | บางหมวดบังคับ |
tripPurpose | string | ไม่ | บางหมวดบังคับ |
checkinDate / checkoutDate | integer | ไม่ | Unix seconds สำหรับค่าที่พัก |
hasTaxInvoice | boolean | ไม่ | ได้รับใบกำกับภาษีหรือไม่ |
vatAmount | number | ไม่ | VAT ที่รวมอยู่ใน amount แล้ว |
amount เป็นยอดรวม VAT อยู่ข้างในแล้ว และ vatAmount บอกว่าเป็นเท่าไร ไม่ต้องนำสองค่ามาบวกกัน
ข้อมูลที่คืนกลับ
{
"success": true,
"data": {
"id": 12,
"claimNo": "EX-2026-0012",
"title": "เดินทางพบลูกค้า กรุงเทพฯ เดือนกันยายน",
"status": "draft",
"totalAmount": 420,
"claimantName": "สมหญิง ใจดี",
"reimbursementMethod": "bank_transfer",
"submittedAt": null,
"reviewedBy": null,
"reviewedAt": null,
"paidAt": null,
"createdAt": 1757289600,
"items": [
{
"id": 30,
"categoryId": 3,
"categoryNameTh": "ค่าเดินทาง",
"categoryNameEn": "Travel",
"expenseDate": 1757289600,
"description": "ค่าแท็กซี่จากสนามบิน",
"amount": 420,
"currencyCode": "THB",
"paidWith": "cash",
"hasAttachment": false,
"attachments": []
}
]
}
}totalAmount คำนวณจากรายการย่อย ให้ส่งยอดของแต่ละรายการแล้วอ่านยอดรวมกลับมา
ค้นหาเรื่องเบิก
GET /expense-claims → แบ่งหน้า อยู่ใน data เป็นมุมมองของผู้อนุมัติ ครอบคลุมทุกเรื่องในองค์กรที่ผู้ใช้เจ้าของคีย์มีสิทธิ์เห็น
| พารามิเตอร์ | หมายเหตุ |
|---|---|
status | ดูรายการสถานะด้านล่าง |
membershipId | กรองเฉพาะเรื่องของสมาชิกคนหนึ่ง |
categoryId | เรื่องที่มีรายการในหมวดนี้ |
fromDate / toDate | Unix seconds |
search | ค้นจากเลขที่เรื่องและชื่อเรื่อง |
page / limit | ดูการแบ่งหน้า |
GET /expense-claims/myไม่เปิดให้ใช้ผ่าน Open API (ใช้ได้ในเว็บแอปเท่านั้น) ให้ใช้membershipIdกรองรายการนี้แทน
แถวในรายการ ไม่มี items เพื่อให้ payload เล็ก หากต้องการรายการย่อยให้เรียกอ่านรายละเอียด
อ่านเรื่องเบิกรายรายการ
GET /expense-claims/{id} → คืนเรื่องเบิกใน data พร้อม items, attachments ของแต่ละรายการ และ claimantBank (bankId, bankCompany, accountName, accountNoMasked — เลขบัญชีปิดบังเหลือสี่ตัวท้าย และไม่มี endpoint ใดที่เปิดเผยเลขเต็ม)
แก้ไขเรื่องเบิก
PATCH /expense-claims/{id} แก้ข้อมูลส่วนหัวของเรื่อง คือ title, note, reimbursementMethod
endpoint ระดับรายการย่อย (
POST /expense-claims/{id}/items,PUT .../items/{itemId},DELETE .../items/{itemId}) ไม่เปิดให้ใช้ผ่าน Open API (ใช้ได้ในเว็บแอปเท่านั้น) ให้ส่งอาร์เรย์itemsทั้งชุดตอนสร้างเรื่อง
สถานะ
| สถานะ | ความหมาย |
|---|---|
draft | กำลังจัดเตรียม ยังแก้ได้ |
submitted | รออนุมัติ |
needs_revision | ถูกส่งกลับให้แก้พร้อมหมายเหตุ |
approved | อนุมัติแล้ว ยังไม่จ่าย |
rejected | ไม่อนุมัติ |
paid | จ่ายคืนแล้ว |
cancelled | ยกเลิก |
สถานะอ่านได้ แต่เปลี่ยนจากที่นี่ไม่ได้
ขั้นตอนอนุมัติทั้งชุด คือ
submit,approve,reject,request-revision,record-payment,cancelไม่เปิดให้ใช้ผ่าน Open API (ใช้ได้ในเว็บแอปเท่านั้น) เพราะการอนุมัติเงินคืนคือจุดที่ต้องมีคนตรวจ ระบบที่เชื่อมต่อจึงทำได้แค่สร้างเรื่อง แล้วให้คนดำเนินการต่อใน ABSERP
ใบเสร็จแนบ
การอ่านจะเห็นไฟล์ที่แนบไว้แล้ว รายการที่มีไฟล์แนบจะขึ้น hasAttachment: true และแต่ละรายการใน attachments มี originalFilename, mimeType, sizeBytes และ fileKey
การอัปโหลดและลบใบเสร็จ (
POST /expense-claims/{id}/items/{itemId}/attachments,DELETE .../attachments/{attachmentId}) ไม่เปิดให้ใช้ผ่าน Open API (ใช้ได้ในเว็บแอปเท่านั้น) เพราะเป็นการรับส่งไฟล์แบบ multipart ไม่ใช่ส่วนหนึ่งของสัญญาการเชื่อมต่อข้อมูล
รหัสข้อผิดพลาด
| สถานะ | รหัส | สาเหตุ |
|---|---|---|
| 404 | ExpenseClaimNotFound | ไม่พบ :id, อยู่คนละองค์กร หรือเป็นของสมาชิกอื่น |
| 403 | Forbidden | ผู้ใช้เจ้าของคีย์ไม่ได้เป็นสมาชิกองค์กรนี้ หรือไม่ใช่เจ้าของ/ผู้ดูแลระบบพยายามแก้ไขเรื่องเบิกของคนอื่น หรือตั้งเรื่องแทน claimantUserId โดยไม่มีสิทธิ์ |
| 404 | ExpenseClaimantNotFound | claimantUserId ไม่ได้เป็นสมาชิกที่ใช้งานอยู่ในองค์กรนี้ |
| 404 | ExpenseCategoryNotFound | ไม่พบ categoryId ของรายการย่อยในองค์กรนี้ |
| 400 | ExpenseCategoryInactive | categoryId ของรายการย่อยชี้ไปที่หมวดที่องค์กรปิดใช้งานแล้ว |
| 400 | ExpenseClaimMissingRequiredField | รายการย่อยขาดฟิลด์ที่ requiredFields ของหมวดนั้นบังคับ |
| 400 | ExpenseClaimNotEditable | PATCH เรื่องเบิกที่สถานะไม่ใช่ draft หรือ needs_revision |
| 409 | ExpenseClaimNumberGenerationFailed | การออกเลขที่เรื่องเบิกชนกัน ให้ลองสร้างใหม่อีกครั้ง |
เรื่องเบิกที่อยู่นอกสมาชิกภาพของคุณจะตอบ 404 ExpenseClaimNotFound เมื่อคุณแค่พยายามอ่าน แต่ตอบ 403 Forbidden เมื่อคุณพยายามแก้ไข การอ่านตั้งใจไม่เปิดเผยว่าเรื่องเบิกนั้นมีอยู่ หากคุณไม่มีสิทธิ์เห็นมัน
ขั้นตอนถัดไป
อ่านต่อที่ ผู้ติดต่อ