# 课题经费拨付对接财务报销 - 前端联调方案 > 涉及模块:`yudao-module-research`(课题管理)↔ `yudao-module-finance`(财务报销),跨模块通过新建的 `yudao-module-finance-api` 本地 Bean 调用。 > 后端接口前缀:`/research/fund-record` > 附件关联:课题侧 `recordType = research_fund_record`,财务侧 `recordType = finance_reimbursement`,`application = file`(同一批 blob 双绑,互不影响) ## 涉及页面 | 文件 | 说明 | |------|------| | `src/api/research/fund/index.ts` | `FundRecord` 类型补 4 个字段;`createFundRecord` / `updateFundRecord` 注释更新 | | `src/views/research/application/data.ts` | `FundStatus` 新增 `APPROVING: 15`、`REJECTED: 30`;`useFundColumns` 新增「财务报销单号」「驳回原因」两列 | | `src/views/research/application/modules/fund-form.vue` | 移除「拨付状态」下拉(状态改为系统驱动);「拨付凭证」改必填;编辑已驳回记录时展示驳回原因 | | `src/views/research/application/modules/fund-panel.vue` | 编辑/删除按钮改为仅「待拨付」可见;「已驳回」行新增「重新发起报销」按钮;按状态补顶部提示 | | `src/views/research/application/detail/approve/index.vue` | **无需改**(内嵌 `fund-panel`,字典驱动展示自动兼容新状态) | 前端 `finance` 模块页面**无需改动**:报销单由后端自动创建并提交,财务人员在既有的「财务报销 → 报销单」列表里正常审批即可。 **字典已由 SQL 脚本补齐**,前端继续用 `getDictOptions(DICT_TYPE.RESEARCH_FUND_STATUS)` / `CellDict` 取值,无需新建枚举: | 字典类型 | 名称 | 取值 | |----------|------|------| | `research_fund_status` | 经费拨付状态 | 0 待拨付 / 15 报销审批中 / 20 已拨付 / 30 已驳回 | > 字典项 id 段:`8100401` 待拨付 / `8100403` 报销审批中 / `8100402` 已拨付 / `8100404` 已驳回。 **按钮权限标识**(无变化,沿用一期): `research:fund:create`、`research:fund:update`、`research:fund:delete`、`research:fund:query` ## 业务流程与数据带入 ``` 课题详情 → 经费拨付面板 │ 登记拨款(拨付批次、拨付金额、拨付日期、收款单位、备注、拨付凭证) │ ← 凭证必填:先上传拿 blobId,再把全部 blobId 放进本次请求体一起提交 ▼ create 接口(一条龙,前端只需调一次) ├── 1. 课题侧落 research_fund_record 台账(状态先置「待拨付」) ├── 2. 同一批 blob 绑定到课题台账(research_fund_record) ├── 3. 自动创建财务报销单 finance_reimbursement(1:1、金额一致) │ 标题 = 课题〔课题名〕第N期/第N次经费拨款 │ 明细固定 1 行:费用类型=其他(5)、发票金额=拨款金额、发票日期=拨付日期、费用说明=标题 ├── 4. 立即提交 BPM 审批(finance_reimbursement_approve) └── 5. 台账回写 reimbursementId / reimbursementNo,状态置「报销审批中(15)」 ▼ 财务审核 → 财务主管审批(在财务模块的报销单审批中心完成) │ ├── 通过 → 台账自动变「已拨付(20)」 ├── 驳回 → 台账自动变「已驳回(30)」,并回写驳回原因 + 驳回时间 └── 撤回/取消 → 台账退回「待拨付(0)」,可编辑后重新发起 ``` **信息带入(关键)** | 从 | 到 | 带入字段 | |----|----|----------| | 课题申报书 | 经费拨付登记弹窗 | 课题名称、立项编号、申请经费(用于余额提示)、可登记性(仅「已立项」可登记) | | 经费拨付登记 | 自动生成的财务报销单 | 标题(课题名 + 批次)、报销总金额 = 拨付金额、申请日期 = 拨付日期、备注、费用说明、申请人 = 当前登录用户 | | 经费拨付登记 | 财务报销单凭证 | 同一批 `blobIds` 双绑到课题台账与财务报销单 | | 财务报销审批结果 | 课题经费拨付台账 | 状态(20/30/0)、驳回原因、驳回时间 | ## API | 方法 | 路径 | 说明 | |------|------|------| | POST | `/research/fund-record/create` | 登记经费拨付(后端自动创建并提交财务报销审批) | | PUT | `/research/fund-record/update` | 修改并重新发起报销(仅「待拨付」「已驳回」可改) | | DELETE | `/research/fund-record/delete?id=` | 删除拨款记录(仅「待拨付」可删,联动清理财务侧草稿报销单) | | GET | `/research/fund-record/list?applicationId=` | 某课题申报书下的经费拨付记录列表 | **请求参数(create / update 共用同一个 VO):** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | update 必填 | 拨款记录编号 | | applicationId | Long | 是 | 课题申报书 ID | | batchName | String | 否 | 拨付批次,如「第一期」;留空时标题按 `第N次` 自动编号 | | amount | BigDecimal | 是 | 拨付金额(元),不得为负 | | payDate | LocalDate(`yyyy-MM-dd`) | 否 | 拨付日期;留空时后端按当天处理 | | payee | String | 否 | 收款单位/账户名 | | remark | String | 否 | 备注 | | blobIds | Long[] | **是** | 拨付凭证附件 blobId 列表;全量替换语义,清空必须传空数组 | > ⚠ **`status` 字段已从请求体中移除**:拨付状态由系统驱动(登记 → 报销审批中 → 已拨付/已驳回),前端不要再传,也不要在表单里放状态下拉。 **响应字段新增(`/list` 返回的每条记录):** | 字段 | 类型 | 说明 | |------|------|------| | reimbursementId | Long | 关联财务报销单 ID | | reimbursementNo | String | 关联财务报销单号(登记成功后回填) | | rejectReason | String | 财务报销驳回原因(BPM 回写) | | rejectTime | LocalDateTime | 财务报销驳回时间(BPM 回写) | **响应示例:** ```json { "code": 0, "data": [ { "id": 3, "applicationId": 2, "batchName": "第一期", "amount": 50000.0, "payDate": "2026-12-01", "payee": "XX大学", "status": 15, "reimbursementId": 7, "reimbursementNo": "BX20260929000007", "rejectReason": "", "rejectTime": null, "remark": "第一期拨款", "creatorName": "超级管理员", "createTime": "2026-09-29 10:00:00", "attachmentList": [], "hasAttachment": true } ] } ``` ## 字段展示规则 | 字段 | 展示位置 | 说明 | |------|----------|------| | status | 列表「拨付状态」列 | 字典 `research_fund_status` 渲染,0/15/20/30 四态 | | reimbursementNo | 列表「财务报销单号」列 | 登记成功后回填;为空显示 `-`;财务人员在财务模块按此单号查单 | | rejectReason | 列表「驳回原因」列;编辑弹窗顶部 Alert | 仅「已驳回」有值;未填时后端回写「财务未填写驳回原因」 | | rejectTime | 仅后端存储 | 列表不展示,供排查/追溯 | | hasAttachment | 列表「拨付凭证」列 | 凭证已必填,仅「已上传 / 缺失」两态 | ## 业务规则说明 | 场景 | 规则 | |------|------| | 单据关系 | 一条拨款记录 ⟷ 一张财务报销单,**1:1、金额一致**;一个批次一张单 | | 登记即提交 | 课题侧点保存后立即自动提交 BPM,**不需要**再去财务模块提交一次 | | 可登记前提 | 只有**已立项**的课题申报书才能登记经费拨付 | | 凭证必填 | 拨付凭证必填,会随自动生成的财务报销单一起送交财务审批 | | 金额上限 | 本次金额 + 该申报单已登记金额 ≤ 申请预算(`budgetAmount`) | | 修改前提 | 只有「待拨付(0)」「已驳回(30)」可修改并重新发起报销;「报销审批中」需财务端先撤回或等待结果 | | 驳回重发 | 「已驳回」记录改金额/凭证/批次后重新发起,原有金额、凭证、批次不丢失,驳回原因与时间被清空 | | 删除前提 | 只有「待拨付(0)」可删除;若关联的财务报销单已不是「草稿」,删除会被拒绝并提示联系财务确认 | | 审批回写 | 报销通过 → 台账「已拨付(20)」;驳回 → 「已驳回(30)」+ 原因/时间;撤回或取消 → 退回「待拨付(0)」 | | 无流程兜底 | 财务审批分类下没有已发布流程时,登记直接失败并提示去 BPM 创建发布流程,**不会留下孤儿草稿报销单**(后端事务回滚) | ## 注意事项 - **必须先部署财务报销审批流程**:`bpm_category` 中 `finance_reimbursement_approve` 分类已存在,但其下必须有一个**已发布**的流程定义(2 节点:财务审核 → 财务主管审批),否则登记拨款会报「请先在 BPM 创建并发布报销审批流程后重试」。 - **拨付明细由系统生成,不可编辑**:报销单的明细固定 1 行(费用类型=其他、发票金额=拨款金额、发票日期=拨付日期),前端不要在课题侧做明细编辑。 - **状态只读**:拨付状态由系统与 BPM 驱动,前端任何表单都不要再提交 `status`。 - **删除是"两清"**:删待拨付记录时会联动删除财务侧的草稿报销单;财务侧已提交/已通过的报销单必须先撤回或处理,否则删除被拒。 - **软关联无外键**:台账与报销单靠 `reimbursement_id` 关联。若财务侧**直接删除**了报销单,台账会留下悬空 ID,此时再次修改会提示「关联的财务报销单已不存在,请删除本条拨款记录后重新登记」。 - **附件为全量替换语义**:修改记录时 `blobIds` 传的是全部凭证;不传或传空数组会清空凭证(且后端会先校验必填并直接拒绝)。 - **列表页余额提示仅供参考**:可用余额 = 申请经费 − 已登记拨付合计,最终以服务端累计校验为准(超出会返回带「本次/已登记/合计/上限」四要素的精确报错)。