用 WorkBuddy 打通金蝶云·星空旗舰版 OpenAPI:采购订单从建单到审核全流程实录
一、背景与目标 目标很明确:用 API 创建一张采购订单,并让它到达**已审核(C)**状态。 听起来简单,实际踩了整整一天。核心难点不在于"调接口",而在于: 1. 大量字段是**隐藏必填**,报错一个、补一个、再冒出下一个; 2. 部分字段**key 名与直觉完全不符**,写错了接口还返回成功,只是值不落库(静默失败); 3. 同一个语义的字段(如"货主")在不同实体类型下有**不同的枚举值**,用错类型同样静默失败。 二、环境准备 2.1 认证:OAuth2 取 token 苍穹网关的认证头字段名是 **`accessToken`**(驼峰、无下划线)。写成 `access_token` 或 `Authorization: Bearer` 一律返回 401。 ```python import httpx, uuid, datetime BASE_URL = "https://<你的域名>/kapi" payload = { "client_id": "", "client_secret": "", "username": "<用户名>", "accountId": "<账套ID>", "language": "zh_CN", "nonce": str(uuid.uuid4()), "timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), } headers = {"Content-Type": "application/json", "x-acgw-identity": "<网关身份串>"} # 私有云需要 r = httpx.post(f"{BASE_URL}/oauth2/getToken", json=payload, headers=headers, timeout=30) token = r.json()["data"]["access_token"] ``` 后续所有请求都带这个头: ```python H = {"Content-Type": "application/json", "accessToken": token, "x-acgw-identity": "<网关身份串>"} ``` 2.2 返回码陷阱 **成功与否要看 `errorCode`(字符串 `"0"` 表示成功),不是 HTTP 状态码。** 而且完整错误信息藏在 `data.result[].errors[].rowMsg[]` 里,顶层的 `message` 会被截断成「……等 N 处错误」。排查时一定要打印完整响应体。 2.3 接口路径约定 ``` /v2/{appId}/{formId}/{操作} ``` 采购订单:`/v2/pm/pm_purorderbill/{add|query|getList|batchSubmit|batchAudit}` - 写操作是 **`add`**,不是 `save` / `create` / `submit`(这些全是 404) - 请求体格式:`{"data": [ {...} ]}`,`data` 必须是**数组** - 查询类接口必须带 `pageNo` / `pageSize`,否则返回 0 条 三、五个致命坑(本文最有价值的部分) 坑 1:含税单价的 key 是 `priceandtax`,不是 `taxprice` 最初按直觉写了 `"taxprice": 10`,接口返回 `ec=0`(成功!),但打开单据一看:**含税单价和税率都是空的**。 读回对比手工单才发现问题——所有金额字段全是 0: json { "price": 0.0, "priceandtax": 0.0, "taxrate": 0.0, "taxrateid_number": "" } **正确写法:** ```python "priceandtax": 10, # 含税单价(不是 taxprice!) "taxrate": 6, # 税率数值 "taxrateid_number": "V6", # 税率档编码,必须显式传 ``` > **教训**:API 接受了无效 key 却不报错、也不赋值。这类"静默失败"必须靠**读回校验**才能发现,不能只看返回码。 坑 2:货主的 `ownertype` 是 `bos_org`,不是 `bd_supplier` 这是卡得最久的一个。提交时一直报: ``` 请填写"物料明细"第1行:"货主"。 ``` 直觉认为"采购订单的货主"应该是供应