--- schema: "hl-changelog/v2" ticket: "finance-reimburse-module-onboarding" title: "报账款模块整模块接入指引(复核管理 5 接口 + 出纳执行 3 接口,含详情支出行明细与退回重推护栏)" consumer: "admin" author: "yst(GIT)" change_type: "新增接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "0aa4ec7a962205edf8b1e02664b8c2807b28ed02" target_release: "" verified_at: "2026-09-16" status_note: "报账款(核单→复核→出纳打款/回款)整模块首次对接指引,覆盖 Epic #7721 全链路 + Epic #7764 改进(详情内嵌核单支出行明细 lines、核单反确认加报账单执行护栏 584330、退回可重推)。8 个管理后台接口全部为本期新交付,前端此前无任何报账款文档,按本文一次对接即可。金额口径、状态机、错误码、勾稽勾稽区语义均以内联最终态给出。mmg 2026-09-16 前端已交付:api/finance/reimburse.js 复核 5 接口 + finance/reimburse 复核管理页(列表/详情勾稽区/lines 垫付明细/批准/退回/反审)+ CashierQueuePage REIMBURSE 外部单位线(金额锁死=settleAmount)+ ReimburseConfirmInModal(RECEIVABLE 走 confirm-in 无 amount)+ finance/pay/reimburse 薄壳;错误码透 message 不建字典;spec 22 例、checkpoint 13 项全绿。待后端配菜单行:财务域「报账款」component=finance/reimburse、出纳支付管理「报账款」component=finance/pay/reimburse。2026-09-16 后端 sys_menu 已配但落在「付款管理→报账复核」component=/finance/payable/reimburse,前端当日迁址对齐(复核页 git mv finance/reimburse→finance/payable/reimburse,commit 0aa4ec7a);支付行 finance/pay/reimburse 与配置一致。" updated_at: "2026-09-16" base: "dev-v3" --- # 报账款模块整模块接入指引(复核管理 + 出纳执行) > **服务**: hl-order-service-v3(hl-finance 模块) > **类型**: 🆕 整模块新增(本期新交付,前端首次对接) > **日期**: 2026-09-16 > **影响范围**: 管理后台财务域「报账款」菜单 + 出纳支付管理「报账款」页签 > **覆盖 Epic**: #7721(报账款全链路)+ #7764(详情明细 / reopen 护栏 / 退回重推) --- ## 一、模块定位 & 业务闭环 报账款 = **司机/导游等一线人员的行程费用报账**。一条订单核单(settlement)完成后,系统自动生成一张报账单,走「财务复核 → 出纳执行」两段: ``` 核单 confirmSettlement ──推送──▶ 报账单(PENDING) │ 财务复核(本模块复核管理 5 接口) ├─ 批准 approve PENDING→APPROVED(净额=0 两清单直通 CLOSED) ├─ 退回 return PENDING→RETURNED(终态留痕,可重推,见 §六) └─ 反审 un-approve APPROVED→PENDING(已付讫/已回款不可反审) │ APPROVED 出纳执行(出纳支付管理,按净额方向二选一) ├─ 净额<0 PAYABLE 公司补司机 → 出纳 pay → PAID(OUT 流水,余额减) ├─ 净额>0 RECEIVABLE 司机回款公司 → 出纳 confirm-in → RECEIVED(IN 流水,余额加) └─ 净额=0 BALANCED 两清 → 复核批准直通 CLOSED(无支付、无流水) ``` **单据来源唯一**:报账单只由核单推送生成,**不提供新建/编辑/删除接口**。前端只做「复核 + 出纳执行 + 查询」。 --- ## 二、金额口径(核心,前端展示勾稽区必读) 报账单金额全部由后端快照算出,前端**只读展示**,不参与计算: | 字段 | 含义 | 说明 | |---|---|---| | `collectableAmount` | 应代收 | 核单总额 − 客户线上已付(剔除司机代收 DRIVER_CASH) | | `actualCollectedAmount` | 实收代收 | 司机实际代收到的现金(勾稽快照) | | `underCollectAmount` | 欠收额 | = 应代收 − 实收代收(勾稽留痕,**不阻断**,供复核判断) | | `advanceAmount` | 预支合计 | 该单已预支给报账人的总额 | | `cashPaidAmount` | 现付垫付合计 | 报账人现付垫付总额(= 详情 `lines` 明细加总,见 §五) | | `netAmount` | 净额 | = 应代收 − 预支 − 现付垫付(**有符号**,正负定方向) | | `settleAmount` | 结算金额 | = \|净额\|(BALANCED 两清为 0),出纳执行金额锁死=此值 | | `direction` | 方向 | `PAYABLE` 公司应付报账人 / `RECEIVABLE` 报账人应回款 / `BALANCED` 两清 | > **勾稽区**(`actualCollectedAmount` / `underCollectAmount`)是复核依据:实收 ≠ 应代收时欠收额非 0,复核据此判断是否退回,但系统不强制拦。 --- ## 三、复核管理接口(5 个,`/admin/finance/reimburses`) ### 3.1 报账款分页 `GET /admin/finance/reimburses/page` 复核视角列表,支持状态/方向筛选 + 订单号/报账人姓名模糊。 **Query 入参**(继承 PageParam:`pageNo` / `pageSize` 必填): | 参数 | 必填 | 说明 | |---|---|---| | `status` | 否 | 单据状态:PENDING/APPROVED/PAID/RECEIVED/CLOSED/RETURNED;空=全部 | | `direction` | 否 | 方向:PAYABLE/RECEIVABLE/BALANCED;空=全部 | | `orderNo` | 否 | 订单号(模糊) | | `reporterName` | 否 | 报账人姓名(模糊) | **出参行 `ReimburseRowRespVO`**(金额=JSON number;Long ID=string): `id / reimburseNo(BZ-前缀) / orderId / orderNo / reporterName / reporterRole(GUIDE/DRIVER/PHOTOGRAPHER) / direction / collectableAmount / actualCollectedAmount / underCollectAmount / advanceAmount / cashPaidAmount / netAmount / settleAmount / status / reviewByName / reviewTime / createTime` ### 3.2 报账详情 `GET /admin/finance/reimburses/{id}` 全字段快照 + 勾稽 + 流水回执 + **核单支出行明细 `lines`**。 出参在分页行字段基础上追加: | 字段 | 说明 | |---|---| | `settlementId` / `versionNo` | 关联核单 ID / 同核单推送版本号(本期恒 1) | | `reporterAssignmentId` | 主报账人人员分配 ID | | `reviewBy` / `reviewRemark` | 复核人 ID / 复核意见(退回原因落此列) | | `fundAccountId` / `payFlowId` / `paidAt` | 出纳执行账户 / 资金流水 ID(一单一流水)/ 付讫到账时间 | | `flowNo` / `flowDirection` / `balanceAfter` | 流水回执(`payFlowId` 反查回填;**未付款/未回款时三字段均 null**) | | `lines` | **核单可报账支出行明细**(见 §五);无垫付固定返回 `[]` | | `remark` / `updateTime` | 备注 / 更新时间 | ### 3.3 复核批准 `PUT /admin/finance/reimburses/{id}/approve` `PENDING → APPROVED`;**净额=0 两清单直通 `CLOSED`**(不进出纳队列)。 入参 body(可空整 body):`{ "remark": "复核意见,可空,≤512字" }` ### 3.4 退回 `PUT /admin/finance/reimburses/{id}/return` `PENDING → RETURNED`(终态留痕;退回后可到核单反确认重推,见 §六)。 入参 body(必填):`{ "reason": "退回原因,必填,≤512字" }` ### 3.5 反审 `PUT /admin/finance/reimburses/{id}/un-approve` `APPROVED → PENDING`,清复核快照。**已付讫(PAID)/已回款(RECEIVED) 不可反审**。无 body。 --- ## 四、出纳执行接口(`/admin/finance/cashier`,复用出纳统一出口) 报账款进出纳后**复用出纳支付管理既有 4 接口**,靠 `payType`/`bizType = REIMBURSE` 路由。报账款的执行动作按方向二选一: ### 4.1 待付款队列 `GET /admin/finance/cashier/queue?payType=REIMBURSE` 拉「公司欠司机(PAYABLE)已批准待打款」的报账单。 | Query | 必填 | 说明 | |---|---|---| | `payType` | 是 | 报账款传 **`REIMBURSE`**(页签下拉值) | | `pageNo`/`pageSize` | 是 | 分页 | > 队列只含 `direction=PAYABLE 且 status=APPROVED` 的报账单;RECEIVABLE(司机回款)不进待付款队列,走下 §4.3。 出参行 `CashierQueueRowRespVO`:`id / bizNo(BZ-单号) / payType=REIMBURSE / payTypeName=报账款 / unitId / unitName / category=报账款(固定) / amount(=settleAmount) / fee / actualAmount / operatorName / createTime / occurDate / status=APPROVED / remark` ### 4.2 登记付款(公司补司机)`POST /admin/finance/cashier/pay` 净额<0(PAYABLE)时,出纳打款给报账人 → 记 OUT 流水 + 回写报账单 `PAID` + 重算账户结存。 | body 字段 | 必填 | 说明 | |---|---|---| | `bizType` | ✅ | 传 **`REIMBURSE`** | | `bizId` | ✅ | 报账单 ID | | `payAccountId` | ✅ | 出账公司账户 ID(fin_fund_account) | | `payMethod` | 否 | 付款方式字典 fin_pay_way:CASH/BANK/THIRD_PARTY | | `payChannel` | 条件 | 仅 `payMethod=THIRD_PARTY` 时传 WXPAY/ALIPAY,其余不传 | | `amount` | ✅ | 付款金额,**必须=settleAmount**,不等抛 598610 | | `fee` | 否 | 手续费(≥0,挂出账流水) | | `voucherNo`/`voucherUrl` | 否 | 付款凭证号 / 凭证影像 URL | | `payDate` | ✅ | 付款日期 yyyy-MM-dd(可回溯补录) | > 前端金额框建议**只读回填 settleAmount**(后端已锁死,改了必报 598610)。 ### 4.3 收款确认入账(司机回款)`POST /admin/finance/cashier/confirm-in` 净额>0(RECEIVABLE)时,司机把多收尾款交回公司 → 记 IN 流水 + 回写报账单 `RECEIVED`。**入账金额 = settleAmount**。 | body 字段 | 必填 | 说明 | |---|---|---| | `bizType` | ✅ | 传 **`REIMBURSE`**(空默认 NONBIZ 业务外收入,报账回款必须显式传) | | `bizId` | ✅ | 报账单 ID(须 direction=RECEIVABLE 且 status=APPROVED) | | `payAccountId` | ✅ | 入账公司账户 ID | | `payMethod` | 否 | 收款方式字典 fin_pay_way:CASH/BANK/THIRD_PARTY | | `payChannel` | 条件 | 仅 THIRD_PARTY 时传 WXPAY/ALIPAY | | `voucherNo`/`voucherUrl` | 否 | 收款凭证 | | `payDate` | ✅ | 收款日期 yyyy-MM-dd | > `confirm-in` 入参**无 amount 字段**——金额后端取单据 settle_amount,前端不传。方向不符(如给 PAYABLE 单调 confirm-in)抛 599207。 ### 4.4 已付款流水台账 `GET /admin/finance/cashier/payments/page` 出纳已付款流水(fin_fund_flow OUT),可按 bizType=REIMBURSE 过滤报账款打款记录。入参 `bizType`/`pageNo`/`pageSize` 等,出参与资金流水台账一致。 **pay / confirm-in 统一出参 `CashierPayRespVO`**:`flowId / flowNo(LS+yyyyMMdd+4位序号) / balanceAfter(本笔记完后结存) / bizId(已回写终态)` --- ## 五、详情支出行明细 `lines`(Epic #7766,复核依据) 报账详情出参 `lines` = **主报账人 CASH_PAID 现付垫付支出行**,供复核看「垫付明细构成」。**只含可报账行**,口径与 `cashPaidAmount` 严格一致(明细加总 == cashPaidAmount)。 `SettlementLineItemVO` 字段: | 字段 | 类型 | 说明 | |---|---|---| | `category` / `categoryName` | string | 费用类别码 / 中文名(HOTEL/TICKET/MEAL/GUIDE/PHOTOGRAPHER/OTHER_EXPENSE/SERVICE 等) | | `itemName` | string | 项目名(住宿=酒店-房型,门票=景区-规格,人员=姓名,车辆=车牌 车型/司机,其他=项目名) | | `unitPrice` / `quantity` | number | 单价 / 数量(可空) | | `amount` | number | 实际金额 | | `reimburseAmount` | number | 报账金额(**加总此列 == cashPaidAmount**) | | `paymentMethod` / `paymentMethodName` | string | 付款方式(本接口恒 `CASH_PAID` 现付垫付)/ 中文名 | | `date` | string | 业务日期 yyyy-MM-dd(可空) | | `remark` | string | 备注(可空) | > 口径说明(前端无需处理,了解即可):人员行(GUIDE/PHOTOGRAPHER)按 staffId 精确匹配主报账人;车辆现金行(VEHICLE)不计入可报账口径故不进 `lines`。**无垫付时 `lines` 固定返回 `[]`,不是 null。** --- ## 六、退回可重推 + 核单反确认护栏(Epic #7764/#7765) ### 6.1 退回可重推(前端只需知流程,无新接口) 复核退回(RETURNED)是终态留痕,但**金额错误可纠正**:财务退回 → 核单员到订单侧「反确认核单」调整 → 重 confirm 产生**新 settlement_id** → 系统推**新报账单**(BZ 单号递增),旧 RETURNED 单不动。前端在退回提示里告知「退回后可到核单反确认重推」即可。 ### 6.2 核单反确认护栏(错误码 584330,跨服务) 为防止「钱已动但核单又改」的资金不一致,订单侧反确认核单接口加了护栏: - **接口**:`POST /v3/admin/order/{orderId}/settlement/final-snapshots/reopen`(在 order-v3 核单域,**非本模块接口**) - **护栏**:该订单报账单已进入出纳执行(status ∈ PENDING/APPROVED/PAID/RECEIVED)→ 抛 **584330**,禁止反确认 - **放行**:无报账单 / 仅 RETURNED / CLOSED(净额0两清无流水) > 前端调核单反确认时若收到 **584330**,提示「该订单报账单已进入出纳执行,禁止反确认核单;如需调整请先退回报账单」。 --- ## 七、状态机 & 数据字典 ### 7.1 报账单状态 `status` | 值 | 中文 | 含义 / 可达动作 | |---|---|---| | `PENDING` | 待复核 | 可 approve / return | | `APPROVED` | 已批准 | 可 un-approve;PAYABLE 进出纳队列待 pay;RECEIVABLE 待 confirm-in | | `PAID` | 已付讫 | 终态(公司已打款);不可反审 | | `RECEIVED` | 已回款 | 终态(司机已回款);不可反审 | | `CLOSED` | 两清 | 终态(净额0直通,无流水) | | `RETURNED` | 已退回 | 终态留痕;可核单反确认重推新单 | ### 7.2 方向 `direction` `PAYABLE` 公司应付报账人 / `RECEIVABLE` 报账人应回款 / `BALANCED` 两清 ### 7.3 出纳付款类型 `payType`(队列页签) `REIMBURSE` 报账款(单号前缀 `BZ-`)。其余页签值:NONBIZ 业务外支出 / EXPENSE 费用报销 / PAYMENT 应付款 / PREPAY 预付款 / STAFF_LOAN 员工借款。 ### 7.4 收付方式 `payMethod`(字典 fin_pay_way) `CASH` 现金 / `BANK` 银行转账 / `THIRD_PARTY` 三方支付(须配 `payChannel`=WXPAY/ALIPAY) ### 7.5 流水方向 `flowDirection` `OUT` 出账 / `IN` 入账 --- ## 八、错误码 ### 8.1 报账域(段位 599200-599299) | 码 | 含义 | 触发场景 | |---|---|---| | 599201 | 报账执行单不存在 | 详情/操作 id 无效或已删 | | 599202 | 报账单状态非法 | 当前状态不允许此操作(如对 PAID 单 approve) | | 599203 | 报账单状态流转非法 | 并发推进,条件更新 0 行兜底 | | 599204 | 该核单已生成报账单 | 幂等命中(重推接缝预留) | | 599205 | 报账单推送快照数据非法 | 脏数据 fail-fast | | 599206 | 报账单号生成冲突 | 取号并发撞号,请重试 | | 599207 | 报账单方向与操作不匹配 | RECEIVABLE 走 pay / PAYABLE 走 confirm-in / BALANCED 进队列 | | 599208 | 退回原因必填 | return 未传 reason | ### 8.2 出纳域(段位 598600-598699,报账相关) | 码 | 含义 | 触发场景 | |---|---|---| | 598601 | 业务单不存在 | bizId 无效 | | 598602 | 业务单状态非已批准不可付款 | 对非 APPROVED 单 pay | | 598603 | 出账/入账账户不存在或已停用 | payAccountId 无效 | | 598604 | 账户余额不足且不允许透支 | 透支闸拦截 | | 598605 | 付款金额无效 | amount≤0 或 fee<0 | | 598606 | 收款确认单状态非法 | confirm-in 单非已批准可入账 | | 598607 | 付款类型非法 | payType/bizType 非已接通枚举 | | 598608 | 收付方式与渠道不匹配 | THIRD_PARTY 未传渠道 / CASH,BANK 传了渠道 | | 598609 | 收付账户与方式不匹配 | 现金方式选银行账户等两级校验 | | 598610 | 付款金额与单据应付金额不一致 | pay 的 amount ≠ settleAmount(金额锁死) | ### 8.3 核单域(跨服务,reopen 护栏) | 码 | 含义 | 触发接口 | |---|---|---| | 584330 | 报账单已进入出纳执行,禁止反确认核单 | `POST /v3/admin/order/{orderId}/settlement/final-snapshots/reopen`(order-v3) | --- ## 九、示例 ### 9.1 复核列表(筛选待复核 + PAYABLE) ```http GET /admin/finance/reimburses/page?status=PENDING&direction=PAYABLE&pageNo=1&pageSize=20 ``` ### 9.2 批准 → 出纳打款(PAYABLE 公司补司机) ```http PUT /admin/finance/reimburses/{id}/approve { "remark": "复核无误" } # 待付款队列取单 GET /admin/finance/cashier/queue?payType=REIMBURSE&pageNo=1&pageSize=20 # 出纳打款(金额锁死=settleAmount) POST /admin/finance/cashier/pay { "bizType":"REIMBURSE", "bizId":"<报账单ID>", "payAccountId":"<账户ID>", "payMethod":"BANK", "amount":975.00, "payDate":"2026-09-16" } ``` ### 9.3 司机回款(RECEIVABLE) ```http POST /admin/finance/cashier/confirm-in { "bizType":"REIMBURSE", "bizId":"<报账单ID>", "payAccountId":"<账户ID>", "payMethod":"CASH", "payDate":"2026-09-16" } ``` ### 9.4 边界:金额不符 / 方向不符 / 反确认被拦 ```http # amount≠settleAmount → 598610 POST /admin/finance/cashier/pay {..."amount":999.00...} → 598610 付款金额与单据应付金额不一致 # PAYABLE 单调 confirm-in → 599207 POST /admin/finance/cashier/confirm-in {..."bizId":""...} → 599207 方向与操作不匹配 # 报账单已进执行 → 核单反确认 584330 POST /v3/admin/order/{orderId}/settlement/final-snapshots/reopen → 584330 禁止反确认核单 ``` --- ## 十、影响评估 / 回滚 - **纯新增模块**,前端首次对接,无存量调用方,无回滚负担。 - 复核/出纳接口均已部署测试服并行为级验证通过。 - 若前端暂不上报账款页面,后端模块独立运行不受影响(报账单仍由核单推送正常生成)。 ## 十一、注意事项 1. **金额一律只读**:报账单所有金额(含勾稽区、settleAmount、lines)由后端快照算出,前端展示即可,不要本地重算。 2. **出纳金额锁死**:pay 的 `amount` 必须 = `settleAmount`;confirm-in **不传 amount**。建议前端金额框只读回填。 3. **Long ID 是 string**:所有 ID(id/orderId/settlementId/bizId/payFlowId 等)序列化为 string,前端按字符串处理防精度丢失。 4. **方向决定动作**:一张报账单面前只有一个可点按钮——PAYABLE 显示「付款」(pay)、RECEIVABLE 显示「收款入账」(confirm-in)、BALANCED/PAID/RECEIVED/CLOSED/RETURNED 均不进出纳。 5. **流水回执可空**:详情的 `flowNo/flowDirection/balanceAfter` 在未付款/未回款时为 null,前端做好空态。 ## 十二、关联 / 联系人 - Epic #7721 报账款全链路:[Issue #7721](https://git.1814.love:8443/wx/HL/issues/7721) - Epic #7764 报账款改进:[Issue #7764](https://git.1814.love:8443/wx/HL/issues/7764)(详情明细 #7766 / reopen 护栏 #7765) - 合并 PR:[#7771](https://git.1814.love:8443/wx/HL/pulls/7771)(护栏+明细)、[#7774](https://git.1814.love:8443/wx/HL/pulls/7774)(文档+原型) - 负责人:腰苏图(yst)| 反馈:财务域后端对接群 / 直接 @yst