销售管理
本页是一篇完整方案指南:以 sales 插件(com.auraboot.sales,命名空间 sl)为例,从用户场景一路讲到开发实施和典型错误。它是 config 型插件——15 个 model、72 条命令、29 个页面全部是 DSL JSON 声明,没有任何后端 Java;下面每个标识符你都能在插件里 grep 到、对运行实例调得通。
它声明依赖
product-catalog(产品主数据)、crm(客户)、inventory(库存)和finance(汇率)。这四个插件未安装时,导入sales会被解析器拒绝。
1. 用户场景
一家电子元器件分销商,销售执行横跨四个部门,每天循环这条链路:
- 销售下单 → 提交审核 → 审批(查信用额度)→ 发货 → 收款;
- 仓库按订单发货 / 装箱(纸箱 / 托盘,带序列号),关联回销售订单;
- 财务收款并按支付状态汇总回订单(未付 / 部分 / 已付);
- 客服处理退货:开 RMA → 收货 → 检验 → 决定处置(维修 / 换货 / 退款 / 报废)→ 开信用凭证冲抵应收;
- 多币种报价(人民币 / 美元 / 欧元 / 英镑 / 日元),下单时按汇率换算成本位币。
销售专员、仓库、财务、客服看到的单据视图和能做的操作各不相同。
2. 需求痛点
- 状态无管控:Excel 里的「订单状态」谁都能改,审批、发货、收款没有强制顺序,事后查不到谁在什么时候推进了哪一步。
- 跨部门交接断链:报价接受、订单审批、发货确认、RMA 授权这些交接点没有契约,改一处下游全靠人工二次录入。
- 多币种对账难:交易币种和本位币各记一套,汇率口径不统一,月底对账错乱。
- 集成困难:CRM 商机赢单、QC 报告、自动化规则想自动触发销售动作,却只能靠人工录单。
这些都不是「再加一张表」能解决的,而是受控状态变更的问题。
3. 产品方案
在 AuraBoot 里,每一个销售动作都是一条命令,走统一的 命令管道:
- 订单详情页的「审批」按钮调用
sl:approve_sales_order,而不是裸写订单表;命令在管道里统一鉴权 → 校验 → 状态流转 → 审计 → 发事件。 - 同一条命令既是 UI 按钮的目标,也能被自动化规则、BPM 流程、AI agent 调用(命令带
cmd_risk_level和agent_hint),CRM 赢单、QC 报告因此能自动触发销售动作。 - 多币种换算不是写死的代码,而是
bindingRules.json里挂在sl:create_sales_order上的currencyConversionHandler——下单时自动按汇率把交易金额折算成本位币。
4. 功能设计
4.1 数据模型(15 个)
模型码统一以 _common 结尾(供跨行业 overlay 复用)。
| Model | 用途 | 关键状态 / 字段 |
|---|---|---|
sl_sales_order_common / sl_sales_order_line_common | 销售订单 + 明细 | 状态字段 sl_so_status(字典 pe_order_status);编号 SO-{yyyyMMdd}-{seq} |
sl_shipment_common / sl_shipment_line_common | 发货单 + 明细 | 状态字段 sl_sh_status(字典 pe_confirm_status);编号 SH-{yyyyMMdd}-{seq} |
sl_packing_common / sl_packing_line_common | 装箱单 + 明细(纸箱 / 托盘,带序列号) | 包装类型字典 sl_pack_type |
sl_sales_collection_common | 收款记录 | 支付方式 pe_payment_method,收款状态 pe_payment_status |
sl_sales_return_common / sl_sales_return_line_common | 销售退货单 + 明细 | 退货状态字典 pe_return_status,责任归因 pe_fault_attribution |
sl_rma_common | 退货授权单(检验 + 处置) | 状态字段 sl_rma_status(字典 sl_rma_status),处置 sl_rma_disposition |
sl_credit_memo_common | 信用凭证(冲抵应收) | 状态字段 sl_cm_status(字典 sl_cm_status) |
sl_order_change_common | 订单变更申请 | 变更状态 pe_change_status,变更类型 pe_change_type |
sl_price_list_common / sl_price_list_item_common | 价格表 + 行项 | 价格表状态字典 sl_price_list_status,币种 sl_currency |
sl_discount_rule_common | 折扣规则(百分比 / 固定金额 / 阶梯) | 折扣类型 sl_discount_type,状态 sl_discount_status |
注意:
models.json里sl_sales_order_common的lifecycle_description是一段人读的说明文字(写着 DRAFT→CONFIRMED→SHIPPED…),它不是状态机定义。真正驱动流转的是状态字段sl_so_status+ 字典pe_order_status+ 下面的命令——以命令为准。
4.2 命令与状态机
命令命名 sl:<动词>_<名词>(冒号 + 动词_名词,不是点号)。销售订单的状态流转由这几条命令驱动:
| 命令 | 流转 | 作用 |
|---|---|---|
sl:create_sales_order | → draft | 建单(create 型) |
sl:submit_sales_order | draft → pending | 提交审核,须至少一条明细 |
sl:approve_sales_order | pending → approved | 审批 |
sl:deliver_sales_order | approved → delivering | 转入发货 |
sl:complete_sales_order | delivering → completed | 完成 |
sl:cancel_sales_order | draft/pending/approved → cancelled | 取消 |
发货 / RMA / 信用凭证各有独立的状态机:sl:confirm_shipment(发货 draft → confirmed);RMA 走 sl:create_rma → sl:receive_rma(authorized → received)→ sl:inspect_rma(received → inspected)→ sl:decide_rma_disposition(inspected → disposition_decided)→ sl:close_rma;信用凭证 sl:approve_credit_memo → sl:apply_credit_memo(approved → applied,冲抵应收,不可逆)。
每条命令都是一段声明。例如 sl:submit_sales_order(config/commands/sl_submit_sales_order.json)的真实定义:
{
"code": "sl:submit_sales_order",
"displayName:zh-CN": "提交审核",
"description": "Submit sales order for approval: draft -> pending; requires at least one line item.",
"type": "state_transition",
"modelCode": "sl_sales_order_common",
"stateField": "sl_so_status",
"fromStates": ["draft"],
"toState": "pending",
"validation": {
"rules": [
{ "type": "has_children", "childModel": "sl_sales_order_line_common",
"parentField": "sl_sol_order_id", "minCount": 1,
"message:zh-CN": "请至少添加一条订单明细后再提交" }
]
},
"permissions": ["sl.sales.manage"],
"extension": {
"confirmMessage:zh-CN": "确认将订单状态提交为待审核?"
},
"agent_hint": "Transition sl sales order status from draft to pending.",
"cmd_risk_level": "L1"
}读出来的设计信息:这是一条 state_transition(draft → pending),带 has_children 校验(没有明细行不让提交),要求 sl.sales.manage 权限,风险级 L1,弹确认框,并通过 agent_hint 告诉 agent 它能做什么。订单详情页的「提交审核 / 审批 / 发货 / 完成 / 取消」按钮分别绑定 sl:submit_sales_order / sl:approve_sales_order / sl:deliver_sales_order / sl:complete_sales_order / sl:cancel_sales_order(页面里以 action.command 形式引用),而不是裸 PUT /api/sl_sales_order_common。
4.3 权限与角色
10 个权限码(<模块>.<资源>.<动作>,每资源 manage/<操作> + read):
sl.sales.manage sl.sales.read
sl.rma.manage sl.rma.read
sl.financial.credit_memo sl.financial.credit_memo.read
sl.packing sl.packing.read
sl.price_list.manage sl.price_list.read
5 个角色,职责按部门切分:
sl_admin(ERP 管理员)——全部权限;sl_sales(销售专员)/sl_crm(CRM 专员)——sl.sales.manage+sl.sales.read,不持有信用凭证和 RMA 权限;sl_finance(财务专员)——sl.financial.credit_memo+sl.rma.read+sl.sales.read,信用凭证财务专属;sl_warehouse(仓库管理员)——只有sl.packing+sl.packing.read。
权限在命令管道里强制求值,无需任何自定义 Controller:销售专员看不到信用凭证按钮,因为 sl:apply_credit_memo 要的是 sl.financial.credit_memo,他没有。
4.4 页面
29 个页面 + 1 个 dashboard。每个有 list / form / detail 三件套的 model(销售订单、发货单、装箱单、收款、退货、RMA、信用凭证、价格表、折扣规则各 3 个),sl_order_change_common 只有 list / form 两个;另有 sl_sales_dashboard 看板。命令通过 detail 页的行动按钮(action.command)和 form 页的提交动作(sl:create_sales_order)接入,不是写死的路由。
5. 具体开发与实施
先掌握基础。本插件没有任何后端 Java,全靠平台的几个核心契约。动手前请先读:Model 与 Field · Command · 命令管道 · Permission · 插件清单 · 纯配置 Plugin · Page Designer。
落地一个像 sales 这样的 config 插件,步骤是:
- 声明依赖 ——
plugin.json的dependencies列上游插件(product-catalog/crm/inventory/finance);依赖缺失导入会被解析器拒绝。 - 定义 model 与字段 ——
config/models.json+config/fields/,字段类型用平台dataType,父子单据(订单 + 明细)在extension.documentConfig里声明lineModel/lineForeignKey/codePattern/statusField。详见 Model 与 Field。 - 声明命令 —— 每个 model 一个
config/commands/<命令>.json,状态流转用type: state_transition+fromStates/toState/stateField,并在permissions绑权限码。详见 Command。 - 配 binding 与 bindingRules ——
config/bindings/(model-field 绑定)和独立的config/bindingRules.json(命令副作用,如本插件的currencyConversionHandler),两者都要在resourceDirs注册(见「典型错误」)。 - 设计页面 ——
config/pages/,列表/表单/详情用 Page Designer 出 DSL,detail 页用action.command接行动按钮。 - 权限、角色、字典、菜单 ——
config/permissions.json/roles.json/dicts.json/menus.json。 - 打包导入 —— 用
auraCLI 的import-directory-sync(参数是目录 path)或平台导入接口;校验返回success:true才算导入成功。详见 插件清单。
6. 常见配置
- 多币种换算:在
bindingRules.json把currencyConversionHandler挂到sl:create_sales_order上,配currencyField/rateField/baseCurrencyField/amountFields,下单时自动按汇率折算本位币;币种取自字典sl_currency。 - 审批阈值自动化:用自动化规则在「订单金额低于阈值」时自动发
sl:approve_sales_order,高于阈值才转人工——同一条命令,人和规则都能调。 - 价格表与折扣:
sl_price_list_common按生效日期 + 币种 + 优先级管理价格;sl_discount_rule_common的sl_discount_type选percentage/fixed_amount/tiered;两者都走draft → active ↔ inactive → archived生命周期。 - RMA → 信用凭证闭环:RMA 走完
inspect → decide_disposition(处置选repair/replace/refund/scrap),退款类用sl:create_credit_memo→sl:approve_credit_memo→sl:apply_credit_memo冲抵应收。 - 从投诉开 RMA:
sl:create_rma_from_complaint建一张 RMA 草稿;它的自动预填(从投诉单带客户/订单)需要一个插件后端 handler,纯配置版只建草稿。
7. 典型错误
- 命令码用点号:写成
sales.order.confirm或sales.rma.approve跑不通——真实是冒号 + 动词_名词:sl:submit_sales_order、sl:approve_sales_order。RMA 也没有approve命令,授权后第一步是sl:receive_rma。 - 绕过命令直接改订单表:直接 UPDATE
sl_sales_order_common会跳过状态机、has_children校验、权限和审计事件,导致流程错乱。一切状态变更都走命令。 - 提交时没有明细行:
sl:submit_sales_order的has_children校验会拒绝(「请至少添加一条订单明细」)——这是设计,不是 bug。 - bindingRules 写进 commands.json 内联:不会被导入;必须独立
bindingRules.json并在resourceDirs注册,否则报[S-EXT-HANDLER] references unregistered handler。本插件的currencyConversionHandler就是这么挂的。详见 纯配置 Plugin。 - 命令执行 payload 结构:字段放在
{ "payload": { ... }, "operationType": ... },目标记录用targetRecordId(不是recordId)——放错位会出现「执行成功却字段为空」的迷惑性报错。 - model 码漏
_common后缀:这个插件的模型码全部以_common结尾(sl_sales_order_common、sl_rma_common…),写成sl_sales_order会找不到 model。也别去找报价单(quotation)——本插件不含报价模型,流程从「销售订单」起。