docs: add design for bx apply withdraw (reimburse + advance)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
8c96de2c0c
commit
ac713070ab
1 file changed
+147
@@ -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. 已审批通过 / 已驳回 / 草稿 单据不可撤回。
|
||||
Reference in new issue
Block a user