2026-06-12-qc-work-order-fee-summary-design.md 11 KB

质检工单 · 用户费用摘要展示设计

日期:2026-06-12
状态:已实现
需求确认:方案 A — 当前快照(展示合同/已收/待缴/缴费状态,不含历史轨迹、不含与质检动作的联动规则)


1. 背景与目标

1.1 业务诉求

质检专员在 入库质检工单、出库质检工单 列表中处理工单时,需要快速了解关联来源单据上的 用户费用当前状态,避免在配置/执行质检时忽略欠费、待确认收款等情况。

1.2 展示范围(已确认)

Tab 来源 系统标识 是否展示费用
入库质检 采集协议单(细胞存储) receptionSource = cell_storage 是
入库质检 捐赠协议单(科研捐赠) receptionSource = research_donation 否(业务无缴费)
出库质检 中试服务申请单 bizType = pilot_service 否(渠道扣费)
出库质检 基础实验 bizType = basic_experiment 是
出库质检 临床应用 bizType = clinical_application 否(本期不做,架构预留)

1.3 非目标

  • 不展示费用 变更历史、操作日志、时间线
  • 不根据费用状态 阻断或自动推进 质检流程
  • 不在本模块 编辑 协议/申请单费用
  • 不展示 质检检测自身费用(第三方检测 thirdPartyFeeTotal、任务 feeAmount)——与用户协议/申请单费用分区展示,避免混淆

1.4 与现有 UI 的关系

  • 列表层:新增紧凑「费用摘要条」,满足「特别关注」的扫视需求
  • 查看弹窗层:补充完整只读缴费区块,可跳转来源单据详情
  • 视觉对齐 Fresh Mint 规范;金额用 formatMoneyDisplay;状态用 el-tag

2. 费用字段定义

2.1 入库 · 采集协议单(paymentSummary)

数据来源:biz_protocol_payment + biz_protocol_payment_detail(经 collection_protocol_id 关联)。

字段 API 别名 说明 计算/口径
缴费模式 payMode dj 趸交 / fq 分期 主表
缴费模式标签 payModeLabel 趸交 / 分期 后端或前端码表
合同金额 contractAmount 元 主表 contract_amount
已收金额 receivedAmount 元 明细中 payStatus ∈ {yjf, jfqrz} 的 transferAmount 之和(与 ProtocolViewDialog 一致)
待收金额 pendingAmount 元 max(contractAmount - receivedAmount, 0),合同为空时返回 null
缴费状态摘要 statusHint 如「已缴清」「1 期待确认」「2 期待缴费」 由明细 payStatus 聚合生成
整体缴费状态 Tag overallPayStatus / overallPayStatusLabel 用于摘要条 Tag 见 §2.1.1

2.1.1 整体缴费状态(摘要 Tag 语义)

优先级从高到低:

  1. 存在 tfqrz(退费确认中)→ Tag warning,文案「退费确认中」
  2. 存在 ytf 且无其他未完结明细 → Tag info,文案「已退费」
  3. 合同金额有效且 pendingAmount <= 0 且不存在 dfj/jfqrz → Tag success,文案「已缴清」
  4. 存在 jfqrz → Tag warning,文案「收款确认中」
  5. 存在 dfj → Tag info,文案「待缴费」
  6. 其他 → Tag info,文案「—」

分期场景下 statusHint 附加明细计数,例如:「分期 · 2 期已缴 / 1 期待缴费」。

2.2 出库 · 基础实验(feeSummary)

数据来源:biz_pilot_infusion_schedule.amount(售卖金额)+ biz_pilot_payment(按 infusion_schedule_id 一对一)。

字段 API 别名 说明
售卖金额 saleAmount schedule.amount
应缴金额 paymentAmount BizPilotPayment.amount
实收金额 transferAmount 已确认收款时的 transfer_amount,否则 null
缴费状态 payStatus dfj / jfqrz / yjf / tfqrz / ytf
缴费状态标签 payStatusLabel 与临床应用缴费模块码表一致
缴费状态 Tag 类型 payStatusTagType 复用 payStatusTagType()

无缴费记录时:paymentAmount、payStatus 等为 null,摘要展示「暂无缴费记录」。


3. 后端设计

3.1 列表 API 扩展

接口:现有 GET /web/admin/qc-work-orders(list_work_orders)

在 _enrich_list_items 流程末尾批量 enrich,避免 N+1:

入库 Tab(workOrderType=inbound)

  1. 收集本页工单的有效 collectionProtocolId(且 receptionSource === cell_storage)
  2. 批量查询 payment 主表 + 明细
  3. 为每条工单写入 paymentSummary(非 cell_storage 时为 null)

出库 Tab(workOrderType=outbound)

  1. 收集本页 bizType === basic_experiment 的 infusionScheduleId
  2. 批量查询 BizPilotPayment + schedule.amount
  3. 写入 feeSummary(非 basic_experiment 时为 null)

新增工具函数建议位置:backend/app/crud/qc_work_order.py 或拆至 backend/app/services/qc_work_order_fee_summary.py

def build_protocol_payment_summaries(db, protocol_ids: List[str], tenant_no: str) -> Dict[str, dict]: ...
def build_basic_experiment_fee_summaries(db, schedule_ids: List[str], tenant_no: str) -> Dict[str, dict]: ...

3.2 详情 API(可选增强)

  • 入库查看:现有工单详情接口增加 paymentSummary + paymentDetails(明细列表,供弹窗展开;可限制最多返回当前协议全部明细,通常 ≤ 分期期数)
  • 出库查看:get_outbound_qc_detail 增加 feeSummary;OutboundQcViewDialog 已有 schedule 区块,补充缴费只读段

本期 最低交付:列表摘要字段;详情弹窗至少与列表同级信息,推荐附带 1~3 条关键明细(分期时下一期应缴)。

3.3 数据库

无需 DDL。只读 join 现有表:

  • biz_protocol_payment / biz_protocol_payment_detail
  • biz_pilot_payment / biz_pilot_infusion_schedule

3.4 性能

  • 列表 enrich 按 当前页 protocol_id / schedule_id 去重 后批量查询
  • 单页默认 10~20 条,额外 1~2 次 batch query,可接受

4. 前端设计

4.1 入库 · 样本组费用摘要条

文件:web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue

位置:.qc-sample-row__ctx 下方、工单 Tab 上方,仅当组内任一工单 receptionSource === 'cell_storage' 且存在 paymentSummary 时渲染。

布局(单行 flex,窄屏换行):

[协议费用]  趸交  ·  合同 ¥28,000.00  ·  已收 ¥28,000.00  ·  待收 ¥0.00  ·  [已缴清]
  • 背景:#ecfdf5(--primary-bg),上边框与 ctx 区分
  • 字体:12px,标签 muted,金额 semibold
  • 右侧 Tag:overallPayStatusLabel + 语义色
  • 点击摘要条或「查看缴费」链接:打开入库工单查看弹窗并滚动至缴费区(可选,P1)

组内多工单:费用按 collectionProtocolId 去重,同协议只展示一份(组 context 已按样本聚合)。

4.2 出库 · 基础实验卡片摘要

文件:web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue

条件:activeTypeTab === 'outbound' && row.bizType === 'basic_experiment' && row.feeSummary

位置:.qc-work-order-card__meta 末尾或独立 .qc-fee-summary 行(全宽 grid-column: 1 / -1)

[费用信息]  售卖 ¥12,000.00  ·  应缴 ¥12,000.00  ·  [待缴费]

已缴费时追加:实收 ¥12,000.00

4.3 查看弹窗

场景 文件 新增区块
入库(cell_storage) index.vue viewSections 「协议缴费信息」:模式、合同、已收、待收、状态 Tag;分期时列 1 行下一期应缴(如有)
出库(basic_experiment) OutboundQcViewDialog.vue 「缴费信息」:售卖、应缴、实收、状态;可复用 ClinicalPaymentSubmitReadonly 精简字段

跳转链接(可选 P1):

  • 入库 → /#/cell-storage/protocol 并打开对应协议查看弹窗(需协议 id)
  • 出库 → 基础实验申请单查看(applicationId 若列表已有)

4.4 筛选修正

文件:index.vue 出库 Tab 筛选「业务类型」

补全选项:

<el-option label="基础实验" value="basic_experiment" />

与三种出库来源一致。

4.5 API 类型

文件:web-admin/src/api/qc-work-order.ts

export type QcProtocolPaymentSummary = {
  payMode?: string
  payModeLabel?: string
  contractAmount?: number | null
  receivedAmount?: number | null
  pendingAmount?: number | null
  statusHint?: string
  overallPayStatus?: string
  overallPayStatusLabel?: string
}

export type QcBasicExperimentFeeSummary = {
  saleAmount?: number | null
  paymentAmount?: number | null
  transferAmount?: number | null
  payStatus?: string
  payStatusLabel?: string
}

// QcWorkOrder 扩展
paymentSummary?: QcProtocolPaymentSummary | null
feeSummary?: QcBasicExperimentFeeSummary | null

4.6 公共组件(可选)

若入库/出库摘要条样式一致,可抽 QcWorkOrderFeeSummaryBar.vue(props: variant: 'inbound-protocol' | 'outbound-basic' + summary object)。非必须,首版可在两处各写 scoped 块,YAGNI。


5. 交互与空态

情况 展示
科研捐赠入库工单 不渲染费用区块
中试/临床出库工单 不渲染费用区块
采集协议无 payment 主表 摘要:「暂无缴费信息」
基础实验无 payment 记录 摘要:「暂无缴费记录」;仍展示售卖金额(若有)
金额为 null 显示 —

只读:摘要区无编辑、无确认收款按钮。


6. 测试要点

6.1 入库

  • 细胞存储样本组显示协议费用摘要,数值与采集协议单查看弹窗一致
  • 科研捐赠样本组不显示费用摘要
  • 趸交已缴清 / 分期部分待缴 / 收款确认中 / 退费确认中 Tag 正确
  • 同页多条工单同协议,摘要去重一致

6.2 出库

  • 基础实验卡片显示售卖 + 缴费状态
  • 中试服务、临床应用卡片无费用摘要
  • 筛选「基础实验」可正确过滤
  • 查看弹窗缴费区与列表摘要一致

6.3 回归

  • 第三方检测费用展示不受影响
  • 临时质检 Tab 无费用区块
  • 列表分页、分组、Tab 切换正常

7. 影响文件清单

层级 文件
后端 backend/app/crud/qc_work_order.py(+ 可选 services/qc_work_order_fee_summary.py)
后端 Schema backend/app/schemas/qc_work_order.py(若响应体走 Pydantic)
前端 API web-admin/src/api/qc-work-order.ts
前端页面 QcWorkOrderSampleGroup.vue、QcWorkOrderSingleCard.vue、index.vue、OutboundQcViewDialog.vue
工具 复用 formatMoneyDisplay、payModeLabel、payStatusLabel、payStatusTagType

8. 后续扩展(本期不做)

  • 临床应用出库工单费用摘要(feeSummary 结构可复用 BizPilotPayment)
  • 摘要条点击跳转来源单据
  • 与「分期缴费用户预警」模块联动高亮欠费

9. 决策记录

日期 决策
2026-06-12 「费用变化」= 当前快照,不做历史轨迹
2026-06-12 入库仅 采集协议单;出库仅 基础实验
2026-06-12 列表摘要 + 查看弹窗详情,推荐实现路径