提交
This commit is contained in:
1 parent
ad8f8d696f
commit
41aa90aa05
115 files changed
+29626
-10210
No files matched your search
@@ -0,0 +1,23 @@
|
||||
-- 输注计划管理 · 出库交接
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `biz_outbound_handover` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '输注计划ID',
|
||||
`handover_user_id` varchar(64) DEFAULT NULL COMMENT '交接人用户ID',
|
||||
`handover_person` varchar(64) DEFAULT NULL COMMENT '交接人姓名快照',
|
||||
`handover_time` datetime DEFAULT NULL COMMENT '交接时间',
|
||||
`handover_location` varchar(200) DEFAULT NULL COMMENT '交接地点',
|
||||
`receiver_person` varchar(200) DEFAULT NULL COMMENT '接收人(文本)',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '交接备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_oh_schedule` (`infusion_schedule_id`, `del_flag`),
|
||||
KEY `idx_oh_tenant` (`tenant_no`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='输注计划-出库交接';
|
||||
@@ -0,0 +1,71 @@
|
||||
-- 出库放行审核 · 出库物料准备(台账,不联动库存)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `biz_outbound_release_material` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '输注计划ID',
|
||||
`paper_attachments` varchar(4000) DEFAULT NULL COMMENT '其他纸质材料附件(逗号分隔URL)',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_orm_schedule` (`infusion_schedule_id`, `del_flag`),
|
||||
KEY `idx_orm_tenant` (`tenant_no`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库放行-出库物料准备主表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `biz_outbound_release_cell` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`material_id` varchar(64) NOT NULL COMMENT '关联 biz_outbound_release_material.id',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '输注计划ID',
|
||||
`cell_product_id` varchar(64) NOT NULL COMMENT '细胞产品ID',
|
||||
`product_code` varchar(128) DEFAULT NULL COMMENT '产品编码快照',
|
||||
`product_name` varchar(200) DEFAULT NULL COMMENT '产品名称快照',
|
||||
`product_type_name` varchar(100) DEFAULT NULL COMMENT '产品类型快照',
|
||||
`spec_name` varchar(100) DEFAULT NULL COMMENT '规格快照',
|
||||
`passage` varchar(32) DEFAULT NULL COMMENT '代次快照',
|
||||
`batch_no` varchar(64) DEFAULT NULL COMMENT '批次号快照',
|
||||
`storage_slot_name` varchar(200) DEFAULT NULL COMMENT '储物格快照',
|
||||
`product_status` varchar(32) DEFAULT NULL COMMENT '保存时状态快照',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_orc_schedule_product` (`infusion_schedule_id`, `cell_product_id`, `del_flag`),
|
||||
KEY `idx_orc_product` (`cell_product_id`, `del_flag`),
|
||||
KEY `idx_orc_material` (`material_id`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库放行-出库细胞(台账)';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `biz_outbound_release_qc_material` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`material_id` varchar(64) NOT NULL COMMENT '关联 biz_outbound_release_material.id',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '输注计划ID',
|
||||
`qc_material_id` varchar(64) NOT NULL COMMENT '质检物料ID',
|
||||
`qty` decimal(18,4) NOT NULL COMMENT '计划用量(台账)',
|
||||
`material_code` varchar(64) DEFAULT NULL COMMENT '物料编码快照',
|
||||
`material_name` varchar(200) DEFAULT NULL COMMENT '物料名称快照',
|
||||
`spec_model` varchar(200) DEFAULT NULL COMMENT '规格型号快照',
|
||||
`unit` varchar(32) DEFAULT NULL COMMENT '单位快照',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_orqm_schedule_material` (`infusion_schedule_id`, `qc_material_id`, `del_flag`),
|
||||
KEY `idx_orqm_material` (`material_id`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库放行-质检物料(台账)';
|
||||
@@ -0,0 +1,22 @@
|
||||
-- 出库放行审核 · 其他纸质材料子表(逐条:名称、份数、附件)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `biz_outbound_release_paper` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`material_id` varchar(64) NOT NULL COMMENT '关联 biz_outbound_release_material.id',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '输注计划ID',
|
||||
`paper_name` varchar(200) NOT NULL COMMENT '材料名称',
|
||||
`copies` int(11) NOT NULL DEFAULT 1 COMMENT '份数',
|
||||
`attachment_url` varchar(2000) DEFAULT NULL COMMENT '附件URL',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_orp_schedule` (`infusion_schedule_id`, `del_flag`),
|
||||
KEY `idx_orp_material` (`material_id`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库放行-其他纸质材料(台账)';
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 出库放行审核表新增审核附件和审核人姓名字段
|
||||
ALTER TABLE biz_outbound_release_review
|
||||
ADD COLUMN review_attachments VARCHAR(4000) NULL COMMENT '审核附件(逗号分隔URL)' AFTER review_remark,
|
||||
ADD COLUMN review_by_name VARCHAR(64) NULL COMMENT '审核人姓名' AFTER reviewer_user_id;
|
||||
@@ -0,0 +1,349 @@
|
||||
# 输注计划管理 · 出库交接设计
|
||||
|
||||
> 日期: 2026-05-27
|
||||
> 状态: 已实现
|
||||
> 父模块: 中试服务管理 → 输注计划管理
|
||||
> 关联: `2026-05-27-outbound-release-material-design.md`(出库物料台账)、`outbound-release-review`
|
||||
> **库存策略(已确认)**: **台账模式 — 与出库物料准备一致,保存时不联动库存**
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 现状
|
||||
|
||||
- **出库放行审核**页已实现「出库物料准备」:按输注计划登记细胞 / 质检物料 / 纸质材料(台账,不扣库存)。
|
||||
- **输注计划管理**页仅有编辑、确认、完成、取消、查看,无出库相关操作。
|
||||
- 全库尚无独立的「出库交接」记录表与 API。
|
||||
|
||||
### 目标
|
||||
|
||||
在**输注计划管理**列表操作列新增「**出库交接**」按钮。点击后打开弹窗:
|
||||
|
||||
| 区块 | 行为 |
|
||||
|------|------|
|
||||
| 出库物料 | 展示并可编辑(与「出库物料准备」相同三区 + 物料备注) |
|
||||
| 交接信息 | 填写交接人、交接时间、交接地点、接收人、备注 |
|
||||
| 保存 | **一次提交**,同时保存物料台账 + 交接记录 |
|
||||
|
||||
### 非目标
|
||||
|
||||
- 不改动细胞产品状态、不扣质检物料库存(沿用台账策略)。
|
||||
- v1 **不自动修改**输注计划 `schedule_status` / `infusion_status`(避免与「完成」结算逻辑冲突)。
|
||||
- 不与中试申请单「交付方式 / 交接温度」合并。
|
||||
|
||||
---
|
||||
|
||||
## 2. 已确认的产品规则
|
||||
|
||||
| 项 | 决策 |
|
||||
|----|------|
|
||||
| **交接人** | 默认当前登录用户;`el-select` 远程搜索 `sys_user`(参考 `dept/index.vue` 负责人选择器);存 `handoverUserId` + 姓名快照 `handoverPerson` |
|
||||
| **接收人** | 自由文本 `el-input`,存 `receiverPerson` |
|
||||
| **交接时间** | 必填;`el-date-picker` type=`datetime`;**首次打开且无历史记录时默认此刻**(客户端 `new Date()`,格式 `YYYY-MM-DD HH:mm:ss`) |
|
||||
| **交接地点** | 可选文本 |
|
||||
| **备注** | 可选文本(交接区块独立备注,与物料 remark 分开) |
|
||||
| **再次编辑** | **已交接后仍可再次打开「出库交接」编辑并 upsert**(非只读、非一次性锁定) |
|
||||
| **查看弹窗** | **输注计划「查看」弹窗须只读展示交接信息**(见 §6.5) |
|
||||
| **按钮可见性** | `infusionStatus !== 'yqx'`(已取消计划不显示) |
|
||||
| **保存后状态** | v1 不改输注/生产状态 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 架构决策
|
||||
|
||||
| 项 | 决策 |
|
||||
|----|------|
|
||||
| 方案 | **方案 A**:独立交接表 + 组合弹窗 + 单事务保存 |
|
||||
| UI 模式 | **P22** 审核/子表编辑弹窗 + 表单区(参考 `OutboundReleaseMaterialDialog` + `ScheduleEditDialog`) |
|
||||
| 物料数据 | 复用 `biz_outbound_release_material` 及 cells / qc / paper 子表(与放行审核共用) |
|
||||
| 交接数据 | 新建 `biz_outbound_handover`(1:1 输注计划) |
|
||||
| API 归属 | 挂在 **输注计划管理** 路由 `/web/admin/pilot-infusion-schedule-mgmt/` |
|
||||
| 组件复用 | 从 `OutboundReleaseMaterialDialog.vue` 抽取 `OutboundMaterialFormSections.vue` 公共组件 |
|
||||
|
||||
### 与出库物料准备的关系
|
||||
|
||||
```
|
||||
出库放行审核 ──「出库物料」──┐
|
||||
├──► biz_outbound_release_material (+ 子表)
|
||||
输注计划管理 ──「出库交接」──┘ │
|
||||
└──► biz_outbound_handover(仅交接页写入)
|
||||
```
|
||||
|
||||
两处编辑**同一份物料台账**;交接页额外写入交接元数据。
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据库设计
|
||||
|
||||
SQL 文件:`docs/sql/outbound_handover.sql`
|
||||
|
||||
### 4.1 `biz_outbound_handover`(交接主表,1:1 输注计划)
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS `biz_outbound_handover` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '输注计划ID',
|
||||
`handover_user_id` varchar(64) DEFAULT NULL COMMENT '交接人用户ID',
|
||||
`handover_person` varchar(64) DEFAULT NULL COMMENT '交接人姓名快照',
|
||||
`handover_time` datetime DEFAULT NULL COMMENT '交接时间',
|
||||
`handover_location` varchar(200) DEFAULT NULL COMMENT '交接地点',
|
||||
`receiver_person` varchar(200) DEFAULT NULL COMMENT '接收人(文本)',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
|
||||
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` varchar(512) DEFAULT NULL COMMENT '交接备注',
|
||||
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_oh_schedule` (`infusion_schedule_id`, `del_flag`),
|
||||
KEY `idx_oh_tenant` (`tenant_no`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='输注计划-出库交接';
|
||||
```
|
||||
|
||||
> 物料表结构不变,见 `docs/sql/outbound_release_material.sql`、`docs/sql/outbound_release_paper_material.sql`。
|
||||
|
||||
---
|
||||
|
||||
## 5. API 设计
|
||||
|
||||
前缀:`/web/admin/pilot-infusion-schedule-mgmt`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/{scheduleId}/outbound-handover` | 获取交接信息 + 出库物料(编辑弹窗用) |
|
||||
| PUT | `/{scheduleId}/outbound-handover` | 保存交接 + 出库物料(单事务) |
|
||||
| GET | `/detail?scheduleId=` | **扩展**:响应增加 `outboundHandover`(查看弹窗用,见 §5.4) |
|
||||
|
||||
### 5.1 GET 响应
|
||||
|
||||
```json
|
||||
{
|
||||
"scheduleSummary": {
|
||||
"applicationNo": "SQ20260527001",
|
||||
"patientName": "张三",
|
||||
"infusionTime": "2026-05-28 10:00:00"
|
||||
},
|
||||
"outboundMaterial": {
|
||||
"cells": [],
|
||||
"qcMaterials": [],
|
||||
"paperMaterials": [],
|
||||
"remark": ""
|
||||
},
|
||||
"handover": {
|
||||
"handoverUserId": "user-uuid",
|
||||
"handoverPerson": "李四",
|
||||
"handoverTime": "2026-05-27 14:30:00",
|
||||
"handoverLocation": "中试库房",
|
||||
"receiverPerson": "王五(渠道)",
|
||||
"remark": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 若无交接记录:`handover` 各字段为空;编辑弹窗前端默认 **当前用户 + 此刻**。
|
||||
- `outboundMaterial` 结构同 `GET .../outbound-materials`。
|
||||
|
||||
### 5.2 PUT Body
|
||||
|
||||
```json
|
||||
{
|
||||
"outboundMaterial": {
|
||||
"remark": "",
|
||||
"cells": [{ "cellProductId": "..." }],
|
||||
"qcMaterials": [{ "materialId": "...", "qty": 1.5 }],
|
||||
"paperMaterials": [{ "paperName": "知情同意书", "copies": 2, "attachmentUrl": "..." }]
|
||||
},
|
||||
"handover": {
|
||||
"handoverUserId": "user-uuid",
|
||||
"handoverTime": "2026-05-27 14:30:00",
|
||||
"handoverLocation": "中试库房",
|
||||
"receiverPerson": "王五",
|
||||
"remark": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `handoverPerson` **不由前端提交**,后端根据 `handoverUserId` 查 `sys_user.nick_name` 写入快照。
|
||||
- 校验:`handoverUserId`、`handoverTime`、`receiverPerson` 必填;物料校验复用 `save_outbound_material` 规则。
|
||||
|
||||
### 5.3 保存流程(单事务)
|
||||
|
||||
```
|
||||
save_outbound_handover(schedule_id, payload):
|
||||
1. 校验输注计划存在 + 租户 + 数据权限
|
||||
2. 校验 handover 必填字段;handoverUserId 对应用户存在
|
||||
3. 调用 save_outbound_material(...) — 全量替换物料台账
|
||||
4. upsert biz_outbound_handover(写 handover_person 快照)
|
||||
5. db.commit()
|
||||
```
|
||||
|
||||
### 5.4 扩展输注计划 Detail(查看弹窗)
|
||||
|
||||
现有 `GET /web/admin/pilot-infusion-schedule-mgmt/detail` 响应增加:
|
||||
|
||||
```json
|
||||
{
|
||||
"...": "原有 schedule / application / patient / products 字段",
|
||||
"outboundHandover": {
|
||||
"handoverPerson": "李四",
|
||||
"handoverTime": "2026-05-27 14:30:00",
|
||||
"handoverLocation": "中试库房",
|
||||
"receiverPerson": "王五",
|
||||
"remark": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 无交接记录时:`outboundHandover` 为 `null` 或各字段空;查看弹窗 Section 仍展示,字段为 `—`。
|
||||
- 查看弹窗**仅展示交接五字段**,不在此版扩展出库物料只读区(物料仍通过「出库交接」编辑弹窗维护)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 前端设计
|
||||
|
||||
### 6.1 页面改动
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `infusion-schedule-management/index.vue` | 操作列「出库交接」;`min-width` 调至 ≥ 500px |
|
||||
| `InfusionScheduleHandoverDialog.vue` | **新建**组合弹窗 |
|
||||
| `ScheduleViewDialog.vue` | 新增「出库交接」只读 Section |
|
||||
| `components/business/outbound-material-form-sections/index.vue` | **新建**(从 MaterialDialog 抽取) |
|
||||
| `OutboundReleaseMaterialDialog.vue` | 改为引用公共组件(可选,建议同期重构) |
|
||||
| `api/admin.ts` 或 `api/infusion-schedule-handover.ts` | fetch/save API |
|
||||
|
||||
### 6.2 弹窗结构
|
||||
|
||||
```
|
||||
标题:出库交接
|
||||
宽度:1120px(与出库物料准备一致)
|
||||
class:dialog-form-layout
|
||||
|
||||
┌─ 摘要条 ─────────────────────────────────────┐
|
||||
│ 申请单号 / 患者姓名 / 输注时间 │
|
||||
├─ OutboundMaterialFormSections ──────────────┤
|
||||
│ 出库细胞 | 使用的物料 | 其他纸质材料 | 物料备注 │
|
||||
├─ 交接信息 (form-grid-2, label-position=top) ─┤
|
||||
│ 交接人 [用户搜索 select,默认当前用户] │
|
||||
│ 交接时间 [datetime picker,默认 now] │
|
||||
│ 交接地点 [text] │
|
||||
│ 接收人 [text,必填] │
|
||||
│ 备注 [textarea,通栏] │
|
||||
├─ footer: 取消 / 保存 ─────────────────────────┤
|
||||
```
|
||||
|
||||
### 6.3 交接人选择器
|
||||
|
||||
- 复用 `fetchUserList({ keyword, status: '1' })` 远程搜索。
|
||||
- 打开弹窗时:
|
||||
- 若 GET 返回已有 `handoverUserId` → 回填;
|
||||
- 否则默认 `useUserStore().info.userId` / `nickName`,并写入 options 首项。
|
||||
- 选项 label:`nickName · userNo · phoneNo`(与部门负责人选择器一致)。
|
||||
- `@change` 时更新本地 `handoverUserId`;展示名由后端快照为准。
|
||||
|
||||
### 6.4 交互规则
|
||||
|
||||
- 保存成功:`ElMessage.success('出库交接已保存')`。
|
||||
- 物料区校验、附件上传、细胞选择逻辑与 `OutboundReleaseMaterialDialog` 完全一致。
|
||||
- 取消关闭不保存;destroy-on-close 重置状态。
|
||||
- **已保存过的计划**:再次点「出库交接」正常打开并回填;保存为 upsert,不提示「已完成不可改」。
|
||||
|
||||
### 6.5 查看弹窗(`ScheduleViewDialog.vue`)
|
||||
|
||||
在现有 `SystemViewDialog` sections 中,于「产品列表」与「其他信息」之间插入:
|
||||
|
||||
```text
|
||||
Section 标题:出库交接
|
||||
fields(只读 ViewField):
|
||||
交接人 handoverPerson
|
||||
交接时间 handoverTime
|
||||
交接地点 handoverLocation
|
||||
接收人 receiverPerson
|
||||
备注 remark(span: true)
|
||||
```
|
||||
|
||||
- 数据来源:`fetchInfusionScheduleMgmtDetail` 返回的 `outboundHandover`(后端 detail 聚合查询 `biz_outbound_handover`)。
|
||||
- 无记录:该 Section 仍展示,各字段值为 `—`,或在 Section 内用一行 muted 文案「暂无出库交接记录」(与 `SystemViewDialog` 空值展示习惯一致,实现时二选一,推荐字段 `—` + section 始终可见)。
|
||||
- 布局:`grid-columns` 保持与现有一致(3 列);备注 `span: true`。
|
||||
|
||||
---
|
||||
|
||||
## 7. 后端模块划分
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `app/crud/outbound_handover.py` | GET/PUT 聚合、upsert 交接、按 schedule 查询交接(供 detail 用) |
|
||||
| `app/crud/pilot_infusion_schedule_mgmt.py` | detail 聚合 `outboundHandover` |
|
||||
| `app/schemas/outbound_handover.py` | Pydantic |
|
||||
| `app/api/pilot_infusion_schedule_mgmt.py` | outbound-handover 两个端点 + detail 扩展 |
|
||||
| `app/models/models.py` | `BizOutboundHandover` ORM |
|
||||
|
||||
**不修改:** `cell_product`、`qc_material_usage` 库存逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 8. 权限
|
||||
|
||||
- 建议菜单/按钮权限:`pilot-service:infusion-schedule-management:handover`
|
||||
- v1 可与列表 edit 权限共用(`require_admin_or_super_admin`),上线前按产品确认。
|
||||
|
||||
---
|
||||
|
||||
## 9. 错误处理
|
||||
|
||||
| 场景 | HTTP | 说明 |
|
||||
|------|------|------|
|
||||
| 输注计划不存在 | 404 | — |
|
||||
| 交接人用户不存在 | 400 | handoverUserId 无效 |
|
||||
| 接收人为空 | 400 | — |
|
||||
| 交接时间为空 | 400 | — |
|
||||
| 物料校验失败 | 400 | 复用现有物料错误文案 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 测试要点
|
||||
|
||||
1. 首次保存:物料 + 交接均写入;交接人快照与所选用户 nickName 一致。
|
||||
2. 再次打开:物料与交接均正确回填;交接人 select 显示已选用户。
|
||||
3. 新建弹窗默认:交接人 = 当前用户,交接时间 = **打开弹窗此刻**。
|
||||
4. 已交接计划:再次打开编辑弹窗可修改并保存;查看弹窗展示最新交接信息。
|
||||
5. 查看弹窗:有交接记录时五字段正确展示;无记录时 Section 可见且为空态。
|
||||
6. 放行审核页改动物料后,交接页打开应看到最新物料。
|
||||
7. 交接页改动物料后,放行审核页「出库物料」应看到同步结果。
|
||||
8. 已取消计划(`yqx`)不显示「出库交接」按钮。
|
||||
9. 保存后:`biz_cell_product` / `biz_qc_material.stock_qty` **不变**。
|
||||
|
||||
---
|
||||
|
||||
## 11. OpenSpec 增量(建议)
|
||||
|
||||
- `infusion-schedule-outbound-handover-api`: GET/PUT outbound-handover
|
||||
- `infusion-schedule-outbound-handover-ui`: 列表按钮 + 组合弹窗 + 物料组件抽取
|
||||
|
||||
---
|
||||
|
||||
## 12. 工作量估算
|
||||
|
||||
| 项 | 人天 |
|
||||
|----|------|
|
||||
| SQL + Model + CRUD + API | 0.5 |
|
||||
| 抽取物料公共组件 + 交接弹窗 | 1.0 |
|
||||
| 查看弹窗 Section + detail 聚合 | 0.25 |
|
||||
| 列表入口 + 联调 | 0.5 |
|
||||
| **合计** | **~2.25** |
|
||||
|
||||
---
|
||||
|
||||
## 附录:决策记录
|
||||
|
||||
| 日期 | 议题 | 决策 |
|
||||
|------|------|------|
|
||||
| 2026-05-27 | 交接人 / 接收人 | 交接人:默认当前用户 + 用户表搜索;接收人:文本 |
|
||||
| 2026-05-27 | 数据模型 | 独立 `biz_outbound_handover`,物料共用现有表 |
|
||||
| 2026-05-27 | 保存语义 | 单事务:物料 + 交接一并保存 |
|
||||
| 2026-05-27 | 状态联动 | v1 不改 schedule/infusion 状态 |
|
||||
| 2026-05-27 | 交接时间 | 首次打开默认此刻 |
|
||||
| 2026-05-27 | 编辑策略 | 已交接后可再次编辑(upsert) |
|
||||
| 2026-05-27 | 查看弹窗 | 输注计划 ScheduleViewDialog 展示交接五字段 |
|
||||
@@ -0,0 +1,284 @@
|
||||
# 出库放行审核 · 出库物料准备(台账)设计
|
||||
|
||||
> 日期: 2026-05-27
|
||||
> 状态: 待审阅
|
||||
> 父模块: 中试服务管理 → 出库放行审核
|
||||
> 关联: `openspec/changes/outbound-release-review`、方案 A(独立弹窗 + 子表)
|
||||
> **库存策略(已确认)**: **台账模式 — 出库物料准备界面仅记录,不联动库存**
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 现状
|
||||
|
||||
- 出库放行审核列表/查看/审核已实现,审核记录存于 `biz_outbound_release_review`。
|
||||
- 每条输注计划尚无「出库物料准备」维护入口。
|
||||
- 细胞产品库、质检物料库已有独立库存/状态管理(生产任务、质检任务等场景会真实扣减)。
|
||||
|
||||
### 目标
|
||||
|
||||
针对**每条输注计划**,提供「**出库物料准备**」界面,用于登记拟出库内容(台账),**保存时不改变细胞产品状态、不扣质检物料库存**:
|
||||
|
||||
| 区块 | 数据来源 | 保存行为 |
|
||||
|------|----------|----------|
|
||||
| 出库细胞 | 细胞产品库勾选 | 写入关联 ID + 字段快照,**不改** `biz_cell_product` |
|
||||
| 使用的物料 | 质检物料库勾选 + 用量 | 写入关联 ID + 用量 + 快照,**不扣** `stock_qty` |
|
||||
| 其他纸质材料 | 附件上传 | 仅存 URL |
|
||||
|
||||
### 非目标
|
||||
|
||||
- **不在本界面做真实出库**(不改细胞状态、不释库位、不写 `biz_qc_material_usage`)。
|
||||
- 不与「交付物料清单」(`biz_pilot_delivery_material`)合并或自动同步。
|
||||
- 批量审核弹窗不涉及出库物料(仍按条维护)。
|
||||
- 真实出库若后续需要,另开能力/节点实现(本 spec 不覆盖)。
|
||||
|
||||
## 2. 架构决策
|
||||
|
||||
| 项 | 决策 |
|
||||
|----|------|
|
||||
| UI 定位 | 「出库物料**准备**」— 台账登记,非库存事务 |
|
||||
| UI | 列表操作列「出库物料」→ `OutboundReleaseMaterialDialog.vue` |
|
||||
| 数据 | 1 主表 + 2 子表;纸质附件放主表 |
|
||||
| 保存语义 | **全量替换**:软删旧子表行 → 写入新行 + 主数据快照 |
|
||||
| 库存 | **零联动**;前端可展示当前库存/状态作参考,保存时不校验库存是否足够 |
|
||||
| 复杂度 | 无 revert/apply、无 `qc_material_usage` 扩展、无库位占用变更 |
|
||||
|
||||
## 3. 数据库设计
|
||||
|
||||
SQL 文件:`docs/sql/outbound_release_material.sql`
|
||||
|
||||
### 3.1 `biz_outbound_release_material`(主表,1:1 输注计划)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `biz_outbound_release_material` (
|
||||
`id` varchar(64) NOT NULL COMMENT '主键ID',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '输注计划ID',
|
||||
`paper_attachments` varchar(4000) DEFAULT NULL COMMENT '其他纸质材料附件(逗号分隔URL)',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw',
|
||||
`order_num` int(11) DEFAULT NULL,
|
||||
`remark` varchar(512) DEFAULT NULL,
|
||||
`create_by` varchar(64) DEFAULT NULL,
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_by` varchar(64) DEFAULT NULL,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_orm_schedule` (`infusion_schedule_id`, `del_flag`),
|
||||
KEY `idx_orm_tenant` (`tenant_no`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库放行-出库物料准备主表';
|
||||
```
|
||||
|
||||
### 3.2 `biz_outbound_release_cell`(出库细胞行)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `biz_outbound_release_cell` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`material_id` varchar(64) NOT NULL COMMENT '关联 biz_outbound_release_material.id',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL COMMENT '冗余,便于查询',
|
||||
`cell_product_id` varchar(64) NOT NULL COMMENT '细胞产品ID',
|
||||
`product_code` varchar(128) DEFAULT NULL COMMENT '保存时快照',
|
||||
`product_name` varchar(200) DEFAULT NULL,
|
||||
`product_type_name` varchar(100) DEFAULT NULL,
|
||||
`spec_name` varchar(100) DEFAULT NULL,
|
||||
`passage` varchar(32) DEFAULT NULL,
|
||||
`batch_no` varchar(64) DEFAULT NULL,
|
||||
`storage_slot_name` varchar(200) DEFAULT NULL,
|
||||
`product_status` varchar(32) DEFAULT NULL COMMENT '保存时状态快照(只读展示)',
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw',
|
||||
`order_num` int(11) DEFAULT NULL,
|
||||
`remark` varchar(512) DEFAULT NULL,
|
||||
`create_by` varchar(64) DEFAULT NULL,
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_by` varchar(64) DEFAULT NULL,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_orc_schedule_product` (`infusion_schedule_id`, `cell_product_id`, `del_flag`),
|
||||
KEY `idx_orc_product` (`cell_product_id`, `del_flag`),
|
||||
KEY `idx_orc_material` (`material_id`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库放行-出库细胞(台账)';
|
||||
```
|
||||
|
||||
> 不需要 `product_status_before` / `storage_slot_id` 回滚字段。
|
||||
|
||||
### 3.3 `biz_outbound_release_qc_material`(质检物料行)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `biz_outbound_release_qc_material` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`material_id` varchar(64) NOT NULL COMMENT '关联 biz_outbound_release_material.id',
|
||||
`infusion_schedule_id` varchar(64) NOT NULL,
|
||||
`qc_material_id` varchar(64) NOT NULL,
|
||||
`qty` decimal(18,4) NOT NULL COMMENT '计划用量(台账)',
|
||||
`material_code` varchar(64) DEFAULT NULL,
|
||||
`material_name` varchar(200) DEFAULT NULL,
|
||||
`spec_model` varchar(200) DEFAULT NULL,
|
||||
`unit` varchar(32) DEFAULT NULL,
|
||||
`tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw',
|
||||
`order_num` int(11) DEFAULT NULL,
|
||||
`remark` varchar(512) DEFAULT NULL,
|
||||
`create_by` varchar(64) DEFAULT NULL,
|
||||
`create_dept_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_by` varchar(64) DEFAULT NULL,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`del_flag` tinyint(4) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_orqm_schedule_material` (`infusion_schedule_id`, `qc_material_id`, `del_flag`),
|
||||
KEY `idx_orqm_material` (`material_id`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库放行-质检物料(台账)';
|
||||
```
|
||||
|
||||
> **不**关联 `biz_qc_material_usage`;**不**扩展 `source_type`。
|
||||
|
||||
## 4. 核心业务流程
|
||||
|
||||
### 4.1 保存出库物料准备(单事务)
|
||||
|
||||
```
|
||||
save_outbound_release_material(schedule_id, payload):
|
||||
1. 校验输注计划存在 + 租户 + 数据权限
|
||||
2. 校验 payload:
|
||||
- cells 内 cell_product_id 不重复
|
||||
- qcMaterials 内 material_id 不重复
|
||||
- qty > 0
|
||||
- 关联主数据存在且未软删(产品/物料)
|
||||
3. upsert 主表(paper_attachments、remark)
|
||||
4. 软删本 schedule 下旧 cell / qc_material 子行
|
||||
5. 对每个 cell_product_id:读主数据 → 写快照 → insert 子行
|
||||
6. 对每个 qc_material + qty:读主数据 → 写快照 → insert 子行
|
||||
7. db.commit()
|
||||
```
|
||||
|
||||
**明确不做:**
|
||||
|
||||
- 不调用 `cell_product.update` / 不改 `product_status`
|
||||
- 不调用 `occupancy_svc.remove_by_item`
|
||||
- 不调用 `qc_material_usage.deduct_*`
|
||||
- 不校验质检库存是否 ≥ 用量(用量仅作计划登记)
|
||||
|
||||
### 4.2 编辑
|
||||
|
||||
- 再次保存 = 全量替换子表(软删旧行 + 插入新行)。
|
||||
- 无库存回滚逻辑。
|
||||
|
||||
### 4.3 业务校验(台账级)
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| 同计划细胞不重复 | 同一 `infusion_schedule_id` 下 `cell_product_id` 唯一 |
|
||||
| 同计划物料不重复 | 同一 schedule 下 `qc_material_id` 唯一 |
|
||||
| 跨计划可重复引用 | 不同输注计划可登记同一细胞/物料(台账互不排斥) |
|
||||
| 主数据存在性 | 保存时产品/物料须存在;已软删的主数据不允许新增引用 |
|
||||
| 细胞状态 | **不限制**可选状态;选择器展示状态供人判断,保存时不拦截 |
|
||||
|
||||
### 4.4 细胞产品选择范围
|
||||
|
||||
- 从细胞产品库分页/搜索勾选(复用 `TaskInputEnhancedTable` 选择弹窗模式)。
|
||||
- 列表展示编码、名称、类型、代次、储物格、**当前状态**(参考信息)。
|
||||
- **可选增强(v1.1)**:默认筛选与本计划出库生产工单产出相关的产品,仍允许从全库补选。
|
||||
|
||||
## 5. API 设计
|
||||
|
||||
前缀:`/web/admin/pilot-release-reviews`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/{scheduleId}/outbound-materials` | 获取出库物料准备(主表 + 细胞行 + 质检行) |
|
||||
| PUT | `/{scheduleId}/outbound-materials` | 全量保存(仅写台账) |
|
||||
|
||||
**PUT Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"paperAttachments": "url1,url2",
|
||||
"remark": "",
|
||||
"cells": [{ "cellProductId": "..." }],
|
||||
"qcMaterials": [{ "materialId": "...", "qty": 1.5, "remark": "" }]
|
||||
}
|
||||
```
|
||||
|
||||
- `cells` / `qcMaterials` 可为空数组。
|
||||
- `GET .../detail` 响应增加 `outboundMaterial` 域(查看弹窗只读)。
|
||||
|
||||
## 6. 前端设计
|
||||
|
||||
### 6.1 模式
|
||||
|
||||
- **P22** 审核列表 + 子表编辑弹窗。
|
||||
- 弹窗标题建议:**「出库物料准备」**(与「真实出库」区分)。
|
||||
- 参考:
|
||||
- 细胞选择:`TaskInputEnhancedTable.vue`
|
||||
- 质检物料:`qc-task/index.vue` 物料使用区(**去掉库存不足拦截**)
|
||||
- 纸质附件:`OutboundReleaseReviewDialog.vue` 卡片上传
|
||||
|
||||
### 6.2 页面改动
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `outbound-release-review/index.vue` | 操作列「出库物料」 |
|
||||
| `OutboundReleaseMaterialDialog.vue` | **新建** |
|
||||
| `OutboundReleaseViewDialog.vue` | Section「出库物料准备」只读 |
|
||||
| `api/outbound-release-review.ts` | fetch/save API |
|
||||
|
||||
### 6.3 交互规则
|
||||
|
||||
- 保存成功:「出库物料准备已保存」(**不提**库存更新)。
|
||||
- 质检物料表可展示「当前库存」列作参考,**不**因库存不足阻止保存。
|
||||
- 删除行:前端移除,点保存后生效。
|
||||
- 操作列宽度:约 260px(查看 / 审核 / 出库物料)。
|
||||
|
||||
## 7. 后端模块划分
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `app/crud/outbound_release_material.py` | 查询、全量 save、主数据快照 |
|
||||
| `app/schemas/outbound_release_material.py` | Pydantic |
|
||||
| `app/api/outbound_release_review.py` | 两个端点 |
|
||||
| `app/models/models.py` | 三个 ORM 模型 |
|
||||
|
||||
**不修改:** `qc_material_usage.py`、`cell_product.py` 库存/状态逻辑。
|
||||
|
||||
## 8. 错误处理
|
||||
|
||||
| 场景 | HTTP | 说明 |
|
||||
|------|------|------|
|
||||
| 细胞/物料不存在 | 400 | ID 无效或已删除 |
|
||||
| 同计划重复勾选 | 400 | 同一产品/物料不可重复 |
|
||||
| qty ≤ 0 | 400 | 用量须大于 0 |
|
||||
| 输注计划不存在 | 404 | — |
|
||||
|
||||
## 9. 测试要点
|
||||
|
||||
1. 保存后:`biz_cell_product` 状态/库位**不变**;`biz_qc_material.stock_qty` **不变**。
|
||||
2. 编辑替换细胞/物料行:仅子表与快照更新。
|
||||
3. 两计划登记同一细胞:均允许保存(台账)。
|
||||
4. 查看弹窗正确展示快照与附件。
|
||||
5. 主数据后续变更:已保存台账仍显示保存时快照。
|
||||
|
||||
## 10. OpenSpec 增量
|
||||
|
||||
- `outbound-release-material-api`: GET/PUT outbound-materials(台账 CRUD)
|
||||
- `outbound-release-material-ui`: 出库物料准备弹窗 + 查看 Section
|
||||
|
||||
## 11. 工作量估算
|
||||
|
||||
| 项 | 人天 |
|
||||
|----|------|
|
||||
| SQL + Model + CRUD | 1.0 |
|
||||
| API + 详情聚合 | 0.5 |
|
||||
| 前端弹窗 + 查看 | 1.5 |
|
||||
| 联调 | 0.5 |
|
||||
| **合计** | **~3.5** |
|
||||
|
||||
---
|
||||
|
||||
## 附录:策略变更记录
|
||||
|
||||
| 版本 | 策略 | 说明 |
|
||||
|------|------|------|
|
||||
| v0(讨论稿) | B 真实出库 | 保存时改状态 + 扣库存 + 回滚 |
|
||||
| **v1(当前)** | **台账模式** | 出库物料**准备**界面仅记录;与交付清单类似,不联动库存 |
|
||||
|
||||
若后续需要在「审核通过」或独立「确认出库」节点做真实出库,应新建 spec,与本台账表通过 `infusion_schedule_id` 衔接,避免在本界面混入库存事务。
|
||||
Reference in new issue
Block a user