采购管理
本页是一篇完整方案指南:以 procurement 插件(com.auraboot.procurement,命名空间 pr,依赖 product-catalog / crm / inventory / finance / quality)为例,从用户场景一路讲到开发实施和典型错误。它声明 23 个 model、95 条命令、42 个页面,绝大部分能力靠 DSL JSON 配置,另带一个小后端 jar 为 8 条命令挂自定义 handler(收货回写已收量、需求转订单等)。下面每个标识符你都能在插件里 grep 到、对运行实例调得通。
注意命名:模型与字段前缀是
pr_(不是proc_),命令码是冒号格式pr:动词_名词(不是点号)。本页所有标识符以/Users/ghj/work/auraboot/plugins/procurement/下的真实配置为准。
1. 用户场景
一家制造企业的采购部,对接数十家供应商,日常覆盖:
- 业务部门或 MRP 提采购需求 → 采购专员转为采购订单;
- 采购订单提交审核 → 审批通过 → 收货 → 完成,大额订单需多级签字;
- 供应商先发预发货通知(ASN),到货后做收货与验收;
- 采购订单、收货单、供应商发票做三方匹配,差异要标记并解决;
- 委外加工:外协订单 → 发料 → 加工 → 收货质检;
- 框架合同管价目与变更,周期末按维度给供应商打评分卡。
采购、生产、财务看到的数据视图和能做的操作各不相同。


2. 需求痛点
- 跨步骤契约靠人维护:"这张订单要先审批才能收货""收货数量要回写订单行""ASN 没到货不让收货"——这些规则散落在前端、脚本、人脑里。
- 金额阈值与审批授权脱节:谁能审批多大金额的订单,没有统一闸门;改一次阈值要改好几处代码。
- 操作无授权无审计:谁在什么时候把订单推到了下一状态、为什么,没有记录。
- 集成困难:需求、收货、发票想自动触发下游动作(库存、财务),却只能人工二次录入。
这些都不是「再加一张表」能解决的,而是受控状态变更的问题。
3. 常规解法 vs AuraBoot 的设计哲学
3.1 常规解法是怎样的
如果让一个团队从零做采购到付款(P2P),通常是这条路:
- 领域建模:画实体和关系——供应商、采购需求、采购订单、收货单、发票、三方匹配、合同、外协单……
- 建表:把实体翻译成一堆
CREATE TABLE,加外键、状态字段、金额字段。 - 写 CRUD:每个实体一套 Repository / Service / Controller,增删改查全手写。
- 把业务规则 hard-code 进 service:「订单要先审批才能收货」「收货数量要回写订单行」「ASN 没到货不让收货」「订单×收货×发票三方核对」「按金额分多级审批」——全写死在 Java 里,审批阈值散落在 if-else 中。
- 各处补横切关注点:权限判断散落在每个 controller;审计靠手动插日志;想把收货、付款事件发给库存/财务,再搭一套集成。
- 前端:每个页面手写表单、列表、详情。
- 想接 AI / 自动化:对不起,得再包一层 API、再定义一遍权限和风险边界。
能跑,但审批规则写死在代码里,改一档金额阈值要改好几处;三方对账只能靠人逐张比对订单、收货、发票;供应商和价目主数据散落在各处;并发、审计、事件、AI 入口每一样都得自己扛。三个月起步,而且越改越脆。
3.2 AuraBoot 的设计哲学
AuraBoot 把这件事反过来:业务的「是什么」用声明描述,「怎么执行」由运行时统一兜底。三条核心理念:
- 元数据驱动:model / field / command / page / permission 都是声明(DSL JSON),不是手写代码。平台据此自动建表、生成接口、渲染页面——供应商主数据、合同价目、订单明细都是声明出来的模型,上面第 2、3、6 步基本消失。
- 单一命令管道:每个采购动作都是一条命令,走同一条 命令管道。鉴权、校验、状态机守卫、事务、审计、发事件这些横切关注点由管道统一处理,不写进每个业务里——你声明
pr:approve_purchase_order时只关心「pending→approved + 权限」,「草稿不能直接收货」「没有明细不能提交」由fromStates/toState与has_children校验白送(第 4、5 步从「自己扛」变成「平台默认」)。 - 命令即契约:同一条命令同时服务 UI 按钮、自动化规则(按金额路由审批)、BPM 节点、AI agent(靠
cmd_risk_level+agent_hint分级)。改审批闸门是改规则与权限,不是改命令代码;AI 原生不是事后包 API,而是天生的(第 7 步免了)。
3.3 同一个功能,两种活法
于是普通做法里那些「自己扛」的事,在 AuraBoot 变成平台契约的默认能力。每一格的 AuraBoot 列都是上面能 grep 到的真实命令/模型,不是宣传话术:
| 能力 | 自己写(手写后端) | 打包 ERP / 典型低代码 | AuraBoot(本插件) |
|---|---|---|---|
| 多级审批 / 金额阈值 | 阈值与审批授权写进 if-else,改一档要动代码 | 审批流现成,但规则写死,改阈值/加层级要二开 | pr:submit_purchase_order(draft→pending)+ pr:approve_purchase_order(pending→approved),阈值路由由自动化规则 + 权限决定,命令不改 |
| 三单匹配(PR→PO→收货→发票) | 自己写比对脚本,人逐张核三单 | 多数有匹配模块,但差异规则常要定制 | pr_three_way_match 走 pr:match_three_way(→matched)/ pr:flag_variance(→variance,L2)/ pr:resolve_three_way_match(→resolved),状态机内建 |
| 收货回写订单 / 跨步骤契约 | 手写「收货数量回写订单行」,易漏易错 | 闭源,想 hook 收货回写很难 | pr:receive_purchase_order(approved→receiving,带 handler)由后端按行回写已收量、自动建入库单 |
| 合同 / 价目主数据 | 自建合同表 + 手写生效/变更/到期逻辑 | 有合同,框架价目/变更常要加购模块 | pr_contract 走 pr:activate_contract / pr:add_amendment / pr:terminate_contract,生命周期声明化 |
| 被自动化 / AI 调用 | 还要再包一层 API,且无风险分级 | 基本无原生 AI 入口 | 同一条命令带 cmd_risk_level + agent_hint,UI / 自动化 / BPM / AI agent 同一条路径,安全可控 |
| 权限 + 审计 | 散落在各 controller,易漏 | 角色粗粒度,字段级常缺 | 14 个权限码 + 审计内建在命令管道,每次状态变更自动留痕 |
一句话:手写要三个月、还得自己扛审批阈值和三单对账;打包 ERP 快但改不动;AuraBoot 用声明式配置就拿到一套 production 级的采购到付内核,而且每个能力都能被 AI 安全驱动、被你自由扩展。 下面看它具体怎么搭。
4. 功能设计
4.1 数据模型(23 个)
| Model | 用途 | 关键状态字典 |
|---|---|---|
pr_supplier_common | 供应商主数据(分类 / 等级 / 联系方式) | pe_enable_status:enabled / disabled |
pr_purchase_request | 采购需求 | pe_request_status:pending / processing / completed / cancelled |
pr_purchase_order / pr_purchase_order_line | 采购订单 + 明细 | pe_order_status:draft / pending / approved / delivering / receiving / completed / cancelled |
pr_purchase_receipt / pr_purchase_receipt_line | 收货记录 + 明细 | — |
pr_asn_common | 预发货通知(ASN) | pr_asn_status:draft / sent / in_transit / received / cancelled |
pr_purchase_order_confirmation_common | 供应商订单确认 | pr_oc_status:pending / confirmed / partial / rejected / deviation_proposed / deviation_accepted / deviation_rejected |
pr_purchase_return / pr_purchase_return_line | 采购退货单 + 明细 | pe_return_status:draft / pending / approved / confirmed / cancelled |
pr_purchase_payment | 付款记录 | pe_payment_status:not_paid / partial / fully_paid |
pr_three_way_match | 三方匹配结果(订单×收货×发票) | pr_match_status:pending / matched / variance / resolved |
pr_contract / pr_contract_line / pr_contract_amendment | 采购合同 / 明细 / 变更 | pr_contract_status:draft / under_review / active / expiring / expired / terminated |
pr_supplier_scorecard / pr_scoring_criteria / pr_scorecard_detail | 供应商评分卡 / 评分标准 / 评分明细 | pr_scorecard_status:draft / submitted / approved |
pr_outsource_order / pr_outsource_order_line | 外协订单 + 物料明细 | pr_oso_status:draft / submitted / approved / materials_sent / in_progress / received / completed / cancelled |
pr_outsource_receipt / pr_outsource_receipt_line | 外协收货单 + 明细 | pr_osr_status:draft / pending_qc / qc_passed / qc_failed / completed |
pr_spend_category | 费用分类 | — |
4.2 命令与状态机
命令命名 pr:<动词>_<名词>(冒号 + 下划线)。以采购订单为例,状态机由几条命令组成:
| 命令 | 流转 |
|---|---|
pr:submit_purchase_order | draft → pending(提交审核) |
pr:approve_purchase_order | pending → approved(审核通过) |
pr:receive_purchase_order | approved → receiving(收货,带 handler) |
pr:complete_purchase_order | receiving → completed(完成) |
pr:cancel_purchase_order | draft / pending / approved → cancelled(取消) |
其他主链路命令:pr:convert_request_to_po(需求转订单,带 handler)、pr:create_purchase_receipt / pr:confirm_purchase_receipt(创建 / 确认收货)、pr:match_three_way / pr:flag_variance / pr:resolve_three_way_match(三方匹配 / 标记差异 / 解决)、pr:activate_contract / pr:add_amendment / pr:terminate_contract(合同启用 / 变更 / 终止)、pr:submit_scorecard / pr:approve_scorecard(评分卡提交 / 审批)、pr:create_outsource_order / pr:send_materials / pr:receive_outsource(外协下单 / 发料 / 收货)。
每条命令都是一段声明。例如 pr:submit_purchase_order(config/commands/pr_submit_purchase_order.json)的真实定义:
{
"code": "pr:submit_purchase_order",
"displayName:zh-CN": "提交审核",
"displayName:en": "Submit Order",
"description": "Submit purchase order for approval: draft -> pending; requires at least one line item.",
"type": "state_transition",
"modelCode": "pr_purchase_order",
"stateField": "pr_po_status",
"fromStates": ["draft"],
"toState": "pending",
"validation": {
"rules": [
{ "type": "has_children", "childModel": "pr_purchase_order_line",
"parentField": "pr_pol_order_id", "minCount": 1,
"message:zh-CN": "请至少添加一条采购订单明细后再提交" }
]
},
"permissions": ["pr.purchase.manage"],
"agent_hint": "Transition pr purchase order status from draft to pending.",
"cmd_risk_level": "L1"
}读出来的设计信息:这是一条 state_transition(draft → pending),带 has_children 校验(没有明细行不让提交),要求 pr.purchase.manage 权限,风险级 L1,并通过 agent_hint 告诉 agent 它能做什么。状态字段是 pr_po_status,值约束来自字典 pe_order_status。
需要写代码的命令多一个 handler 字段,例如 pr:receive_purchase_order 声明 "handler": "pr:receive_purchase_order",由插件后端的 ReceivePurchaseOrderHandler 按行回写采购订单行的已收数量——纯状态流转(如 submit / approve)不需要 handler。
4.3 权限与角色
14 个权限码(<模块>.<资源>.<动作>,每资源 manage + read):
pr.supplier.read pr.supplier.manage
pr.purchase.read pr.purchase.manage
pr.outsource.read pr.outsource.manage
pr.three_way_match.read pr.three_way_match.manage
pr.scorecard.read pr.scorecard.manage
pr.contract.read pr.contract.manage
pr.spend.read pr.spend.manage
4 个角色:pr_admin(ERP 管理员,全部权限)、pr_purchaser(采购专员)、pr_production(生产主管,只管外协)、pr_finance(财务专员,采购只读)。命令 permissions 里绑的就是这些权限码——pr:approve_purchase_order 要 pr.purchase.manage,只有 pr_admin 和 pr_purchaser 持有。
4.4 页面
42 个页面:各 model 的 list / form / detail 三件套(采购订单、收货、ASN、退货、付款、合同、评分卡、外协、供应商、三方匹配、费用分类等),另有 2 个 dashboard(pr_procurement_dashboard 采购总览、pr_spend_analysis_dashboard 费用分析)。
5. 具体开发与实施
先掌握基础。本插件主体是配置,只为少数命令挂后端 handler。动手前请先读:Model 与 Field · Command · 命令管道 · Permission · 插件清单 · 纯配置 Plugin · Page Designer。
落地一个像 procurement 这样的插件,步骤是:
- 定义 model 与字段 ——
config/models.json+config/fields/,字段类型用平台dataType(string/integer/enum/decimal/date…),枚举字段用dictCode指向config/dicts.json里的字典(如pr_po_status绑pe_order_status)。详见 Model 与 Field。 - 声明命令 —— 每个状态流转一个
config/commands/<动词>_<名词>.json,用type: state_transition+fromStates/toState,并在permissions绑权限码;需要写业务逻辑的命令多加handler字段并在后端实现对应 handler。详见 Command。 - 配 binding 与 bindingRules —— model-field binding 放
config/bindings/;命令级钩子(如currencyConversionHandler汇率换算)放独立config/bindingRules.json,并在plugin.json的resourceDirs注册(见「典型错误」)。 - 设计页面 ——
config/pages/,list / form / detail 用 Page Designer 出 DSL。 - 权限、角色、字典、菜单 ——
config/permissions.json/roles.json/dicts.json/menus.json。 - 打包导入 —— 用
auraCLI 的import-directory-sync(参数是目录 path)或平台导入接口;校验返回success:true才算导入成功。带后端 jar 的插件还要把backend/build/libs/procurement-plugin-1.0.0.jar编出来、装进插件目录后重启 backend,否则 handler 命令报[S-EXT-HANDLER] references unregistered handler。详见 插件清单。
6. 常见配置
- 审批阈值:用自动化规则在
pr:submit_purchase_order之后按金额路由——小额由pr:approve_purchase_order自动审批,大额挂人工任务;命令本身只管pending → approved的合法性,审批授权由权限 + 规则决定。 - 三方匹配差异:
pr_three_way_match走pr:match_three_way(pending → matched)→ 发现差异pr:flag_variance(→ variance)→ 处理后pr:resolve_three_way_match(→ resolved),全程在pr_match_status上。 - 合同生命周期:
pr_contract_status从 draft 经pr:submit_contract/pr:activate_contract到 active,pr:add_amendment记变更,到期pr:expire_contract/ 终止pr:terminate_contract。 - 外协闭环:
pr_oso_status走 draft → submitted →(pr:approve_outsource_order)approved →(pr:send_materials)materials_sent →(pr:start_outsource)in_progress →(pr:receive_outsource)received → completed。 - 汇率换算:多币种订单在
config/bindingRules.json里给pr:create_purchase_order挂currencyConversionHandler,把外币金额按汇率折算到本位币字段,无需改命令定义。
7. 典型错误
- 命令码用点号:写成
proc.po.approve跑不通——真实是冒号 + 动词_名词:pr:approve_purchase_order。 - 模型前缀用
proc_:模型是pr_前缀,且供应商是pr_supplier_common、采购订单是pr_purchase_order、收货是pr_purchase_receipt(不存在proc_supplier/proc_po/proc_grn/proc_rfq这类名字)。 - 绕过命令直接改订单表:直接 UPDATE
pr_purchase_order会跳过状态机、权限校验、has_children校验和 handler 的下游回写(如收货回写已收量),导致数据失真。一切状态变更都走命令。 - 提交时没有明细行:
pr:submit_purchase_order的has_children校验会拒绝(「请至少添加一条采购订单明细」)——这是设计,不是 bug。 - bindingRules 写进 commands.json 内联:不会被导入;必须独立
config/bindingRules.json并在resourceDirs注册,否则命令钩子(如汇率换算)静默不生效。 - handler 命令只导配置不打 jar:
pr:receive_purchase_order等 8 条命令带后端 handler,光导 config 不够;jar 没编、没装、没重启会报[S-EXT-HANDLER] references unregistered handler。 - 命令执行 payload 结构:字段放在
{ "payload": { ... }, "operationType": ... },目标记录用targetRecordId(不是recordId)——放错位会出现「执行成功却字段为空」的迷惑性报错。