销售管理

本页是一篇完整方案指南:以 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_levelagent_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.jsonsl_sales_order_commonlifecycle_description 是一段人读的说明文字(写着 DRAFT→CONFIRMED→SHIPPED…),它不是状态机定义。真正驱动流转的是状态字段 sl_so_status + 字典 pe_order_status + 下面的命令——以命令为准。

4.2 命令与状态机

命令命名 sl:<动词>_<名词>(冒号 + 动词_名词,不是点号)。销售订单的状态流转由这几条命令驱动:

命令流转作用
sl:create_sales_orderdraft建单(create 型)
sl:submit_sales_orderdraft → pending提交审核,须至少一条明细
sl:approve_sales_orderpending → approved审批
sl:deliver_sales_orderapproved → delivering转入发货
sl:complete_sales_orderdelivering → completed完成
sl:cancel_sales_orderdraft/pending/approved → cancelled取消

发货 / RMA / 信用凭证各有独立的状态机:sl:confirm_shipment(发货 draft → confirmed);RMA 走 sl:create_rmasl:receive_rma(authorized → received)→ sl:inspect_rma(received → inspected)→ sl:decide_rma_disposition(inspected → disposition_decided)→ sl:close_rma;信用凭证 sl:approve_credit_memosl: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 插件,步骤是:

  1. 声明依赖 —— plugin.jsondependencies 列上游插件(product-catalog / crm / inventory / finance);依赖缺失导入会被解析器拒绝。
  2. 定义 model 与字段 —— config/models.json + config/fields/,字段类型用平台 dataType,父子单据(订单 + 明细)在 extension.documentConfig 里声明 lineModel / lineForeignKey / codePattern / statusField。详见 Model 与 Field
  3. 声明命令 —— 每个 model 一个 config/commands/<命令>.json,状态流转用 type: state_transition + fromStates/toState/stateField,并在 permissions 绑权限码。详见 Command
  4. 配 binding 与 bindingRules —— config/bindings/(model-field 绑定)和独立的 config/bindingRules.json(命令副作用,如本插件的 currencyConversionHandler),两者都要在 resourceDirs 注册(见「典型错误」)。
  5. 设计页面 —— config/pages/,列表/表单/详情用 Page Designer 出 DSL,detail 页用 action.command 接行动按钮。
  6. 权限、角色、字典、菜单 —— config/permissions.json / roles.json / dicts.json / menus.json
  7. 打包导入 —— 用 aura CLI 的 import-directory-sync(参数是目录 path)或平台导入接口;校验返回 success:true 才算导入成功。详见 插件清单

6. 常见配置

  • 多币种换算:在 bindingRules.jsoncurrencyConversionHandler 挂到 sl:create_sales_order 上,配 currencyField / rateField / baseCurrencyField / amountFields,下单时自动按汇率折算本位币;币种取自字典 sl_currency
  • 审批阈值自动化:用自动化规则在「订单金额低于阈值」时自动发 sl:approve_sales_order,高于阈值才转人工——同一条命令,人和规则都能调。
  • 价格表与折扣:sl_price_list_common 按生效日期 + 币种 + 优先级管理价格;sl_discount_rule_commonsl_discount_typepercentage / fixed_amount / tiered;两者都走 draft → active ↔ inactive → archived 生命周期。
  • RMA → 信用凭证闭环:RMA 走完 inspect → decide_disposition(处置选 repair/replace/refund/scrap),退款类用 sl:create_credit_memosl:approve_credit_memosl:apply_credit_memo 冲抵应收。
  • 从投诉开 RMA:sl:create_rma_from_complaint 建一张 RMA 草稿;它的自动预填(从投诉单带客户/订单)需要一个插件后端 handler,纯配置版只建草稿。

7. 典型错误

  • 命令码用点号:写成 sales.order.confirmsales.rma.approve 跑不通——真实是冒号 + 动词_名词:sl:submit_sales_ordersl:approve_sales_order。RMA 也没有 approve 命令,授权后第一步是 sl:receive_rma
  • 绕过命令直接改订单表:直接 UPDATE sl_sales_order_common 会跳过状态机、has_children 校验、权限和审计事件,导致流程错乱。一切状态变更都走命令
  • 提交时没有明细行:sl:submit_sales_orderhas_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_commonsl_rma_common…),写成 sl_sales_order 会找不到 model。也别去找报价单(quotation)——本插件不含报价模型,流程从「销售订单」起。

下一步

  • CRM 用例 —— 线索与商机如何在上游流转、赢单后产生销售订单
  • 系统总览 —— 插件、命令与运行时如何拼到一起
  • 命令管道 —— 上面每条命令都走的执行契约
  • 权限 —— 上面角色背后的五层模型
  • 插件清单 —— Sales 与 CRM / Inventory / Finance 之间的依赖解析机制