PCE AP Write API — 介接手冊(外部廠商)
1. 快速開始
- 向客戶 NetSuite 管理員取得:
- Account ID(如
TD1234567) - RESTlet External URL
- OAuth 2.0 Integration:Client ID、(M2M 時)Certificate ID/私鑰,或 Authorization Code 用的 Client Secret/Redirect URI
- Account ID(如
- 先取得 OAuth 2.0 access token(Bearer)。
- 以 POST +
options.validateOnly: true試跑連線與驗證(不支援 GET)。 - 再以 POST + JSON 依業務情境選擇
action寫入。
1.1 連線方式
| 項目 | 說明 |
|---|---|
| 協定 | HTTPS |
| 驗證 | OAuth 2.0(Authorization: Bearer {access_token}) |
| Content-Type | application/json(寫入);取 token 時為 application/x-www-form-urlencoded |
| 方法 | 僅 POST(GET 回 PCE_METHOD_NOT_ALLOWED) |
External URL 範例(數字 id 以客戶環境為準):
https://{accountId}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=xxx&deploy=yyyToken endpoint:
https://{accountId}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token1.2 OAuth 2.0 取得 Token(廠商建議:M2M)
Client Credentials(Machine-to-Machine) — 適用系統對系統、無使用者登入:
- Integration 勾選 Client Credentials (Machine to Machine),Scope 含 RESTlets。
- 產生憑證(例 ES256),於 Setup → Integration → OAuth 2.0 Client Credentials (M2M) Setup 上傳公鑰並綁定 Entity/Role/Application,記下 Certificate ID。
- 以私鑰簽署 JWT(
iss=Client ID,aud=token URL,scope=restlets,headerkid=Certificate ID)。 - POST token URL:
grant_type=client_credentials
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion={signed_jwt}- 回應之
access_token放入後續 RESTlet 請求標頭:Authorization: Bearer {access_token}。
輔助腳本:docs/postman/generate-client-assertion.js。
Authorization Code Grant — 適用需使用者登入的互動測試/應用;取得 token 後同樣使用 Bearer。
Postman:匯入 docs/postman/PCE-AP-Write.postman_collection.json → 先跑 0. Auth 取 token → 再打 RESTlet(Collection Auth = Bearer {{accessToken}})。
2. 業務情境(action)
每次 POST 必須帶 action,且只能擇一:
| action | 用途 | 必填 | 不可帶 |
|---|---|---|---|
apInvoiceOnly | 只建立進項發票(可不掛交易) | apInvoices(至少 1 筆) | 用來「新建」的 transaction(type/fields/行) |
transactionOnly | 只建立或引用交易單 | transaction(物件或陣列;每筆需有 id 或 type,最多 10 筆) | apInvoices |
transactionWithApInvoices | 交易+一至多筆進項(1:N) | 每筆 transaction(物件或陣列,最多 10 筆)內嵌非空 apInvoices[] | top-level apInvoices/invoices/apInvoicesByTransaction |
公版只接受上表三種 action。API進項審核與請購單不在這份套件;送出那些 action 會回 INVALID_ACTION。
2.1 支援的交易類型
建立新交易時,transaction.type 限下列字串(大小寫不敏感):
| type | NetSuite 交易 |
|---|---|
vendorbill | Vendor Bill |
expensereport | Expense Report |
vendorcredit | Vendor Credit |
vendorprepayment | Vendor Prepayment |
引用既有交易:只傳 transaction.id(內部 ID)即可;可省略 type/fields。
3. GET — 不允許
本 API 不支援 GET。對 External URL 發 GET 時,應收到:
{
"success": false,
"returnCode": "1014",
"returnMessage": "PCE_METHOD_NOT_ALLOWED",
"error": {
"code": "PCE_METHOD_NOT_ALLOWED",
"message": "GET is not allowed on PCE AP Write. Use POST with action=apInvoiceOnly|transactionOnly|transactionWithApInvoices.",
"details": null
}
}契約與範例請見本文件與 Postman collection;連線試跑請用 options.validateOnly: true 的 POST。
4. POST — 寫入
4.1 共用結構
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "apInvoiceOnly | transactionOnly | transactionWithApInvoices",
"apInvoices": [ { } ],
"transaction": { },
"options": {
"validateOnly": false
}
}| 欄位 | 必填 | 說明 |
|---|---|---|
action | 必填 | 三種之一:apInvoiceOnly/transactionOnly/transactionWithApInvoices(見 §2) |
requestId | 必填 | 冪等鍵。建議 UUID v4;8–64 字元,僅 A–Z a–z 0–9 - _。每一次邏輯寫入用新 key;網路重試必須重用同一把。 |
apInvoices | 依 action | 僅 apInvoiceOnly 必填(至少 1 筆);亦接受別名 invoices。其他 action 不可帶 top-level |
transaction | 依 action | transactionOnly/transactionWithApInvoices 必填(物件或陣列,最多 10 筆)。後者每筆須內嵌非空 apInvoices[] |
options.validateOnly | 選填 | true 時只驗證進項(及 shape),不寫入資料(見各 action 差異)。失敗或 validateOnly 回應會附 requestSummary(精簡:index/clientRef/invoiceNo/type/id) |
options.echoRequest | 選填 | 預設不回傳。true 時在任何回應附上完整 request(除錯用;正式環境建議關閉) |
clientRef | 選填 | 放在每個 transaction 與/或每張 apInvoices[] 項目上(字串 1–64),成功回應會原樣回傳以便對帳 |
requestId 先前已成功寫入(非 validateOnly),再次呼叫會回傳上次結果並帶 idempotentReplay: true,不會再建資料。同 key 但 payload 不同 → REQUEST_ID_PAYLOAD_MISMATCH。破壞性契約:
transactionWithApInvoices 的進項必須放在 transaction.apInvoices(或多筆時每筆各自的 apInvoices)。不再接受 top-level apInvoices/apInvoicesByTransaction。4.2 成功/失敗回應格式
每次回應(含 GET 被拒)頂層皆含 returnCode(4 位字串)與 returnMessage(固定英文短句),供外部系統程式判斷;success 與 error.code 仍保留。
成功碼(000x)
| returnCode | 條件 | returnMessage |
|---|---|---|
0000 | 一般寫入成功(全部項目成功) | OK |
0001 | validateOnly: true 且全部通過 | VALIDATED |
0002 | idempotentReplay: true(冪等重播,先前全成功) | IDEMPOTENT_REPLAY |
0003 | 全部成功但有 warnings(稅額容差等) | OK_WITH_WARNINGS |
0004 | 部分成功:至少一筆已寫入/通過,且至少一筆失敗 | PARTIAL_SUCCESS |
success 為 false,partialSuccess 為 true,returnCode 為 0004,但仍回完整 results[](成功項有 id/transactionId,失敗項有 error)。同一 requestId 重送相同 payload 會冪等回傳上次部分結果,不會重複建單;補送失敗項請用新 requestId 且只帶未成功資料。成功(以 results[] 對應 request;每筆含 returnCode;可選 clientRef 原樣回傳):
{
"success": true,
"returnCode": "0000",
"returnMessage": "OK",
"action": "apInvoiceOnly",
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"results": [
{
"index": 0,
"returnCode": "0000",
"returnMessage": "OK",
"id": "1001",
"invoiceNo": "CA12345678",
"clientRef": "inv-001"
}
]
}部分成功(transactionWithApInvoices 多筆發票):
{
"success": false,
"partialSuccess": true,
"returnCode": "0004",
"returnMessage": "PARTIAL_SUCCESS",
"action": "transactionWithApInvoices",
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"error": {
"code": "PARTIAL_SUCCESS",
"message": "Partial success: 1 succeeded, 1 failed. Succeeded: [transaction[0].apInvoices[0] clientRef=… invoiceNo=CA12345678 id=1001 returnCode=0000]. Failed: [transaction[0].apInvoices[1] clientRef=… invoiceNo=AA99999999 returnCode=2001 error=PCE_AP_INVOICE_VALIDATION_ERROR].",
"details": {
"succeeded": 1,
"failed": 1,
"succeededItems": [{ "path": "transaction[0].apInvoices[0]", "clientRef": "…", "invoiceNo": "CA12345678", "id": "1001", "returnCode": "0000" }],
"failedItems": [{ "path": "transaction[0].apInvoices[1]", "clientRef": "…", "invoiceNo": "AA99999999", "returnCode": "2001", "errorCode": "PCE_AP_INVOICE_VALIDATION_ERROR" }]
}
},
"results": [
{
"index": 0,
"returnCode": "0004",
"returnMessage": "PARTIAL_SUCCESS",
"transactionId": "789",
"transactionType": "vendorbill",
"transactionCreated": true,
"apInvoices": [
{
"index": 0,
"returnCode": "0000",
"returnMessage": "OK",
"id": "1001",
"invoiceNo": "CA12345678"
},
{
"index": 1,
"returnCode": "2001",
"returnMessage": "AP_INVOICE_VALIDATION_FAILED",
"invoiceNo": "AA99999999",
"error": {
"code": "PCE_AP_INVOICE_VALIDATION_ERROR",
"message": "…",
"details": {}
}
}
]
}
],
"requestSummary": { }
}失敗(單筆驗證失敗仍回 results[];多筆全失敗頂層常為 2100):
{
"success": false,
"returnCode": "2001",
"returnMessage": "AP_INVOICE_VALIDATION_FAILED",
"error": {
"code": "PCE_AP_INVOICE_VALIDATION_ERROR",
"message": "人類可讀說明",
"details": null
},
"results": [
{
"index": 0,
"returnCode": "2001",
"returnMessage": "AP_INVOICE_VALIDATION_FAILED",
"invoiceNo": "CA12345678",
"error": {
"code": "PCE_AP_INVOICE_VALIDATION_ERROR",
"message": "人類可讀說明",
"details": null
}
}
]
}returnCode/success 為準。OAuth 401/403 屬 token/角色問題,body 可能無 JSON。進項驗證失敗時,error.code 常為 PCE_AP_INVOICE_VALIDATION_ERROR,details 為依欄位分組的錯誤明細。
warnings:仍判定成功,但有提示(例如稅額與 5% 計算差在容許範圍內)。請記錄並視需要人工覆核。
5. 進項發票欄位(apInvoices[])
建議使用 友善別名(camelCase)。亦可直接傳 NetSuite field id(custrecord_pce_ap_*)。
5.1 建議別名對照
| 建議別名 | 中文 | 必填 | 備註 |
|---|---|---|---|
docFormatCode | 憑證格式代碼 | 必填 | 如 "21"~"29";亦可用 docType/formatCode |
invCatCode | 發票類別代號 | 條件必填 | 格式 21:01/05;格式 25:06/07;亦可用 invoiceCategoryCode |
docFormatName | 憑證格式名稱 | 選填 | 完整 Name(如 21-三聯式統一發票);可取代 invCatCode 消歧 |
docDate | 憑證開立日期 | 必填 | YYYY/MM/DD 或 YYYY-MM-DD,如 2026/08/01 |
docPeriod | 資料所屬年月 | 必填 | 民國五碼 YYYMM,如 11508(可自 docDate 推算) |
filingPeriod | 申報期別 | 必填 | 民國五碼 YYYMM |
deductionCode | 進項扣抵代號 | 必填 | 1 進項可扣抵之進貨及費用/2 進項可扣抵之固定資產/3 進項不可扣抵之進貨及費用/4 進項不可扣抵之固定資產 |
invoiceNo | 發票號碼 | 條件必填 | 依格式/路徑;亦可用 invoiceNumber |
otherDocNo | 其他憑證號碼 | 條件必填 | 依格式/彙加路徑 |
customsAuthCode | 海關代徵認證碼 | 條件必填 | 格式 28/29 |
utilityCarrierSn | 公用事業載具流水號 | 條件必填 | 格式 25 |
buyerTaxId | 買受人統一編號 | 必填 | 8 碼,含檢查碼 |
sellerTaxId | 銷售人統編 | 條件必填 | 依格式/彙加可能必填或禁填 |
taxType | 課稅別 | 必填 | 1 應稅/2 零稅率/3 免稅/F 作廢/D 空白未使用 |
salesAmount | 銷售金額 | 條件必填 | 亦可用 salesAmt;部分規則必填 |
taxAmount | 稅額 | 條件必填 | 亦可用 taxAmt;依課稅別/格式檢核 |
totalAmount | 總計 | 選填 | 亦可用 totalAmt |
summaryMark | 彙加/分攤註記 | 條件必填 | A 彙加/B 分攤;部分格式必填 |
summaryQty | 彙總數量 | 條件必填 | 彙加等情境 |
fileBizTax | 申報營業稅 | 選填 | true/false 或 T/F |
clientRef | 客戶對帳鍵 | 選填 | 字串 1–64;回應原樣回傳 |
transaction | 來源交易 | 選填 | 既有交易 internal id(掛 parent;CSV 亦可用 transactionId) |
Lookup 類欄位可傳 業務代碼(建議)或 NetSuite internal id。
5.1.1 格式 21/25 與 ERP 欄位映射(invCatCode)
當 ERP 只傳營業稅代號 docFormatCode: "21" 或 "25" 時,須再帶 invCatCode(或 docFormatName/lookup internal id)消歧:
| docFormatCode | invCatCode | NetSuite Name(docFormatName) | 常見 ERP 憑證類型關鍵字 |
|---|---|---|---|
| 21 | 01 | 21-三聯式統一發票 | 三聯式、手開三聯式 |
| 21 | 05 | 21-電子計算機統一發票 | 電子計算機、電算機 |
| 25 | 06 | 25-進項三聯式收銀機統一發票(…) | 三聯式收銀機、POS 三聯式 |
| 25 | 07 | 25-進項一般稅額計算之電子發票(…) | 一般稅額電子發票、B2B 電子發票 |
完整映射表(含備註):docs/samples/ap-invCatCode-erp-mapping.csv
虛擬碼(ERP 對照邏輯):
若 docFormatCode = "21": 若 憑證類型 含「電子計算機」或「電算機」→ invCatCode = "05" 否若 含「三聯式」→ invCatCode = "01" 若 docFormatCode = "25": 若 憑證類型 含「三聯式收銀機」或「收銀機(三聯)」→ invCatCode = "06" 否若 含「電子發票」或「一般稅額」→ invCatCode = "07" 否則 → 改傳 docFormatName 或 lookup internal id
JSON 範例(格式 25):
{
"docFormatCode": "25",
"invCatCode": "07",
"docDate": "2026/08/01",
"docPeriod": "11508",
"filingPeriod": "11508",
"deductionCode": "1",
"invoiceNo": "CA12345680",
"buyerTaxId": "24536806",
"sellerTaxId": "24536806",
"taxType": "1",
"salesAmount": 1000,
"taxAmount": 50,
"totalAmount": 1050,
"fileBizTax": true
}options.validateOnly: true 對真實樣本試跑。5.2 範例:只寫進項
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "apInvoiceOnly",
"apInvoices": [
{
"docFormatCode": "21",
"invCatCode": "01",
"docDate": "2026/08/01",
"docPeriod": "11508",
"filingPeriod": "11508",
"deductionCode": "1",
"invoiceNo": "CA12345678",
"buyerTaxId": "24536806",
"sellerTaxId": "24536806",
"taxType": "1",
"salesAmount": 1000,
"taxAmount": 50,
"totalAmount": 1050,
"fileBizTax": true
}
]
}成功回應示例:
{
"success": true,
"returnCode": "0000",
"returnMessage": "OK",
"action": "apInvoiceOnly",
"validateOnly": false,
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"results": [
{
"index": 0,
"id": "1001",
"invoiceNo": "CA12345678",
"clientRef": "inv-001"
}
]
}6. 交易物件(transaction)
{
"id": "456",
"type": "vendorbill",
"fields": {
"entity": "123",
"subsidiary": "1",
"account": "220",
"memo": "from external system"
},
"expenseLines": [
{ "account": "610", "amount": 1050 }
],
"itemLines": []
}| 欄位 | 必填 | 說明 |
|---|---|---|
id | 選填 | 既有交易 internal id;有值則不新建(與 purchaseOrderId 互斥) |
type | 條件必填 | 新建時必填(見 §2.1);僅引用既有 id 時可省略 |
purchaseOrderId | 選填 | 僅 vendorbill:從既有 Purchase Order transform 建立 VB(見 §6.2);不可與 id 同時出現 |
fields | 選填 | 表頭欄位 → NetSuite field id → value(無白名單,見 §6.0)。實際 NetSuite 表單必填依客戶設定 |
expenseLines | 選填 | expense 子清單列(欄位同樣透傳) |
itemLines | 條件必填 | 一般選填;有 purchaseOrderId 時必填非空(見 §6.2) |
apInvoices | 條件必填 | 僅 transactionWithApInvoices:每筆交易內必填非空陣列 |
clientRef | 選填 | 字串 1–64;回應原樣回傳 |
fields/行項目在 NetSuite 端的必填與合法值依客戶設定而定(廠商/科目/子公司等請向客戶索取對照表)。
6.0 客製化欄位(允許透傳)
現階段設計允許透過 API 寫入 Vendor Bill/Expense Report/Vendor Credit/Vendor Prepayment 的客製欄位。
transaction.fields的 key 會直接作為 NetSuite 表頭fieldId呼叫setValue(例如custbody_xxx)。expenseLines/itemLines的 key 會直接作為子清單fieldId呼叫setCurrentSublistValue(例如custcol_yyy)。- Payload 契約沒有欄位白名單;PCE 不會驗證這些交易/客製欄內容,成敗由 NetSuite 欄位設定、表單、權限與值型別決定。
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "transactionOnly",
"transaction": {
"type": "vendorbill",
"fields": {
"entity": "123",
"subsidiary": "1",
"memo": "AP Write",
"custbody_xxx": "自訂表頭值"
},
"expenseLines": [
{
"account": "610",
"amount": 1050,
"custcol_yyy": "自訂行值"
}
]
}
}注意:
- Field ID 必須正確,且該交易類型/表單上確實有該欄。
- 值型別須符合欄位(select 傳 internal id、日期格式等依客戶環境)。
- Integration/Role 需具備寫入該欄的權限。
- 標準欄有寫入順序偏好(
entity/subsidiary等較早;account/payment最後)。客製欄通常排在多數標準欄之後;若客製欄依賴 sourcing,請與客戶一併驗證。
6.1 範例:只建交易
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "transactionOnly",
"transaction": {
"type": "vendorbill",
"fields": {
"entity": "123",
"subsidiary": "1",
"account": "220",
"memo": "AP Write"
},
"expenseLines": [
{ "account": "610", "amount": 1050 }
]
}
}6.2 從 Purchase Order 建立 Vendor Bill
當 type 為 vendorbill 且帶 purchaseOrderId(PO 內部 id)時,API 會以 NetSuite transform(PO → Vendor Bill)建立帳單,並依 request 的 itemLines 覆寫數量(支援部分開帳)。
規則:
| 項目 | 說明 |
|---|---|
| 適用 | 僅 vendorbill;其他 type 帶 purchaseOrderId → PURCHASE_ORDER_NOT_ALLOWED |
與 id | 不可同時出現 → PURCHASE_ORDER_WITH_EXISTING_ID |
itemLines | 必填非空陣列;每筆必有 orderline(或別名 line)與 quantity(≥0 數字) |
| 數量 | 以 request 的 quantity 為準;未出現在 request 的 PO 行會設為 quantity=0(不開帳) |
| 找不到行 | request 的 orderline 在 transform 後的 VB 上不存在 → PO_ORDERLINE_NOT_FOUND |
| 其他欄 | fields/itemLines 其餘 key 仍可透傳(如 rate、custcol_*);可另帶 expenseLines 追加費用列 |
itemLines[](有 purchaseOrderId 時):
| 欄位 | 必填 | 說明 |
|---|---|---|
orderline(或 line) | 必填 | 對應 PO item 子清單行號 |
quantity | 必填 | ≥0 數字;以此覆寫開帳數量 |
其餘 key(如 rate、custcol_*) | 選填 | 透傳覆寫;quantity 仍以 request 為準 |
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "transactionOnly",
"transaction": {
"type": "vendorbill",
"purchaseOrderId": "12345",
"fields": {
"memo": "bill from PO"
},
"itemLines": [
{ "orderline": "1", "quantity": 2 },
{ "orderline": "3", "quantity": 1, "rate": 100 }
]
}
}成功時 results[] 會含 transactionId、transactionCreated: true,以及原樣回傳的 purchaseOrderId。
6.3 範例:交易+多筆進項
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "transactionWithApInvoices",
"transaction": {
"clientRef": "vb-001",
"type": "vendorbill",
"fields": {
"entity": "123",
"subsidiary": "1",
"account": "220"
},
"expenseLines": [
{ "account": "610", "amount": 2100 }
],
"apInvoices": [
{
"clientRef": "inv-001",
"docFormatCode": "21",
"invCatCode": "01",
"docDate": "2026/08/01",
"docPeriod": "11508",
"filingPeriod": "11508",
"deductionCode": "1",
"invoiceNo": "CA12345678",
"buyerTaxId": "24536806",
"sellerTaxId": "24536806",
"taxType": "1",
"salesAmount": 1000,
"taxAmount": 50,
"totalAmount": 1050,
"fileBizTax": true
},
{
"clientRef": "inv-002",
"docFormatCode": "21",
"invCatCode": "05",
"docDate": "2026/08/02",
"docPeriod": "11508",
"filingPeriod": "11508",
"deductionCode": "1",
"invoiceNo": "CA12345679",
"buyerTaxId": "24536806",
"sellerTaxId": "24536806",
"taxType": "1",
"salesAmount": 1000,
"taxAmount": 50,
"totalAmount": 1050,
"fileBizTax": true
}
]
},
"options": {
"validateOnly": false
}
}成功回應示例:
{
"success": true,
"action": "transactionWithApInvoices",
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"results": [
{
"index": 0,
"clientRef": "vb-001",
"transactionId": "789",
"transactionType": "vendorbill",
"transactionCreated": true,
"apInvoices": [
{
"index": 0,
"id": "1001",
"invoiceNo": "CA12345678",
"clientRef": "inv-001"
},
{
"index": 1,
"id": "1002",
"invoiceNo": "CA12345679",
"clientRef": "inv-002"
}
]
}
]
}掛到既有交易:
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "transactionWithApInvoices",
"transaction": {
"id": "789",
"apInvoices": [ { "...": "..." } ]
}
}7. CSV 匯入進項(Suitelet)
除 RESTlet 外,亦可在 NetSuite 內以 Suitelet 上傳 CSV 建立進項(內部仍呼叫同一支 AP Write:action=apInvoiceOnly)。
| 項目 | 說明 |
|---|---|
| Script | customscript_pce_sl_ap_csv_import/customdeploy_pce_sl_ap_csv_import |
| 開啟 | Customization → Scripting → Script Deployments → PCE SL AP CSV Import |
| 編碼 | Excel 另存 CSV(建議「CSV UTF-8」);若中文亂碼請確認為 UTF-8 |
| 上限 | 單次 100 列資料(不含表頭) |
| 欄位標頭 | 與 §5.1 API 別名相同(大小寫不敏感);亦可 custrecord_pce_ap_* |
| 掛交易 | 可選欄 transaction 或 transactionId=既有交易 internal id |
| 驗證 | 勾選「只驗證不寫入」=options.validateOnly: true |
| 對帳 | 每次匯入產生 requestId(csv-…),並寫入 PCE AP Write 呼叫紀錄 |
| POST 後導向 | PRG:POST 完成後 https.redirect 至 GET ?action=result&writeLogId={id};重新整理不會重送 CSV/API |
| 結果還原 | GET 結果頁自 PCE AP Write 呼叫紀錄 讀取 response JSON,並自 _csvUi 快照還原表頭/validateOnly/列數(PCE_LIB_AP_CsvImportReplay.js) |
| 結果畫面 | 本頁 Bootstrap 自訂介面:中文總結(成功/失敗筆數);失敗列優先並列出中文原因;成功列可開進項紀錄 |
| 失敗重試 | 有失敗時可 下載失敗列 CSV(表頭與原檔相同),修正後再上傳 |
| 範本下載 | 頁面提供 下載 CSV 範本(action=template)與 下載欄位說明(action=guide) |
範本:docs/samples/ap-invoice-import-sample.csv(含 21/25 各 invCatCode 範例列)/欄位說明:docs/samples/ap-invoice-import-field-guide.csv/ERP 映射:docs/samples/ap-invCatCode-erp-mapping.csv
returnCode、完整 JSON)摺在「進階資訊」。8. 僅驗證(validateOnly)
{
"requestId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"action": "apInvoiceOnly",
"options": { "validateOnly": true },
"apInvoices": [ { "...": "..." } ]
}| action | validateOnly 行為 |
|---|---|
apInvoiceOnly | 跑進項驗證,不 create |
transactionWithApInvoices | 驗證進項(若有 transaction.id 會帶入關聯);不 create 交易/發票 |
transactionOnly | 只確認 JSON 結構合法,不驗證交易內容、不存檔 |
上線前請務必先用 validateOnly: true 對真實樣本資料試跑。
9. 錯誤碼一覽(介接端)
8.1 returnCode(建議程式判斷用)
| returnCode | 語意 | success |
|---|---|---|
0000–0099 | 成功/部分成功(見 §4.2;0004=partial) | true(0004 時為 false) |
1000–1999 | Request/Payload shape | false |
2000–2999 | 進項業務驗證 | false |
3000–3999 | Lookup/主檔解析 | false |
4000–4999 | NetSuite 執行期(權限、save 失敗等) | false |
9000–9999 | 未預期錯誤 | false |
8.2 error.code 對照
| code | returnCode | 意涵 | 建議處理 |
|---|---|---|---|
INVALID_BODY | 1001 | body 不是 JSON 物件 | 檢查序列化 |
INVALID_ACTION | 1002 | action 拼錯 | 使用三種合法值之一 |
REQUEST_ID_REQUIRED | 1003 | 缺少 requestId | 補上冪等鍵(建議 UUID v4) |
REQUEST_ID_INVALID | 1004 | requestId 格式不合 | 8–64 字元,僅 A–Z a–z 0–9 - _ |
REQUEST_ID_PAYLOAD_MISMATCH | 1005 | 同 requestId 但 payload 不同 | 換新 requestId,或重送完全相同的 body |
AP_INVOICES_REQUIRED | 1006 | 缺少進項陣列 | apInvoiceOnly 補 top-level apInvoices;transactionWithApInvoices 補每筆 transaction.apInvoices |
AP_INVOICES_TOP_LEVEL_NOT_ALLOWED | 1008 | transactionWithApInvoices 不可用 top-level 發票欄位 | 改為巢狀在每個 transaction 內 |
CLIENT_REF_INVALID | 1013 | clientRef 格式不合 | 使用 1–64 字元字串 |
AP_INVOICES_NOT_ALLOWED | 1007 | transactionOnly 不應帶發票(top-level 或巢狀) | 改 action 或拿掉陣列 |
TRANSACTION_REQUIRED | 1009 | 缺少交易 | 補 id 或 type |
TRANSACTION_NOT_ALLOWED | 1010 | apInvoiceOnly 不可新建交易 | 改用 transactionWithApInvoices |
INVALID_TRANSACTION_TYPE | 1011 | type 不支援 | 改為 §2.1 列表 |
TRANSACTION_LIMIT_EXCEEDED | 1012 | 一次超過 10 筆交易 | 分批呼叫 |
PCE_METHOD_NOT_ALLOWED | 1014 | 不支援的 HTTP 方法(如 GET) | 改用 POST |
PURCHASE_ORDER_NOT_ALLOWED | 1015 | 非 vendorbill 卻帶 purchaseOrderId | 改 type 或拿掉欄位 |
PURCHASE_ORDER_WITH_EXISTING_ID | 1016 | 同時帶 id 與 purchaseOrderId | 擇一:引用既有 VB,或從 PO 新建 |
PO_ITEM_LINES_REQUIRED | 1017 | 從 PO 建立時缺 itemLines | 補上含 orderline/quantity 的列 |
PO_ORDERLINE_REQUIRED | 1018 | item 列缺 orderline/line | 對應 PO 行號 |
PO_QUANTITY_REQUIRED | 1019 | item 列缺合法 quantity | 填 ≥0 數字 |
PCE_AP_INVOICE_VALIDATION_ERROR | 2001 | 進項業務規則失敗 | 讀 results[i].error/message/details 修正欄位 |
BATCH_FAILED | 2100 | 批次內全部項目失敗 | 逐筆看 results[] |
PARTIAL_SUCCESS | 0004 | 部分成功 | 依 results[] 保留成功項,失敗項換新 requestId 重送 |
PCE_LOOKUP_RESOLVE_ERROR | 3001 | 代碼無法對應主檔 | 向客戶確認 lookup/代碼表 |
PCE_INVALID_TRANSACTION_TYPE | 3002 | 執行期 type 無法對應 record | 改為 §2.1 列表 |
SKIPPED_PARENT_FAILED | 4001 | 父交易失敗,巢狀發票未處理 | 先修好交易再重送 |
PO_ORDERLINE_NOT_FOUND | 4002 | request 的 orderline 不在 PO/transform 結果 | 核對 PO 行號 |
PO_TRANSFORM_FAILED | 4003 | PO→VB transform 失敗 | 查 PO 狀態/權限/可開帳量 |
| 其他 NetSuite 錯誤 | 4000 | 權限、必填、系統錯誤 | 聯絡客戶 NetSuite 管理員 |
UNEXPECTED_ERROR | 9999 | 未預期例外 | 附完整 response 聯絡技術窗口 |
HTTP 層面的 401/403 屬 OAuth 2.0 token/角色問題,與 JSON returnCode/success: false 不同。
10. 呼叫範例(概念)
# 1) 取得 access token(M2M;client_assertion 為已簽署 JWT) curl -X POST "$TOKEN_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \ -d "client_assertion=$CLIENT_ASSERTION" # 2) GET is not allowed (expect PCE_METHOD_NOT_ALLOWED) curl -X GET "$RESTLET_URL" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" # 3) POST write curl -X POST "$RESTLET_URL" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d @payload.json
JWT 產生可參考 docs/postman/generate-client-assertion.js。access token 有時效,過期需重新向 token endpoint 換發。
11. 介接注意事項
- 一筆交易可對多筆進項;請用
transactionWithApInvoices一次送齊,或先建交易再用transaction.id分批掛發票。 - 每次邏輯寫入必須帶唯一
requestId(建議 UUID v4);網路逾時重試請重用同一把,避免重複建單。 - 發票號碼重複會被驗證擋下。比對正式 PCE 進項資料(同一憑證格式)。若帳上有 API進項資料,待審與轉入失敗也列入;退回與已轉入不列入。公版套件不含 API進項資料。
- 目前僅支援新建進項,不支援透過本 API 更新/刪除既有進項。
- 統編、字軌、申報期、稅額等規則見 進項憑證檢核規則。字軌表依帳上財政部字軌主檔,測試帳與正式帳可能不同。
- 成功回應以
results[]對應 request(含index/可選clientRef/invoiceNo/transactionId/apInvoices[].id);請由貴系統保存,作為對帳與除錯依據。 - 若同時需要 UI 與 API 寫入,兩邊共用同一套驗證規則,行為應一致。
12. 聯絡與變更
- 介面變更(action、必填、錯誤碼)應由客戶專案組提前書面通知。
- 技術爭議或驗證規則疑問,請透過客戶指定之顧問/技術窗口,並附上:Account ID、request(可遮罩敏感資料)、完整
errorJSON、時間(含時區)。
文件版本:對應 SuiteApp com.netsuite.pceguitw — PCE RL AP Write(customscript_pce_rl_ap_write)。