# AI 报销单自动填单 — 工作流编排

> 本文件是报销自动填单的完整工作流参考。
> 发票、事前申请、预填信息等数据准备走本技能的 `weaver-work-cli --json invoice run <operation>`；**报销单的创建与修改统一走 `weaver-e10-workflow` 技能**（`workflow.createForm` / `workflow.createRequest` / `workflow.edit*`），命令契约与返回处置见 [`../../weaver-e10-workflow/references/workflow-create.md`](../../weaver-e10-workflow/references/workflow-create.md) 与 [`workflow-edit.md`](../../weaver-e10-workflow/references/workflow-edit.md)。
> 所有调用都必须通过 CLI 执行，不能直接 curl E10 接口。

---

## ⚠️⚠️⚠️ 最高优先级规则：严禁篡改任何原始数据值 ⚠️⚠️⚠️

**所有从 CLI 返回、用户提供、或上下文已有的数据值，都必须逐字符原样使用，严禁做任何修改。**
**这包括但不限于：ID、日期、金额、名称等一切字段值。**

已知反复出现的错误：

| 类型 | 正确值 | 错误值（被AI篡改） | 错误原因 |
|------|--------|-------------------|---------|
| ID | `100003720000076271` | `100003720000000076271` | 中间被插入了 `000` |
| ID | `100003720000077896` | `100003720000000077896` | 中间被插入了 `000` |
| 日期 | `2026-07-19` | `2026-00000000007-19` | 月份被篡改，插入了多余的 `0` |
| 金额 | `108.2` | 不得修改 | — |

**处理规则：**
1. 从 CLI 返回的 JSON 中获取的值 → **逐字符原样复制**，一个字符都不能改
2. 用户提供的值 → 原样使用
3. **绝不做"对齐位数"、"补零"、"格式化"、"规范化"等任何处理**
4. 日期值从 CLI 获取后直接使用，不要尝试拆分再拼接
5. 如果不确定某个值是否正确，回溯到原始数据源重新复制

---

## 报销共享常量（所有 reference 统一引用本节）

以下常量在报销 reference 中多处出现，**统一以本节为准**，其余文件引用本节不再重复定义。浏览按钮类字段一律写成 `名称(ID)`。

| 常量 | 值 | 说明 |
|------|-----|------|
| 默认合同编号 | `"20260999"` | 用户当次未给出别的合同号时使用；表单默认值已是它时不必再传。用户给了别的合同号 → 承担主体 / 相关客户 / 相关项目以合同联动为准，见 [reim-form-fill-rules.md](reim-form-fill-rules.md#用户给了合同号以合同联动为准) |
| 非出差相关客户 | `"泛微上海(100504500000311504)"` | 出差流程必须由用户提供具体客户 |
| 兜底费用科目 | `"其他-管理费用(100502270000000104)"` | 所有科目匹配策略均未命中时使用 |
| 人民币币种 | `"人民币(100001050000000001)"` | 明细每行必传（即使表单默认带出），否则字段联动失效，见 [reim-form-fill-rules.md](reim-form-fill-rules.md) |
| 报销方式默认值 | `"银行（个人报销）"` | Select 传选项名；表单默认带出时不传 |
| 全额红冲状态值 | `status = 8` | `comm_info.pro.status`；此类发票必须过滤，不允许报销 |
| 相关凭证 typeCode | 17, 18, 57, 58 | 不能独立成行，必须与发票配对 |
| 直接上级取值 | `directSuperior` | ⚠️ **不是 `allSuperiors[0]`**（后者是上级链最高层） |
| 总部费用报销 | `总部费用报销(100003460000000060)` | 费用明细 `subFormId` = `100003690000000011`（字段清单以 `createForm` 实际返回为准） |

---

## 时间戳内联计算（零工具调用）

> **所有日期 → 秒级时间戳的转换由 AI 直接计算，无需调用外部脚本。**

### 计算公式（UTC+8 时区）

```
日期 → 秒级时间戳：
  起始时间 = Date("YYYY-MM-DD 00:00:00 +08:00") 的 Unix 秒数
  结束时间 = Date("YYYY-MM-DD 23:59:59 +08:00") 的 Unix 秒数

默认查询（最近一周）：
  begin = 今天00:00:00秒时间戳 - 7 * 86400
  end   = 今天23:59:59秒时间戳

扩大到一个月（fallback）：
  begin = 今天00:00:00秒时间戳 - 30 * 86400
  end   = 今天23:59:59秒时间戳

用户指定日期范围：
  begin = 指定起始日 00:00:00 秒时间戳
  end   = 指定结束日 23:59:59 秒时间戳
```

### 发票查询默认入参

CLI 已默认 `sreim="3"`、`bill_type=0`、`page_size=10`。报销查票时只传日期范围：

```json
{
  "page_size": 20,
  "start_pos": 0,
  "date_begin": "{begin}",
  "date_end": "{end}"
}
```

可选筛选参数（按用户意图添加）：
- `"content"`: 消费内容关键词（如 `"滴滴"`、`"住宿"`）
- `"number"`: 发票号码精确匹配
- `"types"`: 发票类型 ID 数组

> 按发票号码查询时不设 `date_begin` / `date_end`。
> 不要传 `flag`、`fylxs`、`consumption_tm_begin`、`consumption_tm_end`（CLI 不接受这些字段）。

---

## 核心行为准则（必须遵守）

### 1. 立即获取数据，展示后确认

当用户表达报销意图时（如"帮我报销"、"我要报销"、"报销一下"等），**必须立即调用 `invoice.list`（或 `invoice.reim.candidates.list`）获取发票数据，展示发票汇总信息后询问用户确认，确认后再自动执行后续填单流程**。禁止反问用户要求提供发票信息、事前申请信息、报销用途等。

**禁止的行为：**

- 问用户"请提供发票信息"
- 问用户"是否有事前申请"
- 问用户"报销用途是什么"
- 列出一堆需要用户提供的信息清单
- 任何形式的"请先提供XX"
- **输出内部推理过程**（如"我需要引导确认"、"我现在要调用 XX CLI"、"我需要先获取XX数据"），直接对用户说话

**正确的行为：**

- 按内联公式计算时间戳参数，调用 `invoice.list` 获取发票数据
- 最近一周无结果 → 自动用一个月参数再查
- 直接调用 `invoice.reim.requests.list` 获取用户的事前申请
- **获取到发票后，先展示发票汇总信息（张数、基础信息），明确告知用户查询范围，询问用户确认是否报销**
- 用户确认后，自动执行匹配 → 载入报销表单 → 组装 `data` → 展示预览、用户确认 → `workflow.createRequest` 生成报销单

### 2. 根据用户意图智能构建查询条件

#### 报销填单意图

| 用户说的话 | AI 应该做的 |
|------------|-------------|
| "帮我报销" / "我要报销" | 查**最近一周**发票 → 无结果则查**最近一个月** → **展示发票汇总，询问确认** → 确认后走后续流程 |
| "报销打车费" | 设置 `content="滴滴"` 筛选交通类 → **展示发票汇总，询问确认** → 确认后走后续流程 |
| "报销上周的发票" | 设置时间范围为上周 → **展示发票汇总，询问确认** → 确认后走后续流程 |
| "报销发票号码 xxx" | 设置 `number="xxx"` 精确匹配 → **展示发票汇总，询问确认** → 确认后走后续流程 |
| 用户直接提供了发票数据 | **跳过 Step 1A** → **展示发票汇总，询问确认** → 确认后继续后续流程 |
| 用户提供了 fileId | 先调用 `invoice.reim.file-ocr.preview`（Step 0）→ **展示发票汇总，询问确认** → 确认后继续 |

#### 纯查询意图

> **纯查询操作**（查发票、查流程、查申请等）不走本工作流，直接用对应的查询 operation。

#### 查询结果上下文复用（重要）

如果上下文中已有查询过的数据，报销时**直接复用**：

- 已有事前申请 → 用 `createTime` 筛选发票
- 已有发票 → 用 `invoiceDate` 筛选事前申请
- 两者都有 → 直接跳过 Step 1A/1B，进入 Step 2 匹配

### 3. 用户操作场景全覆盖

#### 直接报销

| 用户输入 | 起始步骤 |
|----------|---------|
| "帮我报销" | 1A → **1C（发票确认）→ 用户确认** → 1B → 2 → 2.5 → 3 → 4 → 5 |
| "报销打车费" / "报销上周的" | 1A（加筛选条件）→ **1C → 用户确认** → 后续同上 |
| 提供 fileId | Step 0 → **1C → 用户确认** → 1B → 后续同上 |
| 直接给发票数据 | **1C → 用户确认** → 1B → 后续同上 |

#### 模糊意图引导

| 用户输入特征 | 处理方式 |
|-------------|---------|
| 含"报销"关键词 | 直接执行报销流程 |
| 含"出差"/"采购"/"买了"等间接关键词，不含"报销" | 直接问用户"需要我帮您创建报销单吗？" |
| 用户确认 | 进入全自动报销流程 |
| 用户否认 | 正常回应，不执行报销 |

> **⚠️ 禁止输出内部推理过程**。直接对用户说话，不要解释自己在做什么。

#### 报销后修正

用户补充缺失字段、修改字段值 → 通过 `workflow.editLoad` → `workflow.editPrepare` → 用户确认 → `workflow.editApply` 完成（Step 6）。

### 4. 自动化决策原则

- **用户已提供发票数据 → 跳过 Step 1A，直接用已有数据继续**
- 发票列表有数据 → **先展示发票汇总信息（Step 1C），告知用户查询范围，等用户确认后再往下走**
- 最近一周无结果 → **自动 fallback 到最近一个月再查一次，不需要问用户**
- 事前申请有数据 → 自动匹配，不需要用户确认"是否关联"
- 事前申请为空 → 跳过匹配，直接用发票数据填单，不需要问用户
- 匹配到唯一结果 → 直接使用，不需要用户确认
- **只有在必须用户做选择时才暂停**（如：多候选需要选一个、发票列表为空无法继续、**出差流程必须填写相关客户**、创建前确认报销单内容）
- **⚠️ 严禁篡改任何原始数据值**

---

## 整体工作流程

```
前置: CLI 认证（由 weaver-e10-shared 处理）
         ↓
Step 0 (可选): invoice.reim.file-ocr.preview → 识别发票
         ↓
Step 1A: invoice.list / invoice.reim.candidates.list (可跳过) ←→ Step 1B: invoice.reim.requests.list
         ↓
Step 1C: 展示发票汇总 → 用户确认是否报销
         ↓ (用户确认)
Step 2: 发票 ↔ 事前申请匹配绑定 (reim-matching-rules.md)
         ↓
Step 2.5: invoice.reim.workflow.resolve → 获取报销工作流 ID 与名称
         ↓
Step 2.6: 按工作流分组（多个工作流 → 多组，各组独立执行后续步骤）
         ↓  ↓  ↓ (每组)
  ┌──────────────────────────────────────────────┐
  │ ▼ 并行组 A（同时发起）                          │
  │ Step 3:   workflow.createForm（载入报销表单）     │
  │ Step 3.5: invoice.reim.row-info-batch          │
  │ Step 3.6: invoice.browse-field.data（发票对象）   │
  │ 交通类额外: invoice.get × M                     │
  └──────────────────────────────────────────────┘
         ↓ (全部返回后)
Step 4: 组装 createRequest 的 data (reim-form-fill-rules.md)
         ↓
Step 4.5: 出差流程 → 提醒用户填写相关客户（未提供则不允许报销）
Step 4.6: 话费报销 → 核对材料并按标准核算实报金额
         ↓
Step 5: 展示预览 → 用户确认（存草稿 / 提交审批）→ workflow.createRequest → 原样展示 finalMarkdown
         ↓
Step 6 (可选): workflow.editLoad / editPrepare / editApply → 用户修正
```

### ⚡ 并行调用策略（必须遵守）

| 并行组 | 可并行的操作 | 说明 |
|--------|------------|------|
| **Step 1 并行** | `invoice.reim.requests.list` 与 Step 1C 发票展示 | 用户确认期间预取事前申请 |
| **Step 3 并行** | `workflow.createForm` + `invoice.reim.row-info-batch` + `invoice.browse-field.data` + `invoice.get` × M | 全部互相独立，**必须同时发起** |
| **交通类行程** | 多张交通发票的 `invoice.get` | 各发票独立，强制并行 |

> **`createForm` 不跨组复用**：它返回的 `continuation` 与本次单据绑定，**每个报销单各调一次 `createForm`**，不能拿上一单的 `continuation` 再发起。

---

## Step 0（可选）: fileId → 发票识别

当用户提供了 `fileId`（文件/附件ID）时，调用 `invoice.reim.file-ocr.preview` 一步完成识别。

| 识别结果 | 处理方式 |
|----------|----------|
| 全部成功 | **跳过 Step 1A**，用识别结果继续 |
| 部分成功 | 用成功的继续，提示失败项 |
| **全部失败** | **暂停询问用户**，确认后才继续 |

> CLI 详情见 [reim-api-reference.md](reim-api-reference.md#0-文件ID转发票识别)

---

## Step 1A: 获取发票数据

> **如果用户已提供发票数据或已通过 Step 0 获取，则跳过此步骤。**

两种方式任选其一：

| 方式 | 适用 | 说明 |
| --- | --- | --- |
| `invoice.reim.candidates.list` | 按日期范围批量报销（推荐） | 自动翻页、自动过滤红冲票，并把发票分成 `available`（可报）/ `assigned`（已挂单）两组；**只从 `available` 里选**。见 [reim-api-reference.md](reim-api-reference.md#1b-报销候选发票分类) |
| `invoice.list` | 按关键词 / 号码 / 类型筛选 | 需自己过滤红冲票 |

### 默认查询策略（用户未指定日期时）

1. 按内联公式计算最近一周时间戳 → 调用
2. 有结果 → Step 1C 展示汇总，告知"查询的是最近一周"
3. 无有效发票 → 计算最近一个月时间戳 → 再次调用
4. 有结果 → Step 1C 展示汇总，告知"最近一周没找到，已扩大到一个月"
5. 仍无结果 → 告知用户未找到发票，建议指定日期范围

### 日期筛选参数

| 参数 | 用途 | 使用时机 |
|------|------|----------|
| `date_begin` / `date_end` | 开票日期范围 | 默认使用 |
| `create_tm_begin` / `create_tm_end` | 录入/创建日期范围 | 用户明确说"录入日期"/"创建日期"时 |

**⚠️ 数据过滤（红冲票排除）：**
- `candidates.list` **自动过滤** `status=8` 的全额红冲发票（过滤张数在 `summary.filteredRedReversedCount` 中报告）。
- 使用 `invoice.list` 时，**必须在进入任何后续流程前先过滤掉 `info.comm_info.pro.status == 8` 的发票**，并向用户报告："已自动排除 N 张全额红冲发票"。
- 全额红冲发票不允许报销，**不能让红冲票进入匹配/组装环节**。

关键返回字段映射见 [reim-api-reference.md](reim-api-reference.md#1-获取发票数据)。

---

## Step 1C: 展示发票汇总，等待用户确认

获取到发票数据后，**必须先展示发票汇总信息，询问用户确认后才继续后续流程**。

### 展示格式

展示时必须包含：**查询范围说明** + **发票汇总表格** + **确认提示**。

| 场景 | 开头说明 |
|------|----------|
| 默认查一周有结果 | "已为您查询最近一周（{起始} ~ {结束}）..." |
| 一周无结果、一个月有结果 | "最近一周未找到，已扩大到最近一个月（{起始} ~ {结束}）..." |
| 一个月也无结果 | "均未找到未报销发票。您可以指定日期范围或检查是否已录入。"（结束） |
| 用户指定日期范围 | "共找到 N 张（{用户指定的范围描述}）..." |

表格列：`# | 发票类型 | 开票日期 | 发票号码 | 金额 | 销方名称 | 费用类型`

尾部：「请确认是否使用以上发票进行报销？如需查询其他日期范围，请告诉我。」

### 用户响应处理

| 用户响应 | 处理方式 |
|----------|----------|
| 确认 | 继续 Step 1B → Step 2 → 后续流程 |
| 选择部分发票（"只报第1、3张"） | 按用户指定筛选发票，继续后续流程 |
| 否认 | 结束报销流程 |
| 补充条件 | 调整查询条件重新查询，再次展示确认 |

---

## Step 1B: 获取事前申请流程

通过 `invoice.reim.requests.list` 获取。

默认入参：`{"pageNo": 1, "pageSize": 20}`

### Step 1 执行顺序与预筛选策略

> **核心时间关系：事前申请日期一定在开票日期之前。**

| 场景 | 筛选方式 |
|------|----------|
| 先有发票 → 筛选事前申请 | `cusCreateDateEnd` = 最晚 invoiceDate，`cusCreateDateStart` = 最早 invoiceDate - 90天 |
| 先有发票 → **话费报销** | `cusCreateDateEnd` = 最晚 invoiceDate，`cusCreateDateStart` = 最早 invoiceDate - **1095天（3年）**，且加 `requestname="话费"` 缩小范围（见 [reim-phone-subsidy-rules.md](reim-phone-subsidy-rules.md)） |
| 先有事前申请 → 筛选发票 | `date_begin` = 最早 createTime 秒时间戳 |
| 并行获取 | 不做预筛选，Step 2 匹配时统一过滤 |

---

## Step 2: 发票与事前申请匹配绑定

> **详细匹配规则见 [reim-matching-rules.md](reim-matching-rules.md)**

匹配完成后产出绑定关系数据结构：

```json
[
  {
    "invoiceId": "INV001",
    "preApprovalId": "1298294241967177729",
    "preApprovalName": "交通费报销-后端开发111-2026-07-25",
    "preApprovalWorkflowId": "1181057526276915214",
    "matchConfidence": "high",
    "matchReasons": ["时间范围匹配", "费用类型一致"]
  }
]
```

> `preApprovalName` 要保留：填「事前申请流程 / 相关流程」字段时写成 `{preApprovalName}({preApprovalId})`。

### Step 2 匹配结果展示（推荐）

匹配完成后，**建议向用户展示匹配结果**，而不是静默进入下一步。展示格式：

```markdown
发票与事前申请匹配结果：

| # | 发票号码 | 金额 | 匹配到的事前申请 | 置信度 | 匹配原因 |
|---|----------|------|------------------|--------|----------|
| 1 | 36901189 | ¥108.20 | 交通费报销-后端开发111-2026-07-25 | ✅ 高 | 费用类型匹配、时间匹配 |
| 2 | 36901201 | ¥380.00 | （未匹配） | — | 无匹配的事前申请 |

确认后将自动进入后续填单流程。如需调整匹配关系，请告诉我。
```

- 匹配到 high 置信度的直接标记 ✅ 高
- medium 置信度标记 ⚠️ 中（建议核实）
- 未匹配的标记 —，说明将跳过事前申请关联
- **只有全部都是 high 且用户已在 Step 1C 确认过 → 可以静默跳过本展示直接继续**
- medium 或 low 出现时**必须展示并等用户确认**

---

## Step 2.5: 获取报销单工作流

通过 `invoice.reim.workflow.resolve` 获取报销工作流的 `workFlowId` 与 `workFlowName`（Step 3 用 `{workFlowName}({workFlowId})` 载入表单）。

**⚠️ 核心逻辑：当存在多个工作流时，需要将发票/事前申请按工作流分组，每组各创建一个报销单。**

| 场景 | 处理方式 |
|------|----------|
| 有事前申请 → 同一 workFlowId | 创建 1 个报销单 |
| 有事前申请 → 不同 workFlowId | 按 workFlowId 分组，每组各创建一个报销单 |
| 无事前申请 → 候选工作流只有 1 个 | 创建 1 个报销单 |
| 无事前申请 → 候选工作流有多个 | 发票按匹配规则分组，每组各创建一个报销单 |

### 发票 → 工作流匹配规则（按优先级）

1. **事前申请绑定**：发票已匹配到事前申请 → 使用该事前申请对应的 workFlowId
2. **话费专项**：通讯/话费类发票优先匹配 `workFlowName` 含「话费补贴标准审批流程」或「话费报销」的工作流，命中后必须按 [reim-phone-subsidy-rules.md](reim-phone-subsidy-rules.md) 核算
3. **发票费用类型匹配**：用 `expenseType` 与候选 `workFlowName` 做关键词匹配
4. **通用兜底**：归入 workFlowName 含"日常"/"通用"/"报销"的；仍无法区分归入第一个

每组独立执行 Step 3 → 3.5 → 3.6 → 4 → 5，不能省略任何字段。

---

## Step 3: 载入报销表单（`workflow.createForm`）

调用 `weaver-e10-workflow` 的 `workflow.createForm`，`workflow` 传 Step 2.5 得到的 `{workFlowName}({workFlowId})`（带 ID 时 CLI 不查可发起列表，直接载入）：

macOS/Linux（bash/zsh）：

```bash
weaver-work-cli --json workflow run workflow.createForm --input-json '{"workflow":["总部费用报销(100003460000000060)"]}'
```

Windows PowerShell：

```powershell
weaver-work-cli --json workflow run workflow.createForm --input-json '{"workflow":["总部费用报销(100003460000000060)"]}'
```

从返回里取：

| 返回键 | 用途 |
| --- | --- |
| `continuation`（= `contextId`） | Step 5 `createRequest` 的 `context` |
| `fields` | **可写字段全集**：按 `name` 找业务字段取 `id`；带 `subFormId` 的是明细字段；带 `initialValue` 的有默认值 |
| `requestName` | 表单默认标题（报销单标题按 [reim-form-fill-rules.md](reim-form-fill-rules.md#流程标题requestname) 生成后用 `requestName` 参数覆盖） |
| `historyRequestData` | 只用于本技能规则没覆盖的字段（如费用承担公司），口径见 `workflow-create.md`「历史流程数据参考规则」 |

- `NEEDS_CLARIFICATION`（`workflow_not_exist` / `no_create_right`）→ 按 `workflow-create.md` 如实告知用户，停止本组。
- 大表单超线（stdout 只有一句话 + `resultFile`）→ 按 `workflow-create.md` 读一次 `resultFile`，不要重跑。
- **不再需要** `invoice.reim.form.structure`、`invoice.reim.field.search`：字段清单由 `createForm` 给出，浏览按钮的名称 → ID 由 `createRequest` 解析。

---

## Step 3.5: 获取发票预填信息（`invoice.reim.row-info-batch`）

- 入参：`invoiceIds`=本组全部**发票**（不含凭证）ID，`requestid`=匹配到的事前申请 ID（无则留空）
- 返回的 `date`、`fysm`、`itinerary`、`subjectId`、`subjectName` **在 Step 4 中优先使用**
- 某张失败或字段为空 → 该行回退到 AI 推断逻辑

### 交通类发票额外处理

交通类发票（`expenseType` 含交通/出行/打车，或 `consumeContent` 含客运服务费/运输服务，或 `sellerName` 含滴滴/享道等）→ **额外并行调用 `invoice.get`** 获取出发地和目的地。

> 详细精简规则见 [reim-form-fill-rules.md](reim-form-fill-rules.md#地名精简规则)

---

## Step 3.6: 取发票对象（`invoice.browse-field.data`）

发票字段（`EinvoiceComponent`）只接受发票对象，不接受文本或裸 ID。把本组所有**发票** + 已确定的**配对凭证**的 `fid` 一次传入：

macOS/Linux（bash/zsh）：

```bash
weaver-work-cli --json invoice run invoice.browse-field.data --input-json '{"fids":["1295366637717012482","1298798990831518001"]}'
```

Windows PowerShell：

```powershell
weaver-work-cli --json invoice run invoice.browse-field.data --input-json '{"fids":["1295366637717012482","1298798990831518001"]}'
```

> `row-info` 的 `itinerary` 要等 Step 3.5 返回后才知道。为了不串行，Step 3 并行组里先只传发票 + AI 已配对的凭证；`itinerary` 返回了新的凭证 ID 时再补调一次，只传新增的 `fids`。

> 发票都来自用户已确认的票夹，属于 `workflow-create.md` 里"指名从票夹取"的情形，**不需要**上传提醒。

---

## Step 3.7（兜底）: 当前人员及上级（`invoice.reim.employee-superiors`）

承担主体、报销人上级等人员字段通常由表单默认值带出，**默认不调用**。只有以下情况才调用一次（同一会话可复用）：

- `fields` 里承担主体 / 申请人 / 报销人上级等字段**没有 `initialValue`**；或
- `createRequest` 的 `missingFields` 列出了这些字段。

取值：当前人员 → `{currentEmployee.name}({currentEmployee.id})`；直接上级 → `{directSuperior.name}({directSuperior.id})`（**不是 `allSuperiors[0]`**）；所有上级 → `allSuperiors` 每项的 `名称(ID)`。

---

## Step 4: 组装 `createRequest` 的 `data`

> **详细填值规则见 [reim-form-fill-rules.md](reim-form-fill-rules.md)**
> **费用科目匹配规则见 [reim-expense-subject-rules.md](reim-expense-subject-rules.md)**

```json
{
  "main": {"<主表字段id>": "<值>"},
  "details": {"<费用明细subFormId>": [{"<明细字段id>": "<值>"}]}
}
```

**关键约束：**
- 键只取 `createForm` 的 `fields[].id`；`fields` 里没有的字段不传，带默认值且不需改写的字段不传（例外：明细「币种」每行必传）
- 浏览按钮写 `名称(ID)`；Select 写选项名；发票字段写 `[发票对象]`
- 一张发票一行；相关凭证（17/18/57/58）放进配对发票那一行，每行最多 1 张
- **费用类型（科目）每行必须有值**，匹配不到填 `"其他-管理费用(100502270000000104)"`
- **合同号固定 `"20260999"`**（除用户当次明确给出别的合同号）；表单默认值已是它时不必传
- **用户给了合同号 → 承担主体、相关客户、相关项目都不传，以合同联动为准**（用户口头指定的承担主体也不传）；创建后从 `linkageResult` 读出实际带出的值告诉用户
- **⚠️ 出差流程相关客户规则见 Step 4.5；话费报销规则见 Step 4.6。**

---

## Step 4.5: 出差流程必须确认相关客户

> **出差报销未拿到用户提供的具体相关客户前，禁止进入 Step 5。**
> 完整规则见 [reim-form-fill-rules.md - 出差流程相关客户](reim-form-fill-rules.md#出差流程相关客户硬拦截)。

简要规则：
- 用户给了合同号 → 相关客户由合同联动带出，不拦截
- `workFlowName` 或 `requestname` 含「差旅」「出差」→ 暂停，提醒用户提供具体客户名称
- 用户给出客户名称 → 原样填进「相关客户」，由 `createRequest` 解析；多候选/查不到按 `workflow-create.md` 的浏览按钮口径处理
- 用户拒绝 / 不提供 → **结束报销，禁止调用 `workflow.createRequest`**
- 非出差流程：相关客户固定填 `"泛微上海(100504500000311504)"`

---

## Step 4.6: 话费补贴 / 话费报销必须按月度标准核算

> **命中「话费补贴标准审批流程」或「话费报销」时，未齐备账单材料和岗位标准前，禁止进入 Step 5。**
> 完整规则见 [reim-phone-subsidy-rules.md](reim-phone-subsidy-rules.md)。

简要规则：
- `workFlowName` 或事前申请名称含「话费补贴标准审批流程」「话费报销」，或用户明确说话费报销 → 先暂停
- 必须提供本人实名制当月手机账单截图及账单构成
- 必须确认岗位对应月度上限（350 / 200 / 100），禁止猜测岗位
- 剔除手机设备赠送套餐、家庭宽带套餐后，实报金额 = `min(可报销账单金额, 上限)`
- 材料不齐 / 岗位未确认 / 用户拒绝 → **结束报销，禁止调用 `workflow.createRequest`**

---

## Step 5: 预览确认 → `workflow.createRequest`

> **⚠️ 报销单涉及金额，创建前必须先给用户看预览并取得明确确认**——这是本技能对 `workflow-create.md`「拼完 data 立即发 createRequest」的唯一加严，其余返回处置照 `workflow-create.md` 执行。
> **⚠️ 出差流程拦截：相关客户未由用户提供具体信息前，禁止调用。**
> **⚠️ 话费报销拦截：账单截图、账单构成或岗位标准未确认前，禁止调用。**

### 5.1 预览（创建前）

向用户展示一次，**字典类值一律带中文名 `名称(ID)`**（`data` 里本来就是这种写法，直接照抄）：

```markdown
即将创建报销单「交通费报销-龚为明-2026-10-21（共2笔）」，请确认：

| 项目 | 值 |
| --- | --- |
| 报销工作流 | 总部费用报销 |
| 报销事由 | 2026-07-30 市内交通 - 滴滴出行 |
| 相关客户 | 泛微上海(100504500000311504) |
| 事前申请 | 交通费报销-后端开发111-2026-07-25(1298294241967177729) |
| 合计金额 | ¥185.80 |

| # | 发票号码 | 金额 | 费用日期 | 费用类型 | 费用说明 | 相关凭证 |
|---|----------|------|----------|----------|----------|----------|
| 1 | 36901189 | ¥108.20 | 2026-07-30 | 市内交通费(100502270000000006) | 仁济医院南院-浦江瑞和城 | ✅ 滴滴行程单 |
| 2 | 36901200 | ¥77.60 | 2026-07-30 | 市内交通费(100502270000000006) | 虹桥机场T2-人民广场 | — |

申请人、承担主体、上级、部门、合同号、报销方式由表单按当前登录人默认带出；币种为人民币。
请回复「存草稿」或「提交审批」；需要修改请直接告诉我。
```

- 有未挂上的凭证、未配对的凭证、话费核算明细时，在表格下方逐条列出
- 用户没说清 → 默认按「存草稿」（`action:"draft"`）处理并在回复里说明

### 5.2 创建

macOS/Linux（bash/zsh）：

```bash
weaver-work-cli --json workflow run workflow.createRequest --input ./reim-create-request.json
```

Windows PowerShell：

```powershell
weaver-work-cli --json workflow run workflow.createRequest --input .\reim-create-request.json
```

`reim-create-request.json` 的内容：

```json
{
  "context": "<Step 3 createForm 返回的 continuation>",
  "requestName": "交通费报销-龚为明-2026-10-21（共2笔）",
  "action": "draft",
  "data": {
    "main": {"100003720000000088": "2026-07-30 市内交通 - 滴滴出行", "100003720000000687": "泛微上海(100504500000311504)"},
    "details": {"100003690000000011": [{"100003720000016373": "108.2", "100003720000052200": "人民币(100001050000000001)"}]}
  }
}
```

> `data` 含发票对象，体积较大，建议写成 UTF-8 文件用 `--input <path>` 传入。

### 5.3 返回处置

**完全按 `workflow-create.md`「createRequest 返回后的处置」执行**：

| 返回 | 处置 |
| --- | --- |
| `CREATED` / `CREATED_SUBMITTED` / `CREATED_SUBMIT_FAILED` | **原样粘贴 `finalMarkdown`**，一字不改；链接只出现在回复文本里 |
| `NEEDS_INPUT` | 缺的是承担主体、上级、合同号、相关客户、科目等本技能有规则的字段 → 按 [reim-form-fill-rules.md](reim-form-fill-rules.md) 补值后用同一 `context` 重发；其余原样粘贴 `finalMarkdown` 一次问齐用户 |
| `NEEDS_CONFIRMATION` | 按 `ambiguities` 处理（浏览按钮多候选让用户选；`MULTIPLICITY` 只保留 1 个值） |
| `partial` / `write_uncertain` | 停止，不重放；只读回查一次后告知用户 |

> **⚠️ 禁止重复创建**：同一组发票 `createRequest` 成功后，**不要再次 `createForm` + `createRequest`**；修改一律走 Step 6。

---

## Step 6（可选）: 修改已创建的报销单

通过 `weaver-e10-workflow` 的 `workflow.editLoad`（传 Step 5 返回的 `requestId`）→ `workflow.editPrepare`（`data` 写法同 Step 4，只传要改的字段）→ 用户确认 → `workflow.editApply`。详见 [`workflow-edit.md`](../../weaver-e10-workflow/references/workflow-edit.md)。

- 流程已流转到不可编辑节点时 `editApply` 返回"失去操作权限"，如实转述
- 只读字段会被拒绝（`notEditable`），如实转述，不要换字段 id 绕过

---

## 异常处理

**需暂停询问用户的情况：**
- 发票列表为空 → 提示"未找到未报销的发票"，结束
- 出差/差旅流程缺少相关客户 → 见 Step 4.5
- 话费报销缺少账单截图、账单构成或岗位标准 → 见 Step 4.6
- `createRequest` 返回 `NEEDS_INPUT` / `NEEDS_CONFIRMATION` 且本技能规则补不上 → 按 `workflow-create.md` 问用户

**严禁重复调用：**
- **`createRequest` 已成功 → 严禁再次创建，后续修改一律使用 Step 6**

**自动继续（不问用户）：**
- 事前申请为空 / 匹配失败 → 跳过事前关联，直接用发票填单
- 匹配结果唯一 → 直接使用

**用户中途取消：**
- 用户说"取消"/"不报了"/"算了" → 立即停止
- 已创建成功 → 告知用户可通过链接手动删除

---

## 参考文档

- [reim-api-reference.md](reim-api-reference.md) - 报销数据准备 CLI operation 入参与返回值
- [reim-matching-rules.md](reim-matching-rules.md) - 发票与事前申请匹配逻辑
- [reim-form-fill-rules.md](reim-form-fill-rules.md) - `createRequest` 的 `data` 填值规则
- [reim-expense-subject-rules.md](reim-expense-subject-rules.md) - 费用科目自动匹配规则
- [reim-phone-subsidy-rules.md](reim-phone-subsidy-rules.md) - 话费补贴 / 话费报销月度标准与实报金额
- [reim-examples.md](reim-examples.md) - 完整端到端示例
- [`workflow-create.md`](../../weaver-e10-workflow/references/workflow-create.md) / [`workflow-edit.md`](../../weaver-e10-workflow/references/workflow-edit.md) - 发起与修改流程的命令契约
