เอกสารซื้อ
ใบขอซื้อ ใบสั่งซื้อ ใบรับสินค้า ใบแจ้งหนี้ซื้อ และใบสำคัญจ่าย
สายเอกสารซื้อคล้ายกับสายเอกสารขาย แต่มีสองข้อที่ต้องรู้ก่อนเริ่ม คือ วันที่เป็น Unix timestamp ไม่ใช่สตริงวันที่ และ ทุก response ห่อด้วย data
| รหัส | เอกสาร |
|---|---|
PR | ใบขอซื้อ |
PO | ใบสั่งซื้อ |
GR | ใบรับสินค้า |
PI | ใบแจ้งหนี้ซื้อ |
PV | ใบสำคัญจ่าย |
สร้างเอกสาร
POST /purchase/documents → 201 { "success": true, "data": { … } }
ต้องระบุ docType, docDate, currency และรายการอย่างน้อยหนึ่งรายการ แต่ละรายการต้องมี description, unit และ quantity มากกว่าศูนย์
curl -X POST "$ABSERP_API_URL/openapi/v1/purchase/documents" \
-H "Authorization: Bearer $ABSERP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"docType": "PO",
"docDate": 1757289600,
"dueDate": 1759881600,
"contactId": 7,
"currency": "THB",
"notes": "เติมสต็อกสำหรับโปรโมชันเดือนกันยายน",
"items": [
{
"productId": 5,
"description": "Widget",
"unit": "ชิ้น",
"quantity": 100,
"unitPrice": 250,
"discountPercent": 0,
"taxRate": 7
}
]
}'ฟิลด์ระดับเอกสาร
| ฟิลด์ | ชนิด | จำเป็น | หมายเหตุ |
|---|---|---|---|
docType | string | ใช่ | หนึ่งในห้ารหัสด้านบน |
docDate | integer | ใช่ | Unix seconds เช่น 1757289600 |
currency | string | ใช่ | รหัส 3 ตัวอักษร — จำเป็นในฝั่งซื้อ ต่างจากเอกสารขาย |
items | array | ใช่ | อย่างน้อยหนึ่งรายการ |
dueDate | integer | ไม่ | Unix seconds |
contactId | integer | ไม่ | ผู้ขาย ไม่บังคับใน PR เพราะอาจยังไม่ทราบผู้ขาย |
refDocId / refDocType | integer / string | ไม่ | เชื่อมเอกสารนี้กับเอกสารก่อนหน้าในสาย |
notes | string | ไม่ | แสดงบนเอกสาร |
internalNotes | string | ไม่ | ไม่แสดงบนเอกสาร |
whtRate | number | ไม่ | เปอร์เซ็นต์ภาษีหัก ณ ที่จ่าย 0–100 |
whtAmount | number | ไม่ | จำนวนเงินภาษีหัก ณ ที่จ่าย |
whtSection | string | ไม่ | ข้อความอิสระไม่เกิน 50 ตัวอักษร เช่น SERVICE, RENT |
bankId | integer | ไม่ | ใช้กับ PV เท่านั้น — บัญชีที่เงินออก ส่งกับชนิดอื่นจะได้ 400 |
ฟิลด์รายบรรทัด
| ฟิลด์ | ชนิด | จำเป็น | หมายเหตุ |
|---|---|---|---|
description | string | ใช่ | ข้อความรายการ |
unit | string | ใช่ | ไม่เกิน 20 ตัวอักษร |
quantity | number | ใช่ | มากกว่าศูนย์ |
productId | integer | ไม่ | เชื่อมกับสินค้าเพื่อให้สต็อกและต้นทุนเคลื่อนไหวเมื่อ GR ถูก post |
productAltUnitId | integer | ไม่ | ซื้อด้วยหน่วยทางเลือก |
unitPrice | number | ไม่ | ค่าเริ่มต้น 0 |
discountPercent | number | ไม่ | 0–100 |
taxRate | number | ไม่ | เปอร์เซ็นต์ 0–100 รายบรรทัด ต่างจากฝั่งขายที่ VAT เป็นอัตราเดียวระดับเอกสาร |
accountId | integer | ไม่ | กำหนดบัญชีค่าใช้จ่ายของบรรทัดนี้เอง |
ข้อมูลที่คืนกลับ
{
"success": true,
"data": {
"id": 18,
"docNumber": "PO-2026-0001",
"docType": "PO",
"docDate": 1757289600,
"dueDate": 1759881600,
"contactId": 7,
"contactName": "บริษัท ซัพพลายเออร์ จำกัด",
"status": "draft",
"subtotal": 25000,
"discountAmount": 0,
"taxAmount": 1750,
"totalAmount": 26750,
"paidAmount": 0,
"currency": "THB",
"createdBy": 3,
"createdByName": "สมหญิง ใจดี",
"approvedBy": null,
"approvedAt": null,
"items": [
{
"id": 55,
"productId": 5,
"productName": "Widget",
"description": "Widget",
"unit": "ชิ้น",
"quantity": 100,
"receivedQuantity": 0,
"unitPrice": 250,
"taxRate": 7,
"taxAmount": 1750,
"lineTotal": 26750
}
],
"statusHistories": [
{
"fromStatus": null,
"toStatus": "draft",
"changedBy": 3,
"changedByName": "สมหญิง ใจดี",
"createdAt": 1757289600
}
],
"paymentAllocations": []
}
}receivedQuantity ในบรรทัดของ PO คือจำนวนที่รับเข้ามาแล้วผ่านใบรับสินค้า เป็นค่าที่ควรอ่านเพื่อแยกใบสั่งซื้อที่รับของครบจากที่รับของบางส่วน
ค้นหาเอกสาร
GET /purchase/documents → แบ่งหน้า อยู่ใน data
| พารามิเตอร์ | หมายเหตุ |
|---|---|
docType | หนึ่งใน PR, PO, GR, PI, PV |
contactId | รหัสผู้ขาย |
status | กรองตามสถานะ |
fromDate / toDate | Unix seconds อ้างอิงวันที่เอกสาร |
search | ค้นจากเลขที่เอกสารและชื่อผู้ขาย |
page / limit | ดูการแบ่งหน้า |
curl -X GET "$ABSERP_API_URL/openapi/v1/purchase/documents?docType=PO&status=sent&limit=100" \
-H "Authorization: Bearer $ABSERP_API_KEY"อ่านเอกสารรายฉบับ
GET /purchase/documents/{id} → คืนเอกสารใน data พร้อม items, statusHistories และ paymentAllocations
แก้ไขเอกสาร
PATCH /purchase/documents/{id} → คืนเอกสารที่แก้แล้วใน data
แก้ได้ที่ docDate, dueDate, contactId, notes, internalNotes, ฟิลด์ภาษีหัก ณ ที่จ่าย, bankId และ items แต่ docType แก้ไม่ได้ ใบสั่งซื้อไม่สามารถกลายเป็นใบรับสินค้า ให้สร้างเอกสารถัดไปในสายแล้วอ้างอิงเอกสารนี้ด้วย refDocId / refDocType
เปลี่ยนสถานะ
PATCH /purchase/documents/{id}/status → คืนเอกสารที่แก้แล้วใน data
curl -X PATCH "$ABSERP_API_URL/openapi/v1/purchase/documents/18/status" \
-H "Authorization: Bearer $ABSERP_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "sent", "note": "ส่งให้ผู้ขายแล้ว" }'ฟิลด์หมายเหตุของฝั่งซื้อชื่อ note ขณะที่เอกสารขายใช้ remark การเปลี่ยนสถานะที่อนุญาตของแต่ละชนิดอยู่ในข้อมูลอ้างอิงชนิดเอกสาร การเปลี่ยนที่ไม่อนุญาตจะได้ 400 และไม่มีอะไรเปลี่ยน
ต่างจากฝั่งขาย endpoint นี้คืน เอกสารทั้งฉบับที่อัปเดตแล้ว จึงไม่ต้องเรียกอ่านอีกครั้งเพื่อดูยอดหรือประวัติใหม่
ลบเอกสาร
DELETE /purchase/documents/{id} → 204 No Content
ใช้ได้กับเอกสารที่ยังไม่มีการดำเนินการต่อ ใบสั่งซื้อที่รับของแล้ว หรือใบสำคัญจ่ายที่จ่ายแล้ว ควรยกเลิกผ่าน endpoint เปลี่ยนสถานะ เพื่อให้สายเอกสารตรวจสอบย้อนหลังได้
ตัวอย่างสายงานเต็มรอบ
การเติมสต็อกตั้งแต่ต้นจนจบ
POST /purchase/documentsด้วยdocType: "PO"→ ได้ id18สถานะdraftPATCH /purchase/documents/18/statusเป็นsentแล้วเป็นacknowledgedเมื่อผู้ขายยืนยัน- ของมาถึง:
POST /purchase/documentsด้วยdocType: "GR",refDocId: 18,refDocType: "PO"และจำนวนที่รับจริง PATCHใบGRเป็นposted— สต็อกและต้นทุนเฉลี่ยเคลื่อนไหวที่ขั้นนี้- ใบแจ้งหนี้มาถึง:
docType: "PI"อ้างอิงใบGRแล้วPATCHเป็นmatchedและapproved - จ่ายเงิน:
docType: "PV"พร้อมbankIdแล้วPATCHเป็นapprovedและpaid
หากระบบของคุณต้องตอบสนองเมื่อผู้ขายยืนยัน ให้เรียก GET /purchase/documents?docType=PO&status=acknowledged เป็นระยะตั้งแต่ขั้นที่ 2 เป็นต้นไป
รหัสข้อผิดพลาด
เอกสารซื้อใช้รหัสทั่วไป 400 BadRequest / 404 NotFound / 500 InternalError จากข้อผิดพลาดและการแบ่งหน้า กับความผิดพลาดส่วนใหญ่ รวมถึงการเปลี่ยนสถานะที่ไม่อนุญาตด้วย ซึ่งฝั่งนี้จะได้ 400 BadRequest ธรรมดา ไม่ใช่รหัส InvalidStatusTransition เฉพาะแบบที่เอกสารขายใช้ ให้อ่าน errorDescription เพื่อดูสาเหตุ ส่วนการเปลี่ยนสถานะที่อนุญาตอยู่ในข้อมูลอ้างอิงชนิดเอกสาร
มีบางกรณีที่ได้รหัสเฉพาะ
| สถานะ | รหัส | สาเหตุ |
|---|---|---|
| 400 | BankNotFound | ไม่พบ bankId ในองค์กรนี้ |
| 400 | BankNotAllowedForDocumentType | ส่ง bankId กับชนิดเอกสารที่ไม่ใช่ PV |
| 400 | NoDefaultWarehouse | ยืนยันใบรับสินค้า (GR → posted) โดยไม่มี warehouseId และองค์กรยังไม่ได้ตั้งค่าคลังเริ่มต้น |
| 422 | NegativeStockBlocked | ยกเลิกใบรับสินค้าที่ยืนยันแล้วซึ่งสต็อกที่รับเข้ามาถูกใช้ไปที่อื่นบางส่วนแล้ว |
ขั้นตอนถัดไป
อ่านต่อที่ เรื่องเบิกค่าใช้จ่าย