Ctrl K

PCE AP Write API — 技術與測試文件(內部)

對象:內部技術人員、實作顧問、QA
範圍:架構、部署、驗證鏈、除錯、單元/整合測試
相關:介接手冊(廠商) · English · Postman
HTML:HTML 版

1. 服務概要

項目內容
用途外部系統透過 RESTlet 寫入進項發票(customrecord_pce_ap_invoice)與/或 AP 交易
Scriptcustomscript_pce_rl_ap_write
Deploymentcustomdeploy_pce_rl_ap_write
Entryap/restlets/PCE_RL_AP_Write.js
Payload 契約ap/libraries/PCE_LIB_AP_WritePayload.js
驗證ap/libraries/PCE_LIB_AP_Validate.js(寫入前 + UE beforeSubmit 再驗)
呼叫紀錄customrecord_pce_ap_write_log(每次呼叫自動寫入 request/response;含被拒的 GET)
HTTP僅 POST(GET 回 PCE_METHOD_NOT_ALLOWED)

1.1 action

action行為
apInvoiceOnly只建進項發票;不可帶「要新建的」transaction
transactionOnly只建/引用交易;不可帶 apInvoices
transactionWithApInvoices先解析/建立交易,再寫一至多筆進項並掛 parent

公版只接受上表三種 action。stageApIntake、updateApIntake、createPurchaseReq、updatePurchaseReq 會回 INVALID_ACTION。API進項審核與請購單不在這份套件。

支援交易類型:vendorbill|expensereport|vendorcredit|vendorprepayment。

2. 程式結構

ap/
  restlets/
    PCE_RL_AP_Write.js          # RESTlet 入口(每次呼叫寫入 API Log)
  suitelets/
    PCE_SL_ApWriteSmoke.js      # AP Write smoke Suitelet
    PCE_SL_AP_CsvImport.js      # CSV 匯入 Suitelet(POST→GET PRG)
  libraries/
    PCE_LIB_AP_CsvImportReplay.js  # CSV 結果頁自 write log 還原 UI
    PCE_LIB_AP_WriteReturnCode.js  # returnCode 對照與 applyReturnCode
    PCE_LIB_AP_WritePayload.js  # action/shape/alias 正規化
    PCE_LIB_AP_InvoiceSave.js   # 進項驗證與寫入 PCE 進項資料
    PCE_LIB_AP_WriteDfo.js      # 公版不做事;客戶專用分支才換成審核與請購單
    PCE_LIB_AP_Validate.js      # 進項業務驗證(MOF 對齊)
    PCE_LIB_AP_Lookup.js        # 格式/稅別/扣抵/字軌 code↔id
    PCE_LIB_AP_Period.js        # 民國年月/申報期
    PCE_LIB_AP_WriteLog.js      # 寫入 customrecord_pce_ap_write_log
    PCE_AP_Fields.js            # 欄位 script id 常數
  clientscripts/                # UI(與本 API 分離)
  userevents/
    PCE_UE_APInvoice_DefaultValues.js  # beforeSubmit 再呼叫 Validate
  common/
    PCE_LIB_TaxId.js            # 統編檢查(由 Validate 引用)

2.0 API 呼叫紀錄(customrecord_pce_ap_write_log)

每次 POST(以及被拒的 GET)結束後由 RESTlet 自動建立一筆紀錄。寫入失敗只記 Execution Log,不影響 API 回應。

欄位Script ID說明
Methodcustrecord_pce_apwl_methodGET(應被拒)/POST
Actioncustrecord_pce_apwl_actionPOST 的 action
Request IDcustrecord_pce_apwl_request_id冪等鍵 requestId
Successcustrecord_pce_apwl_success回應 success
Error Codecustrecord_pce_apwl_error_code失敗時 error.code
Duration (ms)custrecord_pce_apwl_duration_ms處理耗時
Governance (units)custrecord_pce_apwl_governance本次 API 消耗的 SuiteScript governance units(寫入本紀錄前計算)
Usercustrecord_pce_apwl_user執行使用者
Requestcustrecord_pce_apwl_requestrequest JSON(過長截斷)
Responsecustrecord_pce_apwl_responseresponse JSON(過長截斷)

UI:Lists → Custom → PCE AP Write 呼叫紀錄。 部署後請確認 Integration/Role 對此 custom record 有 Create 權限。

2.1 POST 處理流程

flowchart TD
  A[POST body] --> B[validatePayloadShape]
  B -->|invalid| F[fail shape error]
  B -->|valid| C{action}
  C -->|apInvoiceOnly| D[normalize + validate + create invoices]
  C -->|transactionOnly| E[resolveOrCreateTransaction]
  C -->|transactionWithApInvoices| G[resolveOrCreateTransaction]
  G --> D2[normalize + validate + create invoices with parent]
  D --> H[ok + warnings]
  E --> H
  D2 --> H
  D -->|validate error| X[fail PCE_AP_INVOICE_VALIDATION_ERROR]
  D2 -->|validate error| X

流程圖原文

resolveOrCreateTransaction:有 transaction.id 則引用既有交易;否則若 type=vendorbill 且帶 purchaseOrderId,則 record.transform(purchaseorder → vendorbill),再依 itemLines[].orderline/quantity 覆寫(未列之 PO 行 quantity=0);其餘情況走既有 record.create + fields/lines。

2.2 驗證雙關卡

  1. RESTlet:buildValidatedInvoiceData → validateRecord;失敗不 save。
  2. UE:實際 record.save 時 beforeSubmit 再驗一次(CSV/WS/UI/RESTLET 皆會跑,視部署 context)。

逐條規則(必填、禁填、擇一、稅額容差、字軌、期別、錯誤代碼)見 進項憑證檢核規則。

Select 欄位(格式/課稅別/扣抵/彙加註記)可傳 代碼(如 "21")或 internal id;由 resolveSelectValue + Lookup 解析。進項格式 21/25 已拆 2 筆,僅傳代碼時須加 invCatCode 或 docFormatName。

3. 部署與環境

3.1 SDF / SuiteCloud

  • Script 物件:src/Objects/Scripts/customscript_pce_rl_ap_write.xml
  • FileCabinet:ap/restlets/、ap/suitelets/、ap/libraries/*(見 deploy.xml)
  • Deploy SuiteApp 後確認 Script Deployment Released、isdeployed = T

3.2 權限與 Integration

檢查項說明
Integration ApplicationOAuth 2.0(建議 M2M Client Credentials;測試可用 Authorization Code)
Scope至少勾選 RESTlets
Role需有 RESTlet 執行權、進項 custom record 建立/編輯、目標交易類型 create/edit;M2M mapping 綁定此 Role
Audience目前 deployment 設 allroles = T;正式環境建議收斂角色
Log level預設 DEBUG;正式可改 ERROR/AUDIT

3.3 取得 External URL

Customization → Scripting → Script Deployments → PCE RL AP Write → 複製 External URL。 格式大致為:

https://{accountId}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script={scriptId}&deploy={deployId}

帳號 ID、script/deploy 內部 id 以環境為準(scriptid 字串不等於 URL 上的數字 id)。

4. 錯誤碼(技術對照)

實作:ap/libraries/PCE_LIB_AP_WriteReturnCode.js;RESTlet ok()/fail() 與 enrichResponseWithRequest() 結尾皆呼叫 applyReturnCode()。

4.0 returnCode 區間

區間語意
0000–0099成功(0000 OK、0001 validateOnly、0002 冪等重播、0003 有 warnings、0004 partial)
1000–1999Payload shape
2000–2999進項驗證
3000–3999Lookup
4000–4999NetSuite 執行期
9000–9999未預期

4.1 Payload shape(寫入前、不進 try 內業務)

codereturnCode時機
INVALID_BODY1001body 非物件
INVALID_ACTION1002action 非法
REQUEST_ID_REQUIRED1003缺 requestId
REQUEST_ID_INVALID1004requestId 格式不合(需 8–64 字元 A–Z a–z 0–9 - _)
REQUEST_ID_PAYLOAD_MISMATCH1005同 requestId 已成功寫入,但本次 payload 指紋不同
AP_INVOICES_REQUIRED1006需要發票陣列卻空
AP_INVOICES_NOT_ALLOWED1007transactionOnly 帶了發票
AP_INVOICES_TOP_LEVEL_NOT_ALLOWED1008transactionWithApInvoices 仍使用 top-level 發票欄位
TRANSACTION_REQUIRED1009缺 transaction.id/type
TRANSACTION_NOT_ALLOWED1010apInvoiceOnly 卻帶新建交易內容
INVALID_TRANSACTION_TYPE1011type 不在白名單
TRANSACTION_LIMIT_EXCEEDED1012一次交易筆數超過上限(10)
CLIENT_REF_INVALID1013clientRef 非 1–64 字元字串
PCE_METHOD_NOT_ALLOWED1014不支援的 HTTP 方法
PURCHASE_ORDER_NOT_ALLOWED1015非 vendorbill 卻帶 purchaseOrderId
PURCHASE_ORDER_WITH_EXISTING_ID1016同時帶 id 與 purchaseOrderId
PO_ITEM_LINES_REQUIRED1017從 PO 建立時缺非空 itemLines
PO_ORDERLINE_REQUIRED1018item 列缺 orderline/line
PO_QUANTITY_REQUIRED1019item 列缺合法 quantity

1020–1027 留給 API進項審核與請購單。公版不會回這些代碼。

4.2 執行期(catch → fail)

name / codereturnCode說明
PCE_AP_INVOICE_VALIDATION_ERROR2001Validate 失敗;error.details = 欄位錯誤物件(亦出現在 results[i].error)
BATCH_FAILED2100批次內全部 leaf 失敗(多筆)
PARTIAL_SUCCESS0004部分成功;partialSuccess: true,仍回 results[]
PCE_LOOKUP_RESOLVE_ERROR3001code/id 無法對到 lookup
PCE_INVALID_TRANSACTION_TYPE3002create 時 type 對不到 record.Type
SKIPPED_PARENT_FAILED4001父交易失敗,巢狀發票未處理
PO_ORDERLINE_NOT_FOUND4002request orderline 不在 transform 後 VB
PO_TRANSFORM_FAILED4003PO→VB transform/save 失敗
NetSuite 原生錯誤4000權限、必填、record.save 失敗等
UNEXPECTED_ERROR9999無 name 的例外

Partial success 行為:processApInvoices/多筆 transaction 逐筆 try/catch,不因單筆失敗中斷整包。findSuccessfulWriteByRequestId 亦會把 returnCode=0004/partialSuccess 視為已提交,避免同 requestId 重試重複建單。

回應型態一律含 returnCode/returnMessage:

{
  "success": false,
  "returnCode": "2001",
  "returnMessage": "AP_INVOICE_VALIDATION_FAILED",
  "error": {
    "code": "PCE_AP_INVOICE_VALIDATION_ERROR",
    "message": "...",
    "details": null
  }
}

5. 測試策略

5.1 單元測試(本機,不需 NetSuite)

npm test
# 或分開跑
npm run test:ap-validate
npm run test:ap-write-payload
npm run test:ap-write-return-code
npm run test:ap-csv
檔案覆蓋
test/ap-write-payload.test.jsaction/tx type/alias/shape
test/ap-write-return-code.test.jsreturnCode 對照與成功優先序
test/ap-csv.test.jsCSV 解析/表頭/100 列上限
test/ap-validate.test.js進項驗證規則(MOF)
test/helpers/amd-loader.jsSuiteScript AMD → Node

變更契約時:先改 PCE_LIB_AP_WritePayload.js + 對應單元測試,再改 RESTlet 與文件。

5.2 帳號內冒煙測試(顧問/QA)

建議順序:

  1. GET → 應回 success: false、error.code: PCE_METHOD_NOT_ALLOWED(本 API 不允許 GET)。
  2. POST options.validateOnly: true + apInvoiceOnly 合法 payload → 不應產生 record。
  3. POST 故意錯統編/字軌 → PCE_AP_INVOICE_VALIDATION_ERROR + details。
  4. POST 真實寫入一筆 apInvoiceOnly(測試統編/字軌表需齊)→ UI 開進項確認。
  5. POST transactionWithApInvoices → 確認 VB/ER/VC/Prepayment parent 與子清單。
  6. 用既有 transaction.id 再掛第二張進項 → 驗證 1:N。
  7. (可選)有可用 PO 時:vendorbill + purchaseOrderId + itemLines[].orderline/quantity → 確認部分開帳與未列行 quantity=0。

5.3 Postman / curl 注意

5.4 建議測試資料檢查清單

  • [x] Lookup:憑證格式 21–29、課稅別、扣抵、彙加註記(Round C validate/寫入通過)
  • [x] 字軌表:對應申報期/格式有字軌(11508 格式 21:CA、CB)
  • [x] Vendor/Subsidiary/Account(建 VB 時;Round C fixtures)
  • [x] UE 部署含 RESTLET execution context(寫入路徑驗證通過)
  • [x] Client Script 不影響 RESTlet 路徑(預期;RESTlet 寫入成功)

5.5 帳號內實測紀錄(transactionOnly)

帳號:TD2905095(Chesley's Dev Environment)。 RESTlet:customscript_pce_rl_ap_write/customdeploy_pce_rl_ap_write。 Action:transactionOnly(實際建立交易)。 主要測試資料:Vendor 944(Lancome)、Subsidiary 8(US East)、Employee 1086、Expense account 415、Bank 422、Currency 1、Location 11。

Round A — SDF Installation smoke(2026-08-04)

類型結果Internal ID
Vendor Bill✅ Pass25647
Expense Report✅ Pass25648
Vendor Credit✅ Pass25649
Vendor Prepayment✅ Pass25551

原始 JSON:test/results/ap-write-smoke-result.json

Round B — Browser MCP + Suitelet smoke(2026-08-04)

入口:/app/site/hosting/scriptlet.nl?script=customscript_pce_sl_ap_write_smoke&deploy=customdeploy_pce_sl_ap_write_smoke&action=run (Suitelet 以登入 session 呼叫 https.requestRestlet → 正式 AP Write RESTlet。)

類型結果Internal IDUI 確認
Vendor Bill✅ Pass25747Bill #188,Lancome,$1,050,Pending Approval
Expense Report✅ Pass25847Employee Abigail Khan
Vendor Credit✅ Pass25848Bill Credit #15,memo 含 browser smoke
Vendor Prepayment✅ Pass25947VP #26,PAID,$1,050,Bank US East Checking

原始 JSON:test/results/ap-write-browser-smoke-result.json

Round C — Browser MCP full checklist §5.2(2026-08-04)

入口:…&action=checklist 本機單元測試:npm test → 49/49 Pass。

Step項目結果備註
5.4測試資料 fixtures✅vendor 944 / emp 1086 / bank 422
1GET 不允許✅/⚠️預期 PCE_METHOD_NOT_ALLOWED
2apInvoiceOnly + validateOnly✅不建檔
3a錯誤統編✅PCE_AP_INVOICE_VALIDATION_ERROR
3b錯誤字軌 AA✅GuiTrackNotAllowed;可用 CA、CB
4apInvoiceOnly 真實寫入✅apInvoiceId 27(CA26632189)
5.1transactionWithApInvoices VB✅tx 26149 + inv 127
5.2ER✅tx 26150 + inv 128
5.3VC✅tx 26151 + inv 129
5.4VP✅tx 26152 + inv 130
6既有 transaction.id 掛第 2 張(1:N)✅tx 26149 + inv 131;UI 子清單可見 CA26632688、CA26642463

JSON:test/results/ap-write-checklist-result.json

測試中一併修正:fileBizTax 改以 boolean setValue;docDate 以 N/format 轉成 Date 再寫入。

備註

  • GET 不允許;應回 PCE_METHOD_NOT_ALLOWED(契約/健康檢查改以文件與 validateOnly POST 為準)。
  • Vendor Prepayment 需設 Bank account(非 AP),且 account/payment 應在 subsidiary/entity 之後寫入(sourcing 會清掉過早設值)。
  • Smoke Suitelet 必須略過 internal id ≤ 0 的 vendor(例如 -3),否則會 INVALID_FLD_VALUE。

6. 除錯指南

現象可能原因處理
401 Unauthorizedaccess token 無效/過期/缺 Bearer重新打 token endpoint;檢查 Scope=restlets
403 / INSUFFICIENT_PERMISSIONRole 缺權限加 custom record/交易權限;檢查 M2M Role mapping
PCE_LOOKUP_RESOLVE_ERROR代碼未建 lookup 或傳錯查 PCE_LIB_AP_Lookup 與安裝 lookup
Validate 過但 save 失敗UE/NS 必填/ sourcingScript Execution Log;比對 UI 同資料
有 warnings 但仍 success稅額差額 1~5 元等「可過提示」屬預期;見 檢核規則
shape OK 但交易沒建validateOnly: true拿掉或設 false
apInvoiceOnly 帶 transactionshape 擋 TRANSACTION_NOT_ALLOWED改用 transactionWithApInvoices
無呼叫紀錄Role 缺 custom record Create開 customrecord_pce_ap_write_log 權限;查 PCE AP Write log failed

Execution Log 關鍵字:PCE AP Write RESTlet failed、PCE AP Write log failed。 除錯時優先開 PCE AP Write 呼叫紀錄 看 Request/Response。

7. 已知限制與設計取捨

  1. 交易欄位不經 PCE Validate(含客製欄):transaction.fields/expenseLines/itemLines 的 key 直接 setValue/setCurrentSublistValue,無白名單。標準欄與客製欄(custbody_/custcol_ 等)皆可透傳;必填、型別與權限由 NetSuite/客戶表單決定。詳見介接手冊 §6.0。
  2. transactionOnly + validateOnly:不驗證交易內容,只確認 payload shape。
  3. 不更新已存在的進項:payload 的 id/externalId 略過。已建立的 PCE 進項資料不經這支 API 修改。
  4. 不刪除/作廢:需另開流程。
  5. 並發重複發票:Validate 有重複檢核;極端並發仍可能 race,正式環境宜由上游控管。
  6. requestId 冪等:先用 requestId、POST、成功或 PARTIAL_SUCCESS 搜呼叫紀錄,再 record.load 讀 Request/Response(Long Text 不能當 Search 欄位,否則查詢失敗會當成沒有舊紀錄而再建立一次)。custom record 無真正 unique constraint,極端並發同 requestId 仍可能 race(實務上串列重試+UUID 可接受)。
  7. Script ID 更名:若帳號曾部署舊 BPM 命名 script,需停用舊 deployment,改用 customscript_pce_rl_ap_write。

8. 變更影響評估(給顧問)

變更類型影響
新增 alias低;更新 payload lib + 單元測試 + 介接手冊
改 action 語意高;破壞性,需版控與通知廠商
Validate 規則加嚴中高;既有整合可能大量失敗,需先 validateOnly 試跑
Lookup 資料缺漏高;立即 PCE_LOOKUP_RESOLVE_ERROR

9. 相關內部資產

資產路徑
UI 測試 Runbooktest/UI-TEST-RUNBOOK.md
UI Checklisttest/UI-TEST-CHECKLIST.md
進項驗證單元測試test/ap-validate.test.js
Payload 單元測試test/ap-write-payload.test.js
AP Write smoke 結果(SDF)test/results/ap-write-smoke-result.json
AP Write smoke 結果(Browser)test/results/ap-write-browser-smoke-result.json
AP Write smoke Suiteletap/suitelets/PCE_SL_ApWriteSmoke.js
AP CSV 匯入 Suiteletap/suitelets/PCE_SL_AP_CsvImport.js + ap/libraries/PCE_LIB_AP_Csv.js
AP CSV 結果還原ap/libraries/PCE_LIB_AP_CsvImportReplay.js(write log _csvUi 快照)
AP CSV 結果還原測試test/ap-csv-import-replay.test.js
AP Write 呼叫紀錄customrecord_pce_ap_write_log/PCE_LIB_AP_WriteLog.js
廠商介接手冊docs/PCE_GUI_MODULE_ap-write-api-integration-guide.md
Technical (EN)docs/PCE_GUI_MODULE_ap-write-api-technical.en.md
Integration (EN)docs/PCE_GUI_MODULE_ap-write-api-integration-guide.en.md
Postman collectiondocs/postman/PCE-AP-Write.postman_collection.json

文件版本:與 SuiteApp com.netsuite.pceguitw AP Write RESTlet 現行實作同步。