日期:2026-06-12
状态:已实现
需求确认:方案 A — 当前快照(展示合同/已收/待缴/缴费状态,不含历史轨迹、不含与质检动作的联动规则)
质检专员在 入库质检工单、出库质检工单 列表中处理工单时,需要快速了解关联来源单据上的 用户费用当前状态,避免在配置/执行质检时忽略欠费、待确认收款等情况。
| Tab | 来源 | 系统标识 | 是否展示费用 |
|---|---|---|---|
| 入库质检 | 采集协议单(细胞存储) | receptionSource = cell_storage |
是 |
| 入库质检 | 捐赠协议单(科研捐赠) | receptionSource = research_donation |
否(业务无缴费) |
| 出库质检 | 中试服务申请单 | bizType = pilot_service |
否(渠道扣费) |
| 出库质检 | 基础实验 | bizType = basic_experiment |
是 |
| 出库质检 | 临床应用 | bizType = clinical_application |
否(本期不做,架构预留) |
thirdPartyFeeTotal、任务 feeAmount)——与用户协议/申请单费用分区展示,避免混淆formatMoneyDisplay;状态用 el-tagpaymentSummary)数据来源: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 |
优先级从高到低:
tfqrz(退费确认中)→ Tag warning,文案「退费确认中」ytf 且无其他未完结明细 → Tag info,文案「已退费」pendingAmount <= 0 且不存在 dfj/jfqrz → Tag success,文案「已缴清」jfqrz → Tag warning,文案「收款确认中」dfj → Tag info,文案「待缴费」info,文案「—」分期场景下 statusHint 附加明细计数,例如:「分期 · 2 期已缴 / 1 期待缴费」。
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,摘要展示「暂无缴费记录」。
接口:现有 GET /web/admin/qc-work-orders(list_work_orders)
在 _enrich_list_items 流程末尾批量 enrich,避免 N+1:
workOrderType=inbound)collectionProtocolId(且 receptionSource === cell_storage)paymentSummary(非 cell_storage 时为 null)workOrderType=outbound)bizType === basic_experiment 的 infusionScheduleIdBizPilotPayment + schedule.amountfeeSummary(非 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]: ...
paymentSummary + paymentDetails(明细列表,供弹窗展开;可限制最多返回当前协议全部明细,通常 ≤ 分期期数)get_outbound_qc_detail 增加 feeSummary;OutboundQcViewDialog 已有 schedule 区块,补充缴费只读段本期 最低交付:列表摘要字段;详情弹窗至少与列表同级信息,推荐附带 1~3 条关键明细(分期时下一期应缴)。
无需 DDL。只读 join 现有表:
biz_protocol_payment / biz_protocol_payment_detailbiz_pilot_payment / biz_pilot_infusion_schedule文件: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 区分overallPayStatusLabel + 语义色组内多工单:费用按 collectionProtocolId 去重,同协议只展示一份(组 context 已按样本聚合)。
文件: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
| 场景 | 文件 | 新增区块 |
|---|---|---|
| 入库(cell_storage) | index.vue viewSections |
「协议缴费信息」:模式、合同、已收、待收、状态 Tag;分期时列 1 行下一期应缴(如有) |
| 出库(basic_experiment) | OutboundQcViewDialog.vue |
「缴费信息」:售卖、应缴、实收、状态;可复用 ClinicalPaymentSubmitReadonly 精简字段 |
跳转链接(可选 P1):
/#/cell-storage/protocol 并打开对应协议查看弹窗(需协议 id)applicationId 若列表已有)文件:index.vue 出库 Tab 筛选「业务类型」
补全选项:
<el-option label="基础实验" value="basic_experiment" />
与三种出库来源一致。
文件: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
若入库/出库摘要条样式一致,可抽 QcWorkOrderFeeSummaryBar.vue(props: variant: 'inbound-protocol' | 'outbound-basic' + summary object)。非必须,首版可在两处各写 scoped 块,YAGNI。
| 情况 | 展示 |
|---|---|
| 科研捐赠入库工单 | 不渲染费用区块 |
| 中试/临床出库工单 | 不渲染费用区块 |
| 采集协议无 payment 主表 | 摘要:「暂无缴费信息」 |
| 基础实验无 payment 记录 | 摘要:「暂无缴费记录」;仍展示售卖金额(若有) |
| 金额为 null | 显示 — |
只读:摘要区无编辑、无确认收款按钮。
| 层级 | 文件 |
|---|---|
| 后端 | 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 |
feeSummary 结构可复用 BizPilotPayment)| 日期 | 决策 |
|---|---|
| 2026-06-12 | 「费用变化」= 当前快照,不做历史轨迹 |
| 2026-06-12 | 入库仅 采集协议单;出库仅 基础实验 |
| 2026-06-12 | 列表摘要 + 查看弹窗详情,推荐实现路径 |