Files
shanxia2.0/docs/superpowers/specs/2026-06-25-pl-freight-settlement-design.md
T
2026-06-25 08:06:32 +08:00

18 KiB
Raw Blame History

运费结算完善 — 设计文档

日期: 2026-06-25
状态: 已确认(2026-06-25)
前置: docs/运输计划管理-需求说明文档.md(§10、BR-26~29、BR-40,BR-27 由本设计 supersede)、docs/superpowers/specs/2026-06-25-pl-plan-archive-design.md
界面模式: P08 页内 Tab + P02 状态驱动列表 + P22 行内审批 + P13 财务确认发放

目标

完善【运输管理 → 运费结算】模块:按业务阶段拆分为 四个 Tab,支持从未结算计划组单、结算单多级审批(对齐合同管理)、审批完成后财务独立 Tab 登记发放,最终进入已结算归档态。


已确认决策(2026-06-25)

# 决策 说明
D1 四 Tab 独立 未结算计划 / 结算单(草稿+审批中)/ 待财务发放 / 已结算 — 四个 Tab 各自独立,不用子筛选合并
D2 审批对齐合同管理 提交时选审批人 + 顺序/会签/或签;独立审批记录表;审批按钮仅在「待我审批」时出现在行操作列
D3 财务发放独立 Tab approved 状态的结算单进入 Tab3「待财务发放」;财务在此 Tab 做发放登记(附件+备注),完成后 confirmed 进入 Tab4
D4 组单以计划展示、运输落库 Tab1 列表以运输计划维度展示(可含多车牌);生成结算单时落库关联 transport_id(复用 pl_freight_settlement_transport)
D5 未结算准入:已归档 + 有运费 Tab1 仅展示 archive_status=archived 且 has_freight=1、且运输单尚未被非草稿结算单占用的计划
D6 Supersede BR-27 原「不走工作流、计划专员 C 直接确认」作废;改为合同式多级审批 + 财务发放两阶段

非目标(本期不做)

  • 接入通用 Python 工作流引擎(OpenSpec workflow 目标设计尚未落地)
  • 结算单驳回后自动拆单/释放部分运输单(驳回整单回到草稿,关联运输单一并释放)
  • 邮件/短信通知审批人(仅系统内操作)
  • 运费结算报表导出
  • 按归属公司隔离数据(延续 BR-51)

一、页面结构(四 Tab)

1.1 路由与菜单

项 值
菜单名 运费结算(沿用)
Hash /#/pl/freight
组件 web-admin/src/views/pl/freight/index.vue(重构为 Tab 容器 + 子面板)
menuCode pl:freight:list

1.2 Tab 定义

┌─────────────┬─────────────┬───────────────┬─────────────┐
│ 未结算计划  │ 结算单      │ 待财务发放    │ 已结算      │
│ Tab unsettled│ Tab bills  │ Tab disburse  │ Tab settled │
└─────────────┴─────────────┴───────────────┴─────────────┘
Tab name 标题 结算状态 filter 主要角色
1 unsettled 未结算计划 —(查计划,非结算单) 计划专员
2 bills 结算单 pending, approving 计划专员、审批人
3 disburse 待财务发放 approved 财务
4 settled 已结算 confirmed 全员只读

Tab 切换不走路由 query;默认激活 Tab1。各 Tab 独立筛选、分页、列表刷新。

1.3 权限按钮(新增/调整)

权限码 用途
pl:freight:list 菜单 + 四 Tab 列表(已有)
pl:freight:query 查看结算单/计划详情
pl:freight:create Tab1 多选组单
pl:freight:edit Tab2 草稿编辑、发票信息
pl:freight:submit Tab2 提交审批
pl:freight:approve Tab2 行内审批(后端校验是否当前节点审批人)
pl:freight:withdraw Tab2 撤回审批(提交人)
pl:freight:disburse Tab3 财务发放登记
pl:freight:disburse:list Tab3 列表(可选:与 disburse 合并,无 list 则 finance 角色仅见 Tab3 需菜单子权限设计 — 实现时 Tab3/4 仍受 list 控制,按钮用 disburse)

二、Tab1 — 未结算计划

2.1 数据范围(D5)

同时满足:

  1. pl_plan.archive_status = 'archived'
  2. pl_plan.has_freight = 1
  3. pl_plan.follow_status != 'cancelled'
  4. 计划已关联运输单(transport_id 非空)且 pl_transport.freight_amount > 0
  5. 该 transport_id 未出现在任何 settlement_status NOT IN ('pending') 且 del_flag=0 的结算单关联中
    (若仅在 pending 草稿结算单中,视为「占用中」:Tab1 不展示,Tab2 草稿可编辑/删除后释放)

2.2 列表列(建议)

列 来源
多选 type="selection"
计划编号 planNo
计划日期 planDate
单据类型 planType Tag
客户/供应商 customerName / supplierName
车牌号 licensePlate
运输公司 transportCompany
运费(元) freightAmount
归档时间 updateTime 或归档变更记录

筛选:计划编号、日期范围、客户、车牌号、单据类型(与运输计划列表 subset 对齐)。

2.3 工具栏与操作

控件 规则
已选 N 项 最前
生成结算单 唯一主色按钮;至少选 1 条;调用 POST /transport/freight/
查看 行操作,打开计划只读查看(PlPlanViewDialog 或跳转 plan detail)

组单规则:

  • 一次多选生成 一张 结算单
  • 后端校验:所有 plan 的 transport 均未锁定;租户一致
  • total_freight = sum(transport.freight_amount)
  • 创建后跳转/激活 Tab2,定位到新结算单

三、Tab2 — 结算单(草稿 + 审批中)

3.1 数据范围

settlement_status IN ('pending', 'approving')

可选 Tab 内筛选:结算单号、创建时间、提交人、状态(草稿/审批中)。

3.2 列表列

列 说明
结算单号 链接打开查看弹窗
运费合计 totalFreight
关联计划数 聚合 count
车牌摘要 多车牌逗号分隔,tooltip 全量
结算状态 Tag:pending 草稿 / approving 审批中
当前审批人 approving 时展示当前节点审批人姓名
创建人 / 时间

3.3 行操作(操作列 fixed="right",min-width ≥ 280)

状态 查看 编辑 提交审批 撤回 审批
pending ✅ ✅(创建人或有 edit 权限) ✅ — —
approving ✅ ❌ — ✅(提交人) ✅ 仅 canApprove=true

约束(D2):

  • 禁止在列表顶栏放批量审批
  • 「审批」按钮必须带 <el-icon>,与规范一致
  • canApprove 由列表/详情 API 返回(后端 can_user_approve_settlement)

3.4 草稿编辑弹窗

  • 只读:结算单号、合计
  • 可编辑:发票号、开票日期、发票金额、备注(FreightSettlementUpdate 已有字段)
  • 明细区:关联计划/运输表格(只读);本期不支持草稿中增删关联行(需删单重建)
  • 删除草稿:软删结算单 + 释放 transport 占用(新增 DELETE /freight/{id},仅 pending)

3.5 提交审批弹窗

复用/抽取合同 ApprovalDialog 交互(web-admin/src/views/crm/contract/components/ApprovalDialog.vue):

字段 说明
审批类型 顺序 / 会签 / 或签
审批人 多选远程搜索用户
提交说明 可选

提交后:pending → approving,创建 pl_freight_settlement_approval 记录。

3.6 审批弹窗

  • 审批意见(必填/选填与合同一致)
  • 通过 / 驳回
  • 驳回:approving → pending,审批记录置 rejected,释放为可重新编辑草稿(审批记录保留历史)

3.7 查看弹窗

  • Hero:结算单号 + 状态 Tag + 运费合计
  • Section:发票信息、关联运输/计划明细表
  • Section:审批流时间线(对齐合同 ContractDetailDialog 审批区)
  • Section:汇款凭证(confirmed 后在 Tab4 查看;Tab2 仅已发放前为空)

四、Tab3 — 待财务发放

4.1 数据范围

settlement_status = 'approved'

4.2 列表列

列 说明
结算单号
运费合计
审批完成时间 最后一节点 approval_time
提交人
关联计划数 / 车牌摘要
状态 Tag:待发放

4.3 行操作

操作 权限 说明
查看 query 只读详情 + 完整审批流
发放登记 disburse 打开财务确认弹窗

无审批按钮。

4.4 发放登记弹窗(P13 财务确认)

┌─────────────────────────────────────────────┐
│ 财务发放登记 · {settlementNo}          [×]  │
├─────────────────────────────────────────────┤
│ [只读] 结算单号、运费合计、关联明细摘要      │
│ [只读] 审批流时间线(已通过)                │
├─────────────────────────────────────────────┤
│ 汇款日期 *                                   │
│ 汇款金额 *                                   │
│ 汇款凭证 *  附件卡片上传(多文件)            │
│ 发放备注     textarea                        │
├─────────────────────────────────────────────┤
│ 取消 │ 确认发放 │
└─────────────────────────────────────────────┘

提交逻辑(单事务):

  1. 校验 settlement_status == approved
  2. 写入 pl_remittance_voucher(voucher_urls JSON 数组、remittance_date、remittance_amount、remark)
  3. settlement_status → confirmed
  4. update_by 当前财务用户

五、Tab4 — 已结算

5.1 数据范围

settlement_status = 'confirmed'

5.2 列表列

在 Tab3 基础上增加:汇款日期、汇款金额、财务确认人、确认时间。

5.3 行操作

仅 查看(含汇款凭证预览/下载)。


六、状态机

                    ┌──────────┐
         组单       │ pending  │ 草稿 (Tab2)
    ───────────────▶│          │
                    └────┬─────┘
                         │ submit
                         ▼
                    ┌──────────┐
                    │approving │ 审批中 (Tab2)
                    └────┬─────┘
              reject   │   │ all approved
                 ┌─────┘   └─────┐
                 ▼               ▼
            ┌──────────┐   ┌──────────┐
            │ pending  │   │ approved │ 待财务发放 (Tab3)
            └──────────┘   └────┬─────┘
                                │ disburse
                                ▼
                           ┌──────────┐
                           │confirmed │ 已结算 (Tab4)
                           └──────────┘

6.1 字典 pl_settlement_status(更新)

dict_value dict_label tag_type 所在 Tab
pending 草稿 info Tab2
approving 审批中 warning Tab2
approved 待发放 primary Tab3
confirmed 已结算 success Tab4
rejected 已驳回 danger 仅审批记录,结算单回到 pending

移除或废弃旧标签「已提交」「已审核」与 API 中 submitted 的直接终态跳转。

6.2 运费锁定(与归档模块一致)

以下状态的结算单关联 transport 锁定运费修改(transport_has_locked_freight_settlement):

approving, approved, confirmed

pending 草稿不锁定(删单可释放)。


七、后端设计

7.1 新表 pl_freight_settlement_approval

字段 类型 说明
id varchar(64) PK
settlement_id varchar(64) 结算单 ID
step int 步骤序号
approver_id varchar(64) 审批人
approver_name varchar(64) 冗余
approval_type varchar(20) sequential / parallel / or
approval_result varchar(20) pending / approved / rejected
approval_opinion varchar(500)
approval_time datetime
is_current tinyint 当前节点
tenant_no + 公共字段

索引:(settlement_id, del_flag), (approver_id, approval_result, is_current)

7.2 Service 层(新建 pl_freight_settlement_service.py)

函数 职责
list_unsettled_plans Tab1 查询
create_settlement_from_transports 组单 + 占用校验
submit_settlement_approval 创建审批链,pending→approving
approve_settlement_step 单步通过/驳回 + 推进
withdraw_settlement_approval 撤回
can_user_approve_settlement 当前用户是否可审
disburse_settlement 财务发放 + voucher
delete_draft_settlement 软删草稿

CRUD 参考:backend/app/crud/crm/contract_approval.py(勿直接依赖 CRM 表)。

7.3 API 清单

Method Path 说明
GET /transport/freight/unsettled-plans Tab1 分页列表
GET /transport/freight/ Tab2/3/4;增加 tab 或 settlementStatus 多值
POST /transport/freight/ 组单 { planIds } 或 { transportIds }(推荐 planIds,服务端解析 transport)
GET /transport/freight/{id} 详情 + transports + plans 摘要 + canApprove
PUT /transport/freight/{id} 更新草稿
DELETE /transport/freight/{id} 删除草稿
POST /transport/freight/{id}/submit-approval 提交审批 body 同合同
POST /transport/freight/approval/{approvalId} 通过/驳回
POST /transport/freight/{id}/withdraw-approval 撤回
GET /transport/freight/{id}/approval-flow 审批时间线
POST /transport/freight/{id}/disburse 财务发放 body: remittanceDate, amount, voucherUrls, remark

废弃/替换:

  • POST /{id}/submit → 由 submit-approval 替代
  • POST /{id}/approve → 由 approval/{id} 替代
  • POST /voucher → 合并进 disburse 或内部调用

列表项返回 canApprove: boolean(当前用户 + 当前 settlement)。

7.4 审批规则(对齐合同)

类型 规则
sequential 按选择顺序,仅 is_current=1 且 pending 的一人可审
parallel 所有 pending 节点需全部通过
or 任一人通过即通过

全部通过后:settlement_status → approved,进入 Tab3。


八、前端文件规划

文件 动作
views/pl/freight/index.vue 重构为 Tab 容器
views/pl/freight/components/UnsettledPlanPanel.vue Tab1
views/pl/freight/components/SettlementBillPanel.vue Tab2
views/pl/freight/components/DisbursePanel.vue Tab3
views/pl/freight/components/SettledPanel.vue Tab4
components/business/pl-freight-settlement-view-dialog/index.vue 查看弹窗
components/business/pl-freight-settlement-form-dialog/index.vue 草稿编辑
components/business/pl-freight-approval-dialog/index.vue 提交/审批(可 fork 合同 ApprovalDialog)
components/business/pl-freight-disburse-dialog/index.vue 财务发放
api/pl/index.ts 扩展 freightApi

九、与现有模块衔接

模块 衔接点
运输计划归档 Tab1 要求已归档;归档弹窗改运费仍受结算锁定约束
合同管理 审批 UX、API 语义对齐
运输计划列表 不新增结算入口,结算统一在 freight 模块
需求文档 实现后更新 §10.1、BR-27、BR-40、附录 C

十、验收清单

# 场景 预期
V1 四 Tab 切换 各自列表/筛选/分页独立正常
V2 Tab1 仅已归档有运费且未占用计划 不符合不出现
V3 多选组单 生成一张草稿,出现在 Tab2,Tab1 对应行消失
V4 Tab2 草稿编辑发票 保存成功
V5 提交审批 进入 approving,审批流生成
V6 非当前审批人 Tab2 无审批按钮
V7 当前审批人 行操作列出现审批,通过后流转
V8 全部审批通过 结算单出现在 Tab3,Tab2 消失
V9 Tab3 发放登记 上传凭证+备注,进入 Tab4
V10 Tab4 查看 只读,含凭证与审批流
V11 审批中锁定 归档页不可改关联 plan 运费
V12 驳回 回 Tab2 草稿,可再提交
V13 权限 无 disburse 不见 Tab3 发放按钮

十一、实施阶段建议

Phase 范围
P1 四 Tab 骨架 + Tab1 未结算 + 组单 + Tab4 只读列表
P2 Tab2 草稿 CRUD + 查看弹窗
P3 审批表 + 提交/审批/撤回 + canApprove 行按钮
P4 Tab3 财务发放 + voucher + 状态 confirmed
P5 菜单权限 SQL、字典更新、归档锁定联调、文档修订

十二、Open Questions(实现前默认可按本文)

# 问题 默认
Q1 Tab1 是否必须已归档 是(D5)
Q2 草稿能否删单 是,仅 pending
Q3 驳回后审批历史 保留记录,结算单回 pending
Q4 发放金额是否必须等于运费合计 允许差异,备注必填当不等