|
|
@@ -0,0 +1,147 @@
|
|
|
+# 报销 / 备用金申请「撤回」能力设计
|
|
|
+
|
|
|
+**日期**:2026-09-14
|
|
|
+**状态**:已确认待实现
|
|
|
+**范围**:Web 管理端报销申请 / 备用金申请 + 小程序报销 / 备用金详情
|
|
|
+
|
|
|
+## 背景
|
|
|
+
|
|
|
+【报销申请】中状态为「审批中」的单据当前不可编辑、不可再次提交。用户希望:**自己作为申请人发起的审批中单据,可自行撤回 → 回草稿 → 修改 → 再提交**。
|
|
|
+
|
|
|
+报销后端已具备撤回接口与发票额度释放逻辑;Web 申请页与小程序详情未暴露入口。备用金无对称的 withdraw API。
|
|
|
+
|
|
|
+## 决策摘要
|
|
|
+
|
|
|
+| 项 | 决策 |
|
|
|
+|----|------|
|
|
|
+| 端范围 | Web 报销申请 + Web 备用金申请 + 小程序(报销/备用金详情) |
|
|
|
+| 权限 | 仅申请人(`applicant_id`)可撤回 |
|
|
|
+| 时机 | 只要状态为 `pending`(审批中)即可,不区分已过几级审批 |
|
|
|
+| 交互 | 仅单条:Web 工具栏(选中 1 条)+ 详情;小程序主要在详情底栏 |
|
|
|
+| 撤回原因 | 不要求 |
|
|
|
+| 批量撤回 | 不做 |
|
|
|
+| 方案 | 复用现有报销 withdraw,补齐备用金后端 + 各端 UI |
|
|
|
+
|
|
|
+## 行为与状态
|
|
|
+
|
|
|
+### 可撤回条件(同时满足)
|
|
|
+
|
|
|
+1. `status === 'pending'`
|
|
|
+2. 当前登录用户 id === 单据 `applicant_id`(报销人 / 借款人)
|
|
|
+
|
|
|
+代申请场景下:填写人若不是申请人,**不能**撤回(与现有报销后端一致)。
|
|
|
+
|
|
|
+### 撤回结果
|
|
|
+
|
|
|
+1. 状态:`pending` → `draft`(待提交)
|
|
|
+2. 清空 `current_node`
|
|
|
+3. 报销:调用既有 `InvoiceLedgerService.release_reimburse`,退回发票占用额度
|
|
|
+4. 备用金:无发票额度逻辑,仅回草稿并清节点
|
|
|
+5. **保留**既有审批历史记录(便于审计;再次提交后进入新一轮)
|
|
|
+6. 不写入「撤回原因」字段
|
|
|
+
|
|
|
+### 撤回后
|
|
|
+
|
|
|
+按现有草稿逻辑:可编辑、可删除、可再次提交审批。前端沿用 `isDraftEditable`(`draft` / `rejected`)门禁。
|
|
|
+
|
|
|
+## 入口与交互
|
|
|
+
|
|
|
+### Web 管理端
|
|
|
+
|
|
|
+页面:
|
|
|
+
|
|
|
+- `/#/bx/reimburse/apply` → `web-admin/src/views/bx/reimburse/apply/index.vue`
|
|
|
+- `/#/bx/advance/apply` → `web-admin/src/views/bx/advance/apply/index.vue`
|
|
|
+
|
|
|
+| 位置 | 行为 |
|
|
|
+|------|------|
|
|
|
+| 列表工具栏 | 「撤回」按钮;仅当恰好选中 1 条且 `canWithdraw(row)` 时可用 |
|
|
|
+| 详情弹窗 | `canWithdraw(viewData)` 时展示「撤回」;与草稿态的编辑/提交互斥 |
|
|
|
+| 确认框 | 报销:说明撤回后将退回发票额度;备用金:说明撤回后可修改再提交 |
|
|
|
+
|
|
|
+前端判断:
|
|
|
+
|
|
|
+```ts
|
|
|
+function canWithdraw(row?: { status?: string; applicantId?: string }) {
|
|
|
+ if (!row) return false
|
|
|
+ return (
|
|
|
+ row.status === 'pending' &&
|
|
|
+ String(row.applicantId || '') === currentUserId()
|
|
|
+ )
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+图标建议:`RefreshLeft`(与运费结算/合同变更等撤回一致)。
|
|
|
+
|
|
|
+### 小程序
|
|
|
+
|
|
|
+页面:
|
|
|
+
|
|
|
+- `miniprogram/pages/bx/reimburse/detail.vue`
|
|
|
+- `miniprogram/pages/bx/advance/detail.vue`
|
|
|
+
|
|
|
+| 位置 | 行为 |
|
|
|
+|------|------|
|
|
|
+| 详情底栏 | `pending` 且当前用户为申请人时显示「撤回」;`showActions` 需覆盖 `pending` |
|
|
|
+| 确认 | `uni.showModal`;成功后刷新详情 |
|
|
|
+| 列表页 | 不加撤回按钮 |
|
|
|
+
|
|
|
+参考已有移动端备份实现:`bak/bx-mobile/src/views/reimburse/ReimburseDetailPage.vue`(确认文案含发票额度退回)。
|
|
|
+
|
|
|
+## 后端与接口
|
|
|
+
|
|
|
+### 报销(已有,本期原则上不改业务规则)
|
|
|
+
|
|
|
+- `POST /api/bx/reimburses/{id}/withdraw`
|
|
|
+- Service:`ReimburseService.withdraw`
|
|
|
+- 校验:单据存在(数据权限内)+ `applicant_id == user.id` + `status == pending`
|
|
|
+- 失败:HTTP 400,`报销单不存在、非本人申请或当前状态不可撤回`
|
|
|
+- Web API 已封装:`reimburseApi.withdraw`
|
|
|
+
|
|
|
+### 备用金(本期新增,对齐报销)
|
|
|
+
|
|
|
+- Service:`AdvanceService.withdraw`
|
|
|
+ - 同上申请人 + `pending` 校验
|
|
|
+ - `status = draft`,`current_node = None`
|
|
|
+ - **不**调用发票账本
|
|
|
+- 路由:`POST /api/bx/advances/{id}/withdraw`
|
|
|
+ - 注册于 `backend/app/api/bx/advance.py`(与 submit/approve 同级)
|
|
|
+- Web:`advanceApi.withdraw(id)`
|
|
|
+- 小程序:`miniprogram/api/bx.js` 中 `advanceApi.withdraw`、`reimburseApi.withdraw`
|
|
|
+
|
|
|
+### 错误与并发
|
|
|
+
|
|
|
+- 前端禁用非可撤单据;后端仍做硬校验(防绕过)
|
|
|
+- 若撤回瞬间审批人已通过末级导致状态非 `pending`:返回 400,前端提示失败并刷新列表/详情
|
|
|
+
|
|
|
+## 明确不做(本期)
|
|
|
+
|
|
|
+- 撤回原因录入
|
|
|
+- 批量撤回
|
|
|
+- 在审批历史中单独插入「撤回」节点(历史原记录保留即可)
|
|
|
+- 列表页(小程序)撤回入口
|
|
|
+- 修改报销既有 withdraw 的权限模型(保持仅申请人)
|
|
|
+
|
|
|
+## 实现落点(文件级)
|
|
|
+
|
|
|
+| 层 | 文件 |
|
|
|
+|----|------|
|
|
|
+| Backend | `backend/app/services/bx/advance.py`(新增 withdraw) |
|
|
|
+| Backend | `backend/app/api/bx/advance.py`(新增路由) |
|
|
|
+| Web API | `web-admin/src/api/bx/index.ts`(advanceApi.withdraw) |
|
|
|
+| Web UI | `web-admin/src/views/bx/reimburse/apply/index.vue` |
|
|
|
+| Web UI | `web-admin/src/views/bx/advance/apply/index.vue` |
|
|
|
+| Mini API | `miniprogram/api/bx.js` |
|
|
|
+| Mini UI | `miniprogram/pages/bx/reimburse/detail.vue` |
|
|
|
+| Mini UI | `miniprogram/pages/bx/advance/detail.vue` |
|
|
|
+
|
|
|
+可选:为 `AdvanceService.withdraw` 补最小单测(权限/状态门禁);报销若已有覆盖可只测备用金。
|
|
|
+
|
|
|
+## 验收标准
|
|
|
+
|
|
|
+1. 申请人在 Web 报销申请页对「审批中」单条可撤回 → 变为待提交 → 可编辑再提交。
|
|
|
+2. 非申请人无法撤回(按钮禁用;直调接口 400)。
|
|
|
+3. 撤回后发票额度回升(报销);备用金无额度副作用。
|
|
|
+4. Web 备用金申请页行为与报销对称。
|
|
|
+5. 小程序报销/备用金详情底栏可撤回,成功后详情变为可提交草稿态。
|
|
|
+6. 已审批通过 / 已驳回 / 草稿 单据不可撤回。
|