# 生产工单 · 缴费信息展示设计 > 日期:2026-06-13 > 状态:已实现 > 关联:`docs/superpowers/specs/2026-06-12-qc-work-order-fee-summary-design.md`(质检工单费用摘要,已实现) --- ## 1. 背景与目标 ### 1.1 业务诉求 生产部门在处理 **生产工单** 时需要快速了解关联来源单据的 **用户缴费状态**,以便安排生产任务前判断客户是否已缴费、是否存在待确认收款等情况。 客户提出:在 **web-admin → 生产工单** 三个 Tab(入库 / 出库 / 临时)中展示缴费信息,规则与业务来源对齐。 ### 1.2 展示范围(客户确认口径) | Tab | 来源 | 系统标识 | 是否展示缴费 | |-----|------|----------|-------------| | 入库生产工单 | 细胞存储(采集协议) | `receptionSource = cell_storage` | **是** — 协议缴费 | | 入库生产工单 | 科研捐赠 | `receptionSource = research_donation` | **否** — 无用户缴费 | | 出库生产工单 | 中试服务 | `bizType = pilot_service` | **否** — 渠道商扣费 | | 出库生产工单 | 临床应用 | `bizType = clinical_application` | **是** — 输注计划缴费 | | 出库生产工单 | 基础实验 | `bizType = basic_experiment` | **是** — 输注计划缴费 | | 临时生产工单 | — | `workOrderType = temporary` | **否** | ### 1.3 非目标 - 不编辑、不确认收款/退款(只读快照) - 不根据缴费状态阻断生产流程 - 不展示生产任务自身成本、物料费用 - 不在本期重构质检工单 UI(除非为保持一致性同步扩展临床应用) --- ## 2. 现状分析 ### 2.1 生产工单前端 | 项 | 现状 | |----|------| | 入口 | `web-admin/src/views/production/production-work-order/index.vue` | | 列表 | `ProductionWorkOrderCardList.vue` — 卡片内联 markup,无独立 SingleCard | | 详情 | `SystemViewDialog` + `viewSections`,出库复用 `buildOutboundPilotContextSections` | | API 类型 | `web-admin/src/api/production-work-order.ts` — `ProductionWorkOrderItem` **无** `paymentSummary` / `feeSummary` | | 费用 UI | **无** | 与质检工单差异:入库 **无样本组聚合**,每条工单独立一张卡片(费用按工单展示即可,无需组内去重)。 ### 2.2 生产工单后端 | 项 | 现状 | |----|------| | 列表/详情 CRUD | `backend/app/crud/production_work_order.py` | | 列表 enrich | 已复用 QC 的 `_enrich_work_order_list_items`(协议状态、接收时间等) | | 费用 enrich | **未调用** `enrich_work_order_fee_summaries` | 工单 dict 已含费用 enrichment 所需字段:`collectionProtocolId`、`receptionId`、`protocolNo`、`receptionSource`、`infusionScheduleId`、`bizType`、`workOrderType`。 ### 2.3 可复用能力(质检工单已实现) | 层级 | 路径 | 说明 | |------|------|------| | 后端服务 | `backend/app/services/qc_work_order_fee_summary.py` | `build_protocol_payment_summaries`、`build_basic_experiment_fee_summaries`、`enrich_work_order_fee_summaries` | | 入库 UI | `QcWorkOrderProtocolPaymentPanel.vue` | 协议费用;列表 `compact` 模式(横向 chip 明细) | | 出库 UI | `QcWorkOrderFeeSummaryBar.vue` | 单行摘要:售卖 / 应缴 / 实收 / 状态 Tag | | API 类型 | `web-admin/src/api/qc-work-order.ts` | `QcProtocolPaymentSummary`、`QcBasicExperimentFeeSummary` | ### 2.4 与质检工单的规则差异(需扩展) 质检工单设计(2026-06-12)出库 **仅** `basic_experiment` 展示 `feeSummary`,**临床应用本期不做**。 客户本次需求明确要求 **临床应用也要展示**,数据源同为: - `biz_pilot_infusion_schedule.amount`(售卖/计划金额) - `biz_pilot_payment`(按 `infusion_schedule_id` 关联,`biz_type` 区分 clinical / basic_experiment) `build_basic_experiment_fee_summary()` 逻辑与业务类型无关,**扩展 enrich 规则即可复用**,无需新表或新计算口径。 --- ## 3. 方案对比 ### 方案 A:接线复用 + 扩展临床应用(推荐) **做法** 1. 生产 CRUD 列表/详情末尾调用现有 `enrich_work_order_fee_summaries` 2. 将 enrich 中出库 `schedule_ids` 收集条件从仅 `basic_experiment` 扩展为 `basic_experiment | clinical_application` 3. 前端 `ProductionWorkOrderCardList` 嵌入已有 QC 费用组件;详情弹窗补充只读区块 4. API 类型增加可选 `paymentSummary?`、`feeSummary?`(类型 import 自 qc-work-order 或抽到共享 types) **优点**:改动小、与质检数据口径一致、上线快 **缺点**:组件命名仍带 `Qc` 前缀(可后续抽公共,非阻塞) ### 方案 B:仅生产前端 mock / 独立请求 **做法**:生产页单独调协议/缴费 API 按 id 拉取 **缺点**:N+1 查询、与列表 enrich 模式不一致、重复逻辑 — **不推荐** ### 方案 C:先抽公共模块再双端实现 **做法**:`work_order_fee_summary` 服务重命名 + `WorkOrderFeeSummaryBar` 公共组件 + 生产/质检同时改 **优点**:命名清晰、长期维护好 **缺点**:范围大、交付慢 — 适合作为 A 的后续 refactor,**不建议首版** **推荐:方案 A**,临床应用扩展写入 enrich 服务(生产与质检共用同一函数,避免口径分叉)。 --- ## 4. 详细设计 ### 4.1 后端 #### 4.1.1 列表 API **接口**:`GET /web/admin/production-work-orders`(`list_production_work_orders`) 在 `_enrich_wo_items` / `_enrich_outbound_items` 之后: ```python from app.services.qc_work_order_fee_summary import enrich_work_order_fee_summaries items = enrich_work_order_fee_summaries(db, items, tenant_no) ``` #### 4.1.2 详情 API **接口**:`GET /web/admin/production-work-orders/{id}`(`_wo_to_response_dict`) 对单条 `[out_dict]` 同样调用 `enrich_work_order_fee_summaries`,合并 `paymentSummary` / `feeSummary`。 #### 4.1.3 enrich 规则扩展 **文件**:`backend/app/services/qc_work_order_fee_summary.py` 出库 `schedule_ids` 收集条件由: ```python (x.get("bizType") or "") == "basic_experiment" ``` 改为: ```python (x.get("bizType") or "") in ("basic_experiment", "clinical_application") ``` 分配 `feeSummary` 时同步扩展。函数名 `build_basic_experiment_fee_summaries` 可保留(实现通用),文档注明适用于出库输注计划缴费。 #### 4.1.4 数据库 **无需 DDL**。只读 join 现有表(与质检工单 §3.3 相同)。 ### 4.2 前端 API 类型 **文件**:`web-admin/src/api/production-work-order.ts` ```typescript import type { QcProtocolPaymentSummary, QcBasicExperimentFeeSummary } from '@/api/qc-work-order' export interface ProductionWorkOrderItem { // ...existing paymentSummary?: QcProtocolPaymentSummary | null feeSummary?: QcBasicExperimentFeeSummary | null } ``` `feeSummary` 字段名保持不变(结构与基础实验相同);临床应用复用同一 shape。 ### 4.3 列表 UI **文件**:`ProductionWorkOrderCardList.vue` 在 `.production-work-order-card__head` 内、meta 下方(对齐质检 `QcWorkOrderSingleCard`): | 条件 | 组件 | 说明 | |------|------|------| | `activeTypeTab === 'inbound'` 且 `receptionSource === 'cell_storage'` | `QcWorkOrderProtocolPaymentPanel` `compact` | 协议费用 + 横向 chip 明细 | | `activeTypeTab === 'outbound'` 且 `bizType === 'basic_experiment'` | `QcWorkOrderFeeSummaryBar` `variant="basic-experiment"` | 单行摘要 | | `activeTypeTab === 'outbound'` 且 `bizType === 'clinical_application'` | `QcWorkOrderFeeSummaryBar` `variant="basic-experiment"` | 同上(数据来自 `feeSummary`) | | 科研捐赠 / 中试 / 临时 | 不渲染 | — | 样式:复用 `.qc-work-order-card__fee-in-head` 类名或等效 scoped 规则(负边距撑满卡片宽)。 ### 4.4 查看弹窗 **文件**:`production-work-order/index.vue` | 场景 | 做法 | |------|------| | 入库 cell_storage | `viewSections` 增加 slot `#protocol-payment-details`,嵌入 `QcWorkOrderProtocolPaymentPanel` `embedded`(完整表格明细) | | 出库 basic_experiment / clinical_application | 在 `buildOutboundPilotContextSections` 之后追加「缴费信息」section,字段:售卖、应缴、实收、缴费状态 Tag(与 `OutboundQcViewDialog` 基础实验段一致) | 只读,无操作按钮。 ### 4.5 空态 | 情况 | 展示 | |------|------| | 科研捐赠入库 | 不渲染费用区 | | 中试出库 | 不渲染 | | 临时工单 | 不渲染 | | 细胞存储无 payment 主表 | 「暂无缴费信息」 | | 临床/基础实验无 payment 记录 | 摘要「暂无缴费记录」;仍可展示售卖金额(若有 schedule.amount) | --- ## 5. 待决策项 | # | 问题 | 选项 | 建议 | |---|------|------|------| | D1 | 临床应用费用是否 **同步** 展示在质检工单出库 Tab? | A. 仅生产工单 / B. 生产 + 质检同步 | **B** — enrich 已共用,不同步会造成两模块口径不一致 | | D2 | 首版交付范围 | A. 仅列表 / B. 列表 + 查看弹窗 | **B** — 与质检工单交付一致,生产人员查看详情时也需要 | | D3 | 组件命名 | A. 直接 import Qc 前缀组件 / B. 本期抽 `WorkOrderFee*` 公共组件 | **A** 首版,**B** 作后续 refactor | **客户确认(2026-06-13)**:D1 = **B**(生产 + 质检同步临床应用缴费展示)。 --- ## 6. 测试要点 ### 6.1 入库 - [ ] 细胞存储卡片展示协议费用 compact 摘要,数值与采集协议单一致 - [ ] 科研捐赠卡片不展示费用 - [ ] 趸交 / 分期 / 待缴费 / 收款确认中 Tag 正确 ### 6.2 出库 - [ ] 基础实验卡片展示 `feeSummary` 单行摘要 - [ ] **临床应用**卡片展示 `feeSummary`(扩展后) - [ ] 中试服务卡片不展示费用 - [ ] 数值与临床应用/基础实验缴费模块一致 ### 6.3 临时 - [ ] 无费用区块 ### 6.4 回归 - [ ] 生产任务子列表、派发质检、完成工单不受影响 - [ ] 质检工单费用展示不受回归(若扩展 enrich 规则,需补临床 QC 用例) --- ## 7. 影响文件清单(预估) | 层级 | 文件 | |------|------| | 后端服务 | `backend/app/services/qc_work_order_fee_summary.py`(扩展 bizType 规则) | | 后端 CRUD | `backend/app/crud/production_work_order.py` | | 前端 API | `web-admin/src/api/production-work-order.ts` | | 前端列表 | `ProductionWorkOrderCardList.vue` | | 前端详情 | `production-work-order/index.vue` | | 可选同步 | `QcWorkOrderSingleCard.vue`(临床 outbound 展示 feeSummary) | | 复用 | `QcWorkOrderProtocolPaymentPanel.vue`、`QcWorkOrderFeeSummaryBar.vue` | --- ## 8. 工作量评估 | 任务 | 复杂度 | 说明 | |------|--------|------| | 后端 enrich 接线 | 低 | 2 处调用 + 1 处 bizType 条件扩展 | | 前端类型 + 列表 UI | 低 | 复用 QC 组件,条件判断 ~30 行 | | 详情弹窗 | 中 | 入库 slot + 出库 section | | 质检临床同步(若选 D1=B) | 低 | SingleCard 增加 `clinical_application` 条件 | | **合计** | **小~中** | 预计 0.5~1 人日 | --- ## 9. 决策记录 | 日期 | 决策 | |------|------| | 2026-06-13 | 需求来源:客户希望在生产工单三 Tab 中展示缴费快照 | | 2026-06-13 | 入库仅细胞存储;出库中试不展示;临床 + 基础实验展示 | | 2026-06-13 | 推荐方案 A:复用 QC fee enrich + UI 组件,扩展 clinical_application | | 2026-06-13 | **已确认 D1=B**:质检工单出库 Tab 同步展示临床应用 `feeSummary` |