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

เรื่องเบิกค่าใช้จ่าย

ตั้งเรื่องเบิกและอ่านรายการเบิก พร้อมหมวดค่าใช้จ่ายที่ต้องอ้างอิง

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

ทุก response ในกลุ่มนี้ห่อด้วย data และวันที่เป็น Unix seconds

อ่านหมวดค่าใช้จ่ายก่อน

GET /expense-categories → อาร์เรย์ใน data ไม่แบ่งหน้า

categoryId ของรายการต้องเป็นหมวดที่มีอยู่จริงในองค์กรนี้ จึงควรดึงมาครั้งเดียวแล้วเก็บไว้ใช้

bash
curl -X GET "$ABSERP_API_URL/openapi/v1/expense-categories?isActive=true" \
  -H "Authorization: Bearer $ABSERP_API_KEY"
json
{
  "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 ว่างไว้จึงเพิ่มรายการทีหลังจากระบบที่เชื่อมต่อไม่ได้

bash
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
      }
    ]
  }'

ฟิลด์ระดับเรื่อง

ฟิลด์ชนิดจำเป็นหมายเหตุ
titlestringใช่ไม่เกิน 255 ตัวอักษร
notestringไม่ไม่เกิน 2000 ตัวอักษร
reimbursementMethodstringไม่bank_transfer, promptpay หรือ cash
claimantUserIdintegerไม่ตั้งเรื่องแทนผู้อื่น ถ้าไม่ส่ง เรื่องจะเป็นของผู้ใช้เจ้าของคีย์
itemsarrayไม่ดูด้านล่าง

ฟิลด์รายการย่อย

ฟิลด์ชนิดจำเป็นหมายเหตุ
categoryIdintegerใช่จาก GET /expense-categories
expenseDateintegerใช่Unix seconds วันที่จ่ายเงิน
amountnumberใช่มากกว่าศูนย์
currencyCodestringใช่3 ตัวอักษรพอดี เช่น THB
paidWithstringใช่cash, personal_card, personal_transfer หรือ company_card
descriptionstringไม่ไม่เกิน 2000 ตัวอักษร
vendorNamestringไม่ชื่อร้านหรือผู้รับเงิน
tripDestinationstringไม่บางหมวดบังคับ
tripPurposestringไม่บางหมวดบังคับ
checkinDate / checkoutDateintegerไม่Unix seconds สำหรับค่าที่พัก
hasTaxInvoicebooleanไม่ได้รับใบกำกับภาษีหรือไม่
vatAmountnumberไม่VAT ที่รวมอยู่ใน amount แล้ว

amount เป็นยอดรวม VAT อยู่ข้างในแล้ว และ vatAmount บอกว่าเป็นเท่าไร ไม่ต้องนำสองค่ามาบวกกัน

ข้อมูลที่คืนกลับ

json
{
  "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 / toDateUnix 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 ไม่ใช่ส่วนหนึ่งของสัญญาการเชื่อมต่อข้อมูล

รหัสข้อผิดพลาด

สถานะรหัสสาเหตุ
404ExpenseClaimNotFoundไม่พบ :id, อยู่คนละองค์กร หรือเป็นของสมาชิกอื่น
403Forbiddenผู้ใช้เจ้าของคีย์ไม่ได้เป็นสมาชิกองค์กรนี้ หรือไม่ใช่เจ้าของ/ผู้ดูแลระบบพยายามแก้ไขเรื่องเบิกของคนอื่น หรือตั้งเรื่องแทน claimantUserId โดยไม่มีสิทธิ์
404ExpenseClaimantNotFoundclaimantUserId ไม่ได้เป็นสมาชิกที่ใช้งานอยู่ในองค์กรนี้
404ExpenseCategoryNotFoundไม่พบ categoryId ของรายการย่อยในองค์กรนี้
400ExpenseCategoryInactivecategoryId ของรายการย่อยชี้ไปที่หมวดที่องค์กรปิดใช้งานแล้ว
400ExpenseClaimMissingRequiredFieldรายการย่อยขาดฟิลด์ที่ requiredFields ของหมวดนั้นบังคับ
400ExpenseClaimNotEditablePATCH เรื่องเบิกที่สถานะไม่ใช่ draft หรือ needs_revision
409ExpenseClaimNumberGenerationFailedการออกเลขที่เรื่องเบิกชนกัน ให้ลองสร้างใหม่อีกครั้ง

เรื่องเบิกที่อยู่นอกสมาชิกภาพของคุณจะตอบ 404 ExpenseClaimNotFound เมื่อคุณแค่พยายามอ่าน แต่ตอบ 403 Forbidden เมื่อคุณพยายามแก้ไข การอ่านตั้งใจไม่เปิดเผยว่าเรื่องเบิกนั้นมีอยู่ หากคุณไม่มีสิทธิ์เห็นมัน

ขั้นตอนถัดไป

อ่านต่อที่ ผู้ติดต่อ