闭环二:物料与采购

第 3 课 · 共 7 课 约 9 分钟

物料跨过安全库存就起草采购订单,草稿与批准分成两个 Action,批准留给采购员。

本课目标

读完这一课,你将能够

  • 设计「低于安全库存就起草采购订单」的闭环:对象、两步 Action、触发条件与效果
  • 把起草和批准拆成两个 Action,并用角色与提交条件设好批准关口
  • 说出通知会在哪里被拦下,以及为什么通知不能是唯一入口

对象与动词:草稿与批准是两步

上一课的订单闭环一发现缺料,就不再建议放行,这一课接着处理缺料。这个闭环的决定是:这种物料现在要不要买、买多少、向谁买。对象是 material、supplier 和 purchaseOrder,关系是 materialPurchaseOrders、supplierPurchaseOrders,加上上一课用过的 productMaterials。采购订单的 status 这里取三个值:draft、approved、received。

草稿和正式订单是同一个对象的两个状态,不是两个对象类型。这样批准只是改一个属性,整段历史留在同一条记录里,也避免出现两套近似的类型。

动词拆成两个:create-purchase-order 起草,approve-purchase-order 批准。起草用 createObject,主键用 $uuid,状态固定为 draft,可以交给自动化。批准必须由人执行。用 roles 收窄角色,再用提交条件检查 authorKind 等于 human、状态和额度:

{
  "kind": "actionType",
  "apiName": "approve-purchase-order",
  "title": "批准采购订单",
  "schema": {
    "parameters": [
      { "name": "purchaseOrder", "type": "object", "objectType": "purchaseOrder", "required": true }
    ],
    "rules": [
      { "type": "modifyObject", "objectType": "purchaseOrder", "object": "$purchaseOrder", "values": { "status": "approved" } }
    ],
    "submissionCriteria": [
      {"condition": {"type": "comparison", "left": {"user": "authorKind"}, "operator": "is", "right": {"literal": "human"}}, "failureMessage": "只有人能批准采购订单"},
      { "condition": { "type": "comparison", "left": { "param": "purchaseOrder", "property": "status" }, "operator": "is", "right": { "literal": "draft" } },
        "failureMessage": "只有草稿状态的采购订单可以批准" },
      { "condition": { "type": "or", "conditions": [
          { "type": "comparison", "left": { "param": "purchaseOrder", "property": "qty" }, "operator": "lte", "right": { "literal": 5000 } },
          { "type": "comparison", "left": { "user": "id" }, "operator": "isIncludedIn", "right": { "literal": ["buyer-lead"] } } ] },
        "failureMessage": "数量超过 5000 的采购订单要采购主管批准" }
    ],
    "roles": ["developer", "editor"],
    "summary": "{purchaseOrder} 已批准",
    "actionLog": true
  }
}

两个 Action 之间的关口留给人。工作流按提供方应用的成员角色执行 Action,roles 收窄可执行的角色。角色不能区分人和智能体;批准 Action 还要检查 authorKind 等于 human。智能体起草后,由人批准。

触发与数据:库存一变就判断

库存来自 ERP,一变就可能跨过安全线,所以用 change,不轮询:

"trigger": {
  "change": {
    "type": "material",
    "when": "=object.onHandQty < object.safetyStockQty",
    "cooldown": "1d",
    "input": { "material_code": "=object.materialCode" }
  }
}

冷却设成 1 天,是因为 ERP 会反复更新同一种物料,而它一整天都低于安全线。冷却期内的变化只推进游标,不开跑。

  • material 库存:change,一变就判断。
  • 在途的 purchaseOrder:起草前现查,避免重复起草,不触发。
  • supplier 评分与 leadTimeDays:变得很慢,起草时读当前值,不触发。

来源清单里每一行还要写负责人:库存归仓库,采购订单归采购,供应商评分归质量。数据不对时,才知道该找谁。

效果:谁算,谁写,谁批

  1. 01查询这种物料、在途采购订单、历史供应商
  2. 02计算补货量与供应商,按规则算
  3. 03AI 分析写理由与风险:交期、集中度
  4. 04写回create-purchase-order,只到草稿
  5. 05通知提醒采购员批准

补货量按规则算:示例口径是「安全库存的 2 倍,减去现有库存,再减去在途」;供应商挑评分最高的一家。这两个数由计算步骤给出,模型只写理由和风险,不改数字。评分最高的不一定最合适,新供应商没有历史订单,规则里根本挑不到它;这是有意的取舍,写进理由,让采购员来决定。

自主程度是「批准」:智能体起草到草稿,采购员在 Actions 页或 Nexus 应用里批准。写回步骤带一个 when,「没有在途草稿」才写,同一种物料不会有两张草稿。自动化写出的是采购订单,不是物料,所以不会回头触发自己;一批物料同时低于安全线,就各开一次运行。

还要想一想重跑。这里起草的主键用 $uuid,重跑不会被主键挡住,所以靠写回步骤的 when(没有在途草稿才写)防重复。想让主键来挡,就在计算步骤里用 concat 和 today() 拼出编号,例如 PO-M-1042-2026-09-29,作为参数传给 Action:createObject 遇到已有的主键会拒绝,同一天重跑就不会有第二张。

出错的地方:只有 20 条邮件

随口一问

每种物料一封通知。一次盘点后 60 种物料同时低于安全线,只有前 20 条发得出去,其余 40 种没人知道。

好的交代

草稿照常写入,邮件只是提醒。每天早上再由一个定时工作流汇总「待批准的采购订单」,发一封。

每个应用每天最多认领 limits.notificationsPerDay 条通知,缺省 20 条。一条通知发给多人、多个通道,仍算一条。超出的记为「已拦下」,不发,但草稿已经写好,所以通知不能是唯一入口。本例的收件人写在清单里,最多 5 个,使用本公司账号。

  • 安全库存本身不准:设错了,就会一次起草一大批。先用验收问题核对几种物料的安全库存,再开自动化。
  • 草稿没人批:草稿越积越多,采购员开始忽略。用每天早上的汇总提醒,并看批准所占的比例。
  1. 验收问题

    「哪些物料低于安全库存,各在途多少?」「哪家供应商延误让最多工单有风险?」

  2. 只校验批准

    批准一张已批准的订单应当失败;用不在名单里的账号批准数量 6000 的草稿也应当失败。

  3. 预演

    在 test 通道对三种物料预演:刚跨线、远低于线、已有在途草稿。

aidc semantic apply approve-purchase-order --param purchaseOrder=PO-2609-031 --validate-only
aidc semantic automate run procurement-watch --param material_code=M-1042 --channel test --preview
aidc app usage procurement-watch

要点

  • 起草与批准拆成两个 Action,中间的缝就是人工关口。
  • 库存用 change 触发,冷却期内的变化只推进游标,不开跑。
  • 补货量与供应商由计算步骤按规则给,模型只写理由与风险。
  • 每个应用每天缺省最多 20 条通知,超出的被拦下,所以通知不能是唯一入口。

练一练

设计你的「起草加批准」

选一个「系统起草、人来批准」的场景:报销、请购、排班都行。

写出起草和批准两个 Action 的参数与规则。批准 Action 写三条提交条件:人类执行人、草稿状态、额度,并写失败信息。

小测

选一个答案,马上看解析。

Q1为什么批准要单独做成一个 Action,而不是起草时顺手写 approved?

Q2盘点后 60 种物料同时低于安全线,应用每天缺省只发 20 条通知。会发生什么?

Q3为什么这个工作流写出采购订单,不会回头触发自己?

延伸阅读