SOP-604:外采付款审批
标准 SOP 测试文档;每个任务(Task ID)代表一个独立的测试步骤,便于人工/自动化测试按序号完成并记录进度。
文档元数据
- 文档类型:SOP / Checklist
- 适用场景:外采端口付款(非广告账户充值),完成后从端口余额扣减并写流水
- 配置文件:
20260416_create_external_payment_review_template.sql(SOP-604 审批模板)
20260416_seed_ext_payment_dicts.sql(业务类型字典种子)
20260423_update_ext_payment_business_type.sql(业务类型口径更新为:广告业务类 / 商品采购类 / 其它)
20260423_drop_payment_method.sql(下线 payment_method 字段)
20260423_add_porter_cashflows_trans_order_id.sql(资金流水追溯列)
20260424_create_ext_payment_payees.sql(收款主体库)
- 创建者:财务 / 运营 / QA
- 最近更新时间:2026-04-24
- 相关接口定义:参考
crm.swagger.json 中 CashService 与 ReviewService 模块(特别是 POST /api/v1/cash/external_payment、POST /api/v1/cash/external_trans_orders、POST /api/v1/cash/ext_payment_payees、GET /api/v1/reviews/{flow_id}、POST /api/v1/reviews/{flow_id}/actions)
工作流概述
流程图
graph LR
A[发起申请] --> B[上级领导审批]
B --> C[财务审批]
C --> D[自动结算]
D --> E1[扣减端口余额]
D --> E2[写 transactions 流水]
D --> E3[写 porter_cashflows 资金流水]
D --> E4[订单状态 SUCCESS]
审批角色
| 步骤 |
审批人 |
说明 |
| 上级领导审批 |
提交人所在部门(含上级部门)的 leader(多层级串行,any) |
按 department_leaders 解析 |
| 财务审批 |
往来会计 / 总账会计 / 税务会计 / 财务主管 / 财务总 |
any 模式 |
前置条件
| 条件 |
检查方式 |
已使用有权限的用户登录(有 CashService.ExternalPayment + CashService.ListExternalTransOrders + CashService.ListExtPaymentPayees 权限) |
登录成功,外采付款菜单可见 |
| 目标外采端口存在且余额充足 |
SELECT balance FROM ext_corps WHERE provider='EXTERNAL' |
| 业务类型字典已导入 |
SELECT label FROM dicts WHERE type='ext_payment_business_type' 至少返回 3 条 |
| 已配置 SOP-604 审批模板 |
SELECT id FROM review_templates WHERE sop_number='SOP-604' AND is_active=1 |
审批模板 on_approved_actions[0].config 含 porter_id=payload.PorterID / amount=payload.Amount |
否则结算 action 会返回 porter_id field is required |
提交人所在部门在 department_leaders 至少有一个能登录的 leader |
否则 step1 会卡住 |
任务矩阵
任务 T1:进入外采付款列表
入口:CRM 菜单 → 外采付款
| Action Steps |
Input / Payload |
预期结果 |
验证方式 |
证据 |
状态 |
下游任务 |
步骤1:打开 /crm/external-payment |
— |
展示"外采付款单"页面 |
页面标题正确、表格加载 |
截图 |
[ ] |
步骤 2 |
| 步骤2:点击行展开箭头 |
点击订单号前的 ▸ |
展开详情面板 |
面板含订单信息 / 收款人 / 审计 4 组字段 |
截图 |
[ ] |
任务 T2 |
任务 T2:填写并提交外采付款表单
入口:外采付款列表页 → 新增外采付款
| Action Steps |
Input / Payload |
预期结果 |
验证方式 |
证据 |
状态 |
下游任务 |
| 步骤1:选择端口 |
选 porter_name="飞天经纬" |
端口字段显示已选端口 |
字段值正确 |
截图 |
[ ] |
步骤 2 |
| 步骤2:填金额 |
amount=1000 |
金额显示 1000 |
输入框显示 |
截图 |
[ ] |
步骤 3 |
| 步骤3:选业务类型 |
business_type="广告业务类" |
下拉显示选中值 |
字段值正确 |
截图 |
[ ] |
步骤 4 |
| 步骤4:选"历史收款主体"(若已有) |
从下拉选择一条历史主体 |
自动填入账户类型/名称/卡号/开户行等 |
6 项字段同时更新 |
截图 |
[ ] |
步骤 5 |
| 步骤5:填付款事由 / 付款日期 |
事由 e2e 测试;日期今天 |
字段正确显示 |
字段值正确 |
截图 |
[ ] |
步骤 6 |
| 步骤6:提交 |
点击"提交审批" |
trans_orders 新增一条 status=REVIEWING + events 触发 |
SELECT status FROM trans_orders ORDER BY id DESC LIMIT 1 返回 REVIEWING |
截图 |
[ ] |
任务 T3 |
| 步骤7:边界——空业务类型 |
留空提交 |
HTTP 400,消息 business_type 不能为空 |
Toast 错误显示 |
截图 |
[ ] |
— |
| 步骤8:边界——未来付款日期 |
付款日期设为明天 |
接口成功(付款日期与审批日期不强关联) |
后端接受 |
截图 |
[ ] |
— |
任务 T3:查看审批详情
入口:审批中心 → 我发起的 / 工作流详情
| Action Steps |
Input / Payload |
预期结果 |
验证方式 |
证据 |
状态 |
下游任务 |
| 步骤1:打开审批详情 |
点击刚创建的审批 |
顶部显示 外采付款详情 卡片 |
卡片含业务类型/付款事由/付款日期/收款人各字段 |
截图 |
[ ] |
步骤 2 |
| 步骤2:核对扁平字段 |
— |
extPayment 各字段与提交值一致 |
肉眼核对 |
截图 |
[ ] |
任务 T4 |
任务 T4:上级领导审批(step 1)
入口:审批中心 → 待我审批(提交人的 leader 登录)
| Action Steps |
Input / Payload |
预期结果 |
验证方式 |
证据 |
状态 |
下游任务 |
| 步骤1:leader 登录 |
提交人部门的 leader |
登录成功 |
页面加载 |
截图 |
[ ] |
步骤 2 |
| 步骤2:查看待审批 |
筛选 SOP-604 |
待审批出现该单 |
列表含 "外采付款审批" |
截图 |
[ ] |
步骤 3 |
| 步骤3:通过审批 |
点击"通过" |
review_instances.current_step=2(进入财务审批) |
DB 查询 |
截图 |
[ ] |
任务 T5 |
| 步骤4:多层 leader 场景 |
若有多层 leader,需每层各 approve 一次 |
current_step 到 2 时才视为 step1 完成 |
review_tasks 里该用户 PENDING=0 |
截图 |
[ ] |
— |
任务 T5:财务审批(step 2)
入口:审批中心 → 待我审批(财务 position 登录)
| Action Steps |
Input / Payload |
预期结果 |
验证方式 |
证据 |
状态 |
下游任务 |
| 步骤1:财务登录 |
position ∈ {往来会计, 总账会计, 税务会计, 财务主管, 财务总} |
登录成功 |
页面加载 |
截图 |
[ ] |
步骤 2 |
| 步骤2:通过审批 |
点击"通过" |
review_instances.status=APPROVED |
DB 查询 |
截图 |
[ ] |
任务 T6 |
任务 T6:验证资金池核算
入口:MySQL 直查
| Action Steps |
Input / Payload |
预期结果 |
验证方式 |
证据 |
状态 |
下游任务 |
| 步骤1:端口余额扣减 |
SELECT balance,total_balance FROM ext_corps WHERE id=PORTER_ID |
两个值都 -=amount |
数值比对 |
截图 |
[ ] |
步骤 2 |
| 步骤2:transactions 新增 |
SELECT type,sub_act_type,amount,status FROM transactions WHERE trans_order_id=TO_ID |
EXPEND / 外采付款 / amount / SUCCESS |
全字段核对 |
截图 |
[ ] |
步骤 3 |
| 步骤3:porter_cashflows 新增 |
SELECT flow_type,amount,discount,real_amount FROM porter_cashflows WHERE trans_order_id=TO_ID |
FLOW_TYPE_EXPEND / amount / 0 / amount |
全字段核对 |
截图 |
[ ] |
步骤 4 |
| 步骤4:trans_orders 最终态 |
SELECT status,err_msg FROM trans_orders WHERE id=TO_ID |
SUCCESS + err_msg 空 |
字段核对 |
截图 |
[ ] |
步骤 5 |
| 步骤5:审批失败路径 |
主动让结算失败(如 porter 余额<amount) |
trans_orders.status=FAILED + err_msg="completion actions failed: ..." |
字段核对 |
截图 |
[ ] |
— |
任务 T7:主体库验证
入口:外采付款表单 / DB
| Action Steps |
Input / Payload |
预期结果 |
验证方式 |
证据 |
状态 |
下游任务 |
| 步骤1:首次提交新收款人 |
账户名 张三 + 卡号 6228... |
ext_payment_payees 新增 1 行 |
SELECT usage_count,last_used_at FROM ext_payment_payees WHERE payee_account_name='张三' → usage_count=1 |
截图 |
[ ] |
步骤 2 |
| 步骤2:相同收款人再次提交 |
同账户名 + 同卡号 |
不新增行,usage_count +1 |
行数仍为 1,usage_count=2 |
截图 |
[ ] |
步骤 3 |
| 步骤3:表单刷新 |
打开表单,展开"历史收款主体"下拉 |
该条显示在下拉首位(last_used_at DESC) |
下拉第一项即为该收款人 |
截图 |
[ ] |
步骤 4 |
| 步骤4:选择填充 |
下拉选择该条 |
账户类型/名称/卡号/开户行/地区/支行 6 项自动填充 |
表单字段值 |
截图 |
[ ] |
— |
API 参考
相关接口
| 接口 |
方法 |
描述 |
/api/v1/cash/external_payment |
POST |
提交外采付款申请 |
/api/v1/cash/external_trans_orders |
POST |
列外采付款单(扁平订单+详情) |
/api/v1/cash/ext_payment_payees |
POST |
列历史收款主体(含关键字搜索、按 last_used_at DESC) |
/api/v1/reviews/{flow_id} |
GET |
审批详情(payload 含 extPayment 扩展字段) |
/api/v1/reviews/{flow_id}/actions |
POST |
approve / reject |
测试数据示例
成功案例
请求参数:
{
"porter_name": "飞天经纬",
"amount": 1000,
"business_type": "广告业务类",
"payment_reason": "e2e 测试外采付款",
"payment_date": "2026-04-24",
"payee_account_type": "个人",
"payee_account_name": "张三",
"payee_bank_card": "6228480000000000001",
"payee_bank": "工商银行",
"payee_bank_region": "湖南长沙",
"payee_bank_branch": "长沙分行",
"attachments": [],
"memo": "e2e"
}
预期响应:
{
"transOrder": { "id": 685, "status": "REVIEWING", "orderType": "ORDER_TYPE_EXTERNAL_PAYMENT", "amount": 1000, "..." },
"review": { "id": 758, "flowId": "...", "status": "REVIEWING" }
}
失败案例
| 场景 |
错误提示 |
HTTP 状态码 |
| 未填端口 |
必须提供 porter_id 或 porter_name |
400 |
| 金额 ≤ 0 |
付款金额必须大于0 |
400 |
| 空 business_type |
business_type 不能为空 |
400 |
| 无效 business_type |
无效的业务类型: xxx |
400 |
| 端口余额不足 |
settle action 失败 → trans_orders.err_msg="completion actions failed: porter balance not enough" |
审批完成但 status=REVIEWING,需关注 err_msg |
模板未配置 config.porter_id |
porter_id field is required |
同上 |
异常处理
| 异常场景 |
预期行为 |
处理方法 |
| 列表无数据 |
列表为空 |
检查 trans_orders.order_type='ORDER_TYPE_EXTERNAL_PAYMENT' 是否有数据;前端请求的接口是否是 /api/v1/cash/external_trans_orders |
| 审批详情"外采付款详情"缺字段 |
卡片部分字段显示 - |
后端 converter 是否填了 pbt.ExtPayment;gateway 是否用本地 replace 指向最新 core |
| step1 卡在 REVIEWING 不推进 |
current_step 不变 |
提交人所在部门 leader 必须能登录,多层 leader 需依次 approve |
| 结算静默失败 |
trans_orders 停在 REVIEWING |
查 err_msg;升级后 completion actions 失败会写回 err_msg |
进度采集模板
| 任务ID |
任务名称 |
执行人 |
开始时间 |
结束时间 |
状态 |
备注 |
| T1 |
进入外采付款列表 |
|
|
|
⬜ |
|
| T2 |
填写并提交外采付款表单 |
|
|
|
⬜ |
|
| T3 |
查看审批详情 |
|
|
|
⬜ |
|
| T4 |
上级领导审批 |
|
|
|
⬜ |
|
| T5 |
财务审批 |
|
|
|
⬜ |
|
| T6 |
验证资金池核算 |
|
|
|
⬜ |
|
| T7 |
主体库验证 |
|
|
|
⬜ |
|
附录
相关配置文件
migrations/20260416_create_external_payment_review_template.sql - SOP-604 审批模板(步骤 + action config)
migrations/20260416_seed_ext_payment_dicts.sql - 业务类型字典种子
migrations/20260423_update_ext_payment_business_type.sql - 业务类型最终口径
migrations/20260423_drop_payment_method.sql - 下线 payment_method
migrations/20260423_add_porter_cashflows_trans_order_id.sql - 资金流水追溯列
migrations/20260424_create_ext_payment_payees.sql - 收款主体库
相关文档
docs/SOP/sop-505-add-policy-compensation.md - 订单添加政策补差款审批
docs/SOP/sop-601-external-recharge.md - 外采帐户充值审批
docs/SOP/sop-603-porter-recharge.md - 端口充值审批
e2e 脚本
e2e/external_payment/run_s1_happy_path.sh(core 仓库):创建 → 审批 → 资金池核算 全链路
文档版本:v1.0.0
最后更新:2026-04-24
维护者:财务团队 / 运营团队 / QA