"""中试服务出库生产/质检工单与输注计划生产状态、质检状态双向联动。""" from __future__ import annotations from datetime import datetime from typing import Optional from sqlalchemy.orm import Session from app.models.models import ( BizPilotInfusionSchedule, BizProductionWorkOrder, BizQcWorkOrder, ) _OUTBOUND_BIZ_TYPES = ("pilot_service", "clinical_application", "basic_experiment") def _is_pilot_outbound_production(wo: BizProductionWorkOrder) -> bool: return ( (getattr(wo, "work_order_type", None) or "") == "outbound" and (getattr(wo, "biz_type", None) or "") in _OUTBOUND_BIZ_TYPES and bool(getattr(wo, "infusion_schedule_id", None)) ) def _is_pilot_outbound_qc(wo: BizQcWorkOrder) -> bool: return ( (getattr(wo, "work_order_type", None) or "") == "outbound" and (getattr(wo, "biz_type", None) or "") in _OUTBOUND_BIZ_TYPES and bool(getattr(wo, "infusion_schedule_id", None)) ) def _get_schedule( db: Session, schedule_id: str, tenant_no: str ) -> Optional[BizPilotInfusionSchedule]: if not (schedule_id or "").strip(): return None return ( db.query(BizPilotInfusionSchedule) .filter( BizPilotInfusionSchedule.id == schedule_id.strip(), BizPilotInfusionSchedule.tenant_no == tenant_no, BizPilotInfusionSchedule.del_flag == 0, ) .first() ) def _touch(row, user_id: Optional[str]) -> None: now = datetime.now() if user_id: row.update_by = user_id row.update_time = now def sync_schedule_from_production_work_order( db: Session, wo: BizProductionWorkOrder, *, user_id: Optional[str] = None, ) -> None: """出库生产工单状态 → 输注计划 schedule_status。""" if not _is_pilot_outbound_production(wo): return sched = _get_schedule(db, wo.infusion_schedule_id or "", wo.tenant_no or "") if not sched: return wo_status = (wo.work_order_status or "").strip() current = (sched.schedule_status or "").strip() new_status: Optional[str] = None if wo_status in ("dpz", "jxz"): if current not in ("ysc", "yjf", "yqx"): new_status = "dsc" elif wo_status == "ywc": if current not in ("yjf", "yqx"): new_status = "ysc" elif wo_status == "yzf": new_status = "yqx" if new_status and new_status != current: sched.schedule_status = new_status _touch(sched, user_id) db.add(sched) def sync_schedule_from_qc_work_order( db: Session, wo: BizQcWorkOrder, *, user_id: Optional[str] = None, ) -> None: """出库质检工单状态 → 输注计划 qc_status(仅计划自动质检参与联动)。""" if not _is_pilot_outbound_qc(wo): return trigger = (getattr(wo, "trigger_source", None) or "reception").strip() if trigger != "schedule": return sched = _get_schedule(db, wo.infusion_schedule_id or "", wo.tenant_no or "") if not sched: return wo_status = (wo.work_order_status or "").strip() current = (sched.qc_status or "").strip() new_status: Optional[str] = None if wo_status in ("dpz", "ypz"): if current not in ("yzj", "yqx"): new_status = "dzc" elif wo_status == "ywc": if current != "yqx": new_status = "yzj" elif wo_status == "yzf": new_status = "yqx" if new_status and new_status != current: sched.qc_status = new_status _touch(sched, user_id) db.add(sched) def sync_production_work_order_from_schedule( db: Session, schedule: BizPilotInfusionSchedule, *, user_id: Optional[str] = None, ) -> None: """输注计划 schedule_status → 出库生产工单状态。""" schedule_id = schedule.id or "" tenant_no = schedule.tenant_no or "" if not schedule_id: return wo = ( db.query(BizProductionWorkOrder) .filter( BizProductionWorkOrder.infusion_schedule_id == schedule_id, BizProductionWorkOrder.tenant_no == tenant_no, BizProductionWorkOrder.del_flag == 0, BizProductionWorkOrder.work_order_type == "outbound", BizProductionWorkOrder.biz_type.in_(_OUTBOUND_BIZ_TYPES), ) .first() ) if not wo: return sched_status = (schedule.schedule_status or "").strip() current = (wo.work_order_status or "").strip() new_status: Optional[str] = None if sched_status in ("ysc", "yjf"): if current not in ("ywc", "yzf"): new_status = "ywc" elif sched_status == "yqx": if current != "ywc": new_status = "yzf" if new_status and new_status != current: wo.work_order_status = new_status _touch(wo, user_id) db.add(wo) def sync_qc_work_order_from_schedule( db: Session, schedule: BizPilotInfusionSchedule, *, user_id: Optional[str] = None, ) -> None: """输注计划 qc_status → 出库质检工单状态。""" schedule_id = schedule.id or "" tenant_no = schedule.tenant_no or "" if not schedule_id: return wo = ( db.query(BizQcWorkOrder) .filter( BizQcWorkOrder.infusion_schedule_id == schedule_id, BizQcWorkOrder.tenant_no == tenant_no, BizQcWorkOrder.del_flag == 0, BizQcWorkOrder.work_order_type == "outbound", BizQcWorkOrder.biz_type.in_(_OUTBOUND_BIZ_TYPES), BizQcWorkOrder.trigger_source == "schedule", ) .first() ) if not wo: return qc_status = (schedule.qc_status or "").strip() current = (wo.work_order_status or "").strip() new_status: Optional[str] = None if qc_status == "yzj": if current not in ("ywc", "yzf"): new_status = "ywc" elif qc_status == "yqx": if current != "ywc": new_status = "yzf" if new_status and new_status != current: wo.work_order_status = new_status _touch(wo, user_id) db.add(wo) def sync_pilot_outbound_from_schedule( db: Session, schedule: BizPilotInfusionSchedule, *, user_id: Optional[str] = None, ) -> None: """输注计划状态变更后,回写关联出库工单。""" sync_production_work_order_from_schedule(db, schedule, user_id=user_id) sync_qc_work_order_from_schedule(db, schedule, user_id=user_id)