From d84f5056f2a0e7991487e934627f5839b89c8490 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 26 May 2026 18:04:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(production):=20extend=20CRUD=20for=20outbo?= =?UTF-8?q?und=20work=20orders=20=E2=80=94=20biz=5Ftype,=20yzf=20status,?= =?UTF-8?q?=20list/detail=20enrichment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/crud/production_work_order.py | 111 +++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/backend/app/crud/production_work_order.py b/backend/app/crud/production_work_order.py index 54337ab4..4fa539dd 100644 --- a/backend/app/crud/production_work_order.py +++ b/backend/app/crud/production_work_order.py @@ -33,6 +33,7 @@ _WORK_ORDER_STATUS_LABELS = { "dpz": "待配置", "jxz": "进行中", "ywc": "已完成", + "yzf": "已作废", } _RECEPTION_SOURCE_LABELS = { "cell_storage": "细胞存储", @@ -100,6 +101,8 @@ def _wo_to_dict(wo: BizProductionWorkOrder) -> Dict[str, Any]: getattr(wo, "reception_source", "cell_storage") or "cell_storage", ), "infusionScheduleId": getattr(wo, "infusion_schedule_id", None) or None, + "bizType": getattr(wo, "biz_type", None) or None, + "bizTypeLabel": {"pilot_service": "中试服务", "clinical_application": "临床应用"}.get(getattr(wo, "biz_type", None) or "", ""), "workOrderStatus": wo.work_order_status, "workOrderStatusLabel": _WORK_ORDER_STATUS_LABELS.get(wo.work_order_status, wo.work_order_status), "receptionTime": "", @@ -117,6 +120,65 @@ def _enrich_wo_items(db: Session, items: List[Dict[str, Any]]) -> List[Dict[str, return qc_wo_svc._enrich_work_order_list_items(db, items) +_BIZ_TYPE_LABELS = {"pilot_service": "中试服务", "clinical_application": "临床应用"} + + +def _enrich_outbound_items(db: Session, items: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """为 outbound 类型工单附加申请单号、细胞类型、规格、输注时间等关联数据。""" + from app.models.models import ( + BizPilotApplication, + BizPilotInfusionPlan, + BizPilotInfusionSchedule, + ) + + outbound_ids = [ + it["infusionScheduleId"] + for it in items + if it.get("workOrderType") == "outbound" and it.get("infusionScheduleId") + ] + if not outbound_ids: + return items + + schedules = ( + db.query(BizPilotInfusionSchedule) + .filter( + BizPilotInfusionSchedule.id.in_(outbound_ids), + BizPilotInfusionSchedule.del_flag == 0, + ) + .all() + ) + sched_map = {s.id: s for s in schedules} + + plan_ids = list({s.infusion_plan_id for s in schedules if s.infusion_plan_id}) + plan_map = {} + if plan_ids: + plans = db.query(BizPilotInfusionPlan).filter(BizPilotInfusionPlan.id.in_(plan_ids)).all() + plan_map = {p.id: p for p in plans} + + app_ids = list({p.pilot_application_id for p in plan_map.values() if p.pilot_application_id}) + app_map = {} + if app_ids: + apps = db.query(BizPilotApplication).filter(BizPilotApplication.id.in_(app_ids)).all() + app_map = {a.id: a for a in apps} + + for it in items: + if it.get("workOrderType") != "outbound" or not it.get("infusionScheduleId"): + continue + sched = sched_map.get(it["infusionScheduleId"]) + if not sched: + continue + plan = plan_map.get(sched.infusion_plan_id) if sched.infusion_plan_id else None + app = app_map.get(plan.pilot_application_id) if plan and plan.pilot_application_id else None + + it["applicationNo"] = app.application_no if app else "" + it["cellType"] = sched.cell_type or "" + it["specText"] = sched.spec_text or "" + it["infusionTime"] = _fmt_dt(sched.infusion_time) if sched.infusion_time else "" + it["infusionLocation"] = sched.infusion_location or "" + + return items + + def _reception_source_label(source: Optional[str]) -> str: key = (source or "").strip() or "cell_storage" return _RECEPTION_SOURCE_LABELS.get(key, key) @@ -157,10 +219,54 @@ def _attach_reception_detail( } +def _attach_outbound_detail( + db: Session, wo: BizProductionWorkOrder, out: Dict[str, Any] +) -> None: + """outbound 工单详情附加申请单 + 输注计划信息。""" + from app.models.models import ( + BizPilotApplication, + BizPilotInfusionPlan, + BizPilotInfusionSchedule, + ) + + _APP_STATUS_LABELS = {"dks": "待开始", "jxz": "进行中", "ywc": "已完成", "yzf": "已作废"} + + sched = ( + db.query(BizPilotInfusionSchedule) + .filter(BizPilotInfusionSchedule.id == wo.infusion_schedule_id, BizPilotInfusionSchedule.del_flag == 0) + .first() + ) + plan = None + app = None + if sched and sched.infusion_plan_id: + plan = db.query(BizPilotInfusionPlan).filter(BizPilotInfusionPlan.id == sched.infusion_plan_id).first() + if plan and plan.pilot_application_id: + app = db.query(BizPilotApplication).filter(BizPilotApplication.id == plan.pilot_application_id).first() + + out["reception"] = {} + out["pilotApplication"] = { + "applicationNo": app.application_no if app else "", + "applicationName": app.application_name if app else "", + "applicationStatus": app.application_status if app else "", + "applicationStatusLabel": _APP_STATUS_LABELS.get(app.application_status, "") if app else "", + } if app else {} + out["infusionSchedule"] = { + "cellType": sched.cell_type if sched else "", + "specText": sched.spec_text if sched else "", + "infusionTime": _fmt_dt(sched.infusion_time) if sched and sched.infusion_time else "", + "infusionLocation": sched.infusion_location if sched else "", + "infusionMethod": sched.infusion_method if sched else "", + "scheduleStatus": sched.schedule_status if sched else "", + } if sched else {} + + def _wo_to_response_dict(db: Session, wo: BizProductionWorkOrder) -> Dict[str, Any]: """工单响应(含接收记录关联的样本类型、接收人、接收时间及 reception 详情)。""" out = _enrich_wo_items(db, [_wo_to_dict(wo)])[0] - _attach_reception_detail(db, wo, out, wo.tenant_no or "") + if wo.work_order_type == "outbound" and wo.infusion_schedule_id: + _attach_outbound_detail(db, wo, out) + else: + _attach_reception_detail(db, wo, out, wo.tenant_no or "") return out @@ -341,6 +447,7 @@ def list_production_work_orders( .all() ) items = _enrich_wo_items(db, [_wo_to_dict(x) for x in rows]) + items = _enrich_outbound_items(db, items) return {"items": items, "total": int(total)} @@ -643,6 +750,7 @@ def create_outbound_production_work_order( user_id: Optional[str] = None, create_dept_id: str = "", remark: str = "", + biz_type: str = "pilot_service", ) -> BizProductionWorkOrder: existing = ( db.query(BizProductionWorkOrder) @@ -666,6 +774,7 @@ def create_outbound_production_work_order( sample_code=None, reception_source="outbound", infusion_schedule_id=schedule_id, + biz_type=biz_type, work_order_status="dpz", tenant_no=tenant_no, remark=remark,