"""中试服务申请单管理 API(申请单、患者信息、输注方案、输注计划、出库联动)""" from typing import Optional from fastapi import APIRouter, Depends, Query from pydantic import BaseModel, ConfigDict, Field from sqlalchemy.orm import Session from app.core.database import get_db from app.core.menu_guard import require_admin_or_super_admin from app.core.response import error_response, success_response from app.crud import pilot_application from app.models.models import SysUser router = APIRouter(prefix="/web/admin", tags=["中试服务申请单管理"]) def _get_tenant_no(db: Session, user: dict) -> str: user_id = user.get("user_id") if not user_id: raise PermissionError("用户身份信息缺失,无法确定租户") sys_user = db.query(SysUser).filter(SysUser.id == user_id).first() if not sys_user or not sys_user.tenant_no: raise PermissionError("用户租户信息缺失") return sys_user.tenant_no # --------------------------------------------------------------------------- # Pydantic Schemas # --------------------------------------------------------------------------- class IdBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: str class PlanIdBody(BaseModel): model_config = ConfigDict(populate_by_name=True) plan_id: str = Field(..., alias="infusionPlanId") class ApplicationCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) channel_service_order_id: Optional[str] = Field(None, alias="channelServiceOrderId") remark: Optional[str] = None biz_type: Optional[str] = Field(None, alias="bizType") project_title: Optional[str] = Field(None, alias="projectTitle") customer_name: Optional[str] = Field(None, alias="customerName") customer_contact: Optional[str] = Field(None, alias="customerContact") customer_phone: Optional[str] = Field(None, alias="customerPhone") class ApplicationUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: str remark: Optional[str] = None project_title: Optional[str] = Field(None, alias="projectTitle") customer_name: Optional[str] = Field(None, alias="customerName") customer_contact: Optional[str] = Field(None, alias="customerContact") customer_phone: Optional[str] = Field(None, alias="customerPhone") class PatientSaveBody(BaseModel): model_config = ConfigDict(populate_by_name=True) application_id: str = Field(..., alias="pilotApplicationId") customer_id: Optional[str] = Field(None, alias="customerId") patient_name: str = Field(..., alias="patientName") sex: Optional[str] = None age: Optional[int] = None id_card_no: Optional[str] = Field(None, alias="idCardNo") phone_no: Optional[str] = Field(None, alias="phoneNo") emergency_contact: Optional[str] = Field(None, alias="emergencyContact") emergency_phone: Optional[str] = Field(None, alias="emergencyPhone") clinical_diagnosis: Optional[str] = Field(None, alias="clinicalDiagnosis") diagnosis_attachments: Optional[str] = Field(None, alias="diagnosisAttachments") need_checkup: int = Field(1, alias="needCheckup") class InfusionPlanCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) pilot_application_id: str = Field(..., alias="pilotApplicationId") cell_type: str = Field(..., alias="cellType") spec_text: Optional[str] = Field(None, alias="specText") total_course_count: int = Field(..., alias="totalCourseCount") course_interval: Optional[str] = Field(None, alias="courseInterval") infusion_route: Optional[str] = Field(None, alias="infusionRoute") infusion_start_date: Optional[str] = Field(None, alias="infusionStartDate") class InfusionPlanUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: str cell_type: Optional[str] = Field(None, alias="cellType") spec_text: Optional[str] = Field(None, alias="specText") total_course_count: Optional[int] = Field(None, alias="totalCourseCount") course_interval: Optional[str] = Field(None, alias="courseInterval") infusion_route: Optional[str] = Field(None, alias="infusionRoute") infusion_start_date: Optional[str] = Field(None, alias="infusionStartDate") class InfusionScheduleUpdateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: str infusion_time: Optional[str] = Field(None, alias="infusionTime") remark: Optional[str] = None attachments: Optional[str] = None class ScheduleCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) plan_id: str = Field(..., alias="infusionPlanId") infusion_time: Optional[str] = Field(None, alias="infusionTime") remark: Optional[str] = None amount: Optional[float] = None # --- 复合保存 Schema(参考采集协议单一次性保存模式)--- class PatientNestedBody(BaseModel): model_config = ConfigDict(populate_by_name=True) customer_id: Optional[str] = Field(None, alias="customerId") patient_name: Optional[str] = Field(None, alias="patientName") sex: Optional[str] = None age: Optional[int] = None id_card_no: Optional[str] = Field(None, alias="idCardNo") phone_no: Optional[str] = Field(None, alias="phoneNo") emergency_contact: Optional[str] = Field(None, alias="emergencyContact") emergency_phone: Optional[str] = Field(None, alias="emergencyPhone") clinical_diagnosis: Optional[str] = Field(None, alias="clinicalDiagnosis") diagnosis_attachments: Optional[str] = Field(None, alias="diagnosisAttachments") need_checkup: int = Field(1, alias="needCheckup") class ScheduleProductNestedBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: Optional[str] = None price_id: Optional[str] = Field(None, alias="priceId") project_code: Optional[str] = Field(None, alias="projectCode") project_name: Optional[str] = Field(None, alias="projectName") cell_type_name: Optional[str] = Field(None, alias="cellTypeName") spec_text: Optional[str] = Field(None, alias="specText") agreement_price: Optional[float] = Field(None, alias="agreementPrice") actual_price: Optional[float] = Field(None, alias="actualPrice") quantity: Optional[int] = 1 status: Optional[str] = None remark: Optional[str] = None class InfusionScheduleNestedBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: Optional[str] = None sort_no: Optional[int] = Field(None, alias="sortNo") infusion_time: Optional[str] = Field(None, alias="infusionTime") infusion_location: Optional[str] = Field(None, alias="infusionLocation") infusion_method: Optional[str] = Field(None, alias="infusionMethod") amount: Optional[float] = None cell_products: Optional[list[ScheduleProductNestedBody]] = Field(None, alias="cellProducts") class InfusionPlanNestedBody(BaseModel): model_config = ConfigDict(populate_by_name=True) id: Optional[str] = None cell_type: Optional[str] = Field(None, alias="cellType") spec_text: Optional[str] = Field(None, alias="specText") total_course_count: Optional[int] = Field(None, alias="totalCourseCount") course_interval: Optional[str] = Field(None, alias="courseInterval") infusion_route: Optional[str] = Field(None, alias="infusionRoute") infusion_start_date: Optional[str] = Field(None, alias="infusionStartDate") channel_agreement_id: Optional[str] = Field(None, alias="channelAgreementId") class ApplicationSaveBody(BaseModel): """一揽子保存:申请单 + 患者信息 + 输注方案 + 输注计划(含细胞产品)""" model_config = ConfigDict(populate_by_name=True) id: Optional[str] = None application_name: Optional[str] = Field(None, alias="applicationName") channel_service_order_id: Optional[str] = Field(None, alias="channelServiceOrderId") channel_id: Optional[str] = Field(None, alias="channelId") biz_type: Optional[str] = Field(None, alias="bizType") project_title: Optional[str] = Field(None, alias="projectTitle") customer_name: Optional[str] = Field(None, alias="customerName") customer_contact: Optional[str] = Field(None, alias="customerContact") customer_phone: Optional[str] = Field(None, alias="customerPhone") remark: Optional[str] = None consent_form_urls: Optional[str] = Field(None, alias="consentFormUrls") consent_attachments: Optional[list] = Field(None, alias="consentAttachments") attachment_urls: Optional[str] = Field(None, alias="attachmentUrls") other_attachments: Optional[list] = Field(None, alias="otherAttachments") patient: Optional[PatientNestedBody] = None infusion_plan: Optional[InfusionPlanNestedBody] = Field(None, alias="infusionPlan") infusion_schedules: Optional[list[InfusionScheduleNestedBody]] = Field( None, alias="infusionSchedules" ) infusion_plans: Optional[list[InfusionPlanNestedBody]] = Field(None, alias="infusionPlans") # --------------------------------------------------------------------------- # 1. 申请单 CRUD + 状态变更 # --------------------------------------------------------------------------- @router.get("/pilot-application/list") async def api_list_applications( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100, alias="pageSize"), keyword: Optional[str] = Query(None, alias="keyword"), channelId: Optional[str] = Query(None, alias="channelId"), applicationStatus: Optional[str] = Query(None, alias="applicationStatus"), channelServiceOrderId: Optional[str] = Query(None, alias="channelServiceOrderId"), bizType: Optional[str] = Query(None, alias="bizType"), user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) result = pilot_application.list_applications( db, tenant_no=tenant_no, page=page, page_size=pageSize, channel_id=channelId, application_status=applicationStatus, channel_service_order_id=channelServiceOrderId, keyword=keyword, biz_type=bizType, ) return success_response(result) @router.get("/pilot-application/detail") async def api_get_application( id: str = Query(...), user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) result = pilot_application.get_application(db, app_id=id, tenant_no=tenant_no) if result is None: return error_response(404, "申请单不存在") return success_response(result) @router.post("/pilot-application/create") async def api_create_application( body: ApplicationCreateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) data = { "channelServiceOrderId": body.channel_service_order_id or "", "remark": body.remark, "bizType": body.biz_type or "pilot_service", "projectTitle": body.project_title, "customerName": body.customer_name, "customerContact": body.customer_contact, "customerPhone": body.customer_phone, } result = pilot_application.create_application( db, data=data, tenant_no=tenant_no, user_id=user.get("user_id"), dept_id=user.get("region_id", ""), ) return success_response(result, message="创建成功") except ValueError as e: return error_response(400, str(e)) @router.put("/pilot-application/update") async def api_update_application( body: ApplicationUpdateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) data = {} if body.remark is not None: data["remark"] = body.remark if body.project_title is not None: data["projectTitle"] = body.project_title if body.customer_name is not None: data["customerName"] = body.customer_name if body.customer_contact is not None: data["customerContact"] = body.customer_contact if body.customer_phone is not None: data["customerPhone"] = body.customer_phone result = pilot_application.update_application( db, app_id=body.id, data=data, tenant_no=tenant_no ) return success_response(result, message="更新成功") except ValueError as e: return error_response(400, str(e)) @router.post("/pilot-application/save") async def api_save_application( body: ApplicationSaveBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) user_id = user.get("user_id") dept_id = user.get("region_id", "") is_edit = bool(body.id) biz_type = body.biz_type or "pilot_service" app_data: dict = { "channelServiceOrderId": body.channel_service_order_id or "", "channelId": body.channel_id or "", "bizType": biz_type, "remark": body.remark, } if body.application_name is not None: app_data["applicationName"] = body.application_name if body.project_title is not None: app_data["projectTitle"] = body.project_title if body.customer_name is not None: app_data["customerName"] = body.customer_name if body.customer_contact is not None: app_data["customerContact"] = body.customer_contact if body.customer_phone is not None: app_data["customerPhone"] = body.customer_phone if body.consent_attachments is not None: app_data["consentAttachments"] = body.consent_attachments elif body.consent_form_urls is not None: app_data["consentFormUrls"] = body.consent_form_urls if body.other_attachments is not None: app_data["otherAttachments"] = body.other_attachments elif body.attachment_urls is not None: app_data["attachmentUrls"] = body.attachment_urls if ( not is_edit and biz_type not in ("clinical_application", "basic_experiment") and not app_data["channelServiceOrderId"] ): return error_response(400, "请选择渠道服务订单") if not is_edit and biz_type == "clinical_application" and not app_data["channelId"]: return error_response(400, "请选择渠道") if is_edit: app_result = pilot_application.update_application( db, app_id=body.id, data=app_data, tenant_no=tenant_no, ) app_id = body.id else: app_result = pilot_application.create_application( db, data=app_data, tenant_no=tenant_no, user_id=user_id, dept_id=dept_id, ) app_id = app_result.get("id") if not app_id: return error_response(500, "创建申请单失败,未获取到ID") if body.patient: pilot_application.save_patient( db, app_id=app_id, data=body.patient.model_dump(by_alias=True), tenant_no=tenant_no, user_id=user_id, dept_id=dept_id, ) if body.infusion_plan is not None or body.infusion_schedules: plan_dict = ( body.infusion_plan.model_dump(by_alias=True) if body.infusion_plan else {} ) schedules = [ s.model_dump(by_alias=True) for s in (body.infusion_schedules or []) ] if is_edit: pilot_application.upsert_application_infusion( db, app_id=app_id, plan_data=plan_dict, schedules_data=schedules, tenant_no=tenant_no, user_id=user_id, dept_id=dept_id, ) else: pilot_application.save_application_infusion( db, app_id=app_id, plan_data=plan_dict, schedules_data=schedules, tenant_no=tenant_no, user_id=user_id, dept_id=dept_id, replace_existing=False, ) elif body.infusion_plans: for plan_data in body.infusion_plans: plan_result = pilot_application.create_infusion_plan( db, app_id=app_id, data=plan_data.model_dump(by_alias=True), tenant_no=tenant_no, user_id=user_id, dept_id=dept_id, ) plan_id = plan_result.get("id") if plan_id and (plan_data.total_course_count or 0) > 0: pilot_application.auto_generate_schedules( db, plan_id=plan_id, tenant_no=tenant_no, user_id=user_id, dept_id=dept_id, ) app_result = pilot_application.get_application(db, app_id=app_id, tenant_no=tenant_no) return success_response(app_result, message="保存成功") except ValueError as e: return error_response(400, str(e)) except Exception as e: return error_response(500, str(e)) @router.delete("/pilot-application/delete") async def api_delete_application( body: IdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) pilot_application.soft_delete_application(db, app_id=body.id, tenant_no=tenant_no) return success_response(None, message="删除成功") except ValueError as e: return error_response(400, str(e)) @router.post("/pilot-application/start") async def api_start_application( body: IdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = pilot_application.start_application( db, app_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id") ) return success_response(result, message="操作成功") except ValueError as e: return error_response(400, str(e)) @router.post("/pilot-application/complete") async def api_complete_application( body: IdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = pilot_application.complete_application( db, app_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id") ) return success_response(result, message="操作成功") except ValueError as e: return error_response(400, str(e)) class SaleScheduleProductLineBody(BaseModel): model_config = ConfigDict(populate_by_name=True) price_id: Optional[str] = Field(None, alias="priceId") project_code: Optional[str] = Field(None, alias="projectCode") project_name: Optional[str] = Field(None, alias="projectName") cell_type_name: Optional[str] = Field(None, alias="cellTypeName") spec_text: Optional[str] = Field(None, alias="specText") agreement_price: Optional[float] = Field(None, alias="agreementPrice") actual_price: Optional[float] = Field(None, alias="actualPrice") quantity: Optional[int] = 1 pickup_time: Optional[str] = Field(None, alias="pickupTime") amount: Optional[float] = None class SaleSchedulesCreateBody(BaseModel): model_config = ConfigDict(populate_by_name=True) product_lines: list[SaleScheduleProductLineBody] = Field(..., alias="productLines") @router.post("/pilot-application/{application_id}/sale-schedules") async def api_create_sale_schedules( application_id: str, body: SaleSchedulesCreateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) product_lines = [line.model_dump(by_alias=True) for line in body.product_lines] results = pilot_application.create_basic_experiment_sale_schedules( db, application_id=application_id, product_lines=product_lines, tenant_no=tenant_no, user_id=user.get("user_id"), dept_id=user.get("region_id", ""), ) return success_response(results, message="售卖计划创建成功") except ValueError as e: return error_response(400, str(e)) @router.post("/pilot-application/void") async def api_void_application( body: IdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = pilot_application.void_application( db, app_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id") ) return success_response(result, message="操作成功") except ValueError as e: return error_response(400, str(e)) # --------------------------------------------------------------------------- # 2. 患者信息 # --------------------------------------------------------------------------- @router.get("/pilot-patient/detail") async def api_get_patient( application_id: str = Query(..., alias="pilotApplicationId"), user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) result = pilot_application.get_patient_by_application( db, app_id=application_id, tenant_no=tenant_no ) if result is None: return success_response(None) return success_response(result) @router.post("/pilot-patient/save") async def api_save_patient( body: PatientSaveBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) data = { "customerId": body.customer_id, "patientName": body.patient_name, "sex": body.sex, "age": body.age, "idCardNo": body.id_card_no, "phoneNo": body.phone_no, "emergencyContact": body.emergency_contact, "emergencyPhone": body.emergency_phone, "clinicalDiagnosis": body.clinical_diagnosis, "diagnosisAttachments": body.diagnosis_attachments, "needCheckup": body.need_checkup, } result = pilot_application.save_patient( db, app_id=body.application_id, data=data, tenant_no=tenant_no, user_id=user.get("user_id"), dept_id=user.get("region_id", ""), ) return success_response(result, message="保存成功") except ValueError as e: return error_response(400, str(e)) # --------------------------------------------------------------------------- # 3. 输注方案 # --------------------------------------------------------------------------- @router.get("/pilot-infusion-plan/list") async def api_list_plans( application_id: str = Query(..., alias="pilotApplicationId"), user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) result = pilot_application.list_infusion_plans( db, app_id=application_id, tenant_no=tenant_no ) return success_response(result) @router.post("/pilot-infusion-plan/create") async def api_create_plan( body: InfusionPlanCreateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) data = { "cellType": body.cell_type, "specText": body.spec_text, "totalCourseCount": body.total_course_count, "courseInterval": body.course_interval, "infusionRoute": body.infusion_route, "infusionStartDate": body.infusion_start_date, } result = pilot_application.create_infusion_plan( db, app_id=body.pilot_application_id, data=data, tenant_no=tenant_no, user_id=user.get("user_id"), dept_id=user.get("region_id", ""), ) return success_response(result, message="创建成功") except ValueError as e: return error_response(400, str(e)) @router.put("/pilot-infusion-plan/update") async def api_update_plan( body: InfusionPlanUpdateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) data = {} if body.cell_type is not None: data["cellType"] = body.cell_type if body.spec_text is not None: data["specText"] = body.spec_text if body.total_course_count is not None: data["totalCourseCount"] = body.total_course_count if body.course_interval is not None: data["courseInterval"] = body.course_interval if body.infusion_route is not None: data["infusionRoute"] = body.infusion_route if body.infusion_start_date is not None: data["infusionStartDate"] = body.infusion_start_date result = pilot_application.update_infusion_plan( db, plan_id=body.id, data=data, tenant_no=tenant_no ) return success_response(result, message="更新成功") except ValueError as e: return error_response(400, str(e)) @router.delete("/pilot-infusion-plan/delete") async def api_delete_plan( body: IdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) pilot_application.soft_delete_infusion_plan( db, plan_id=body.id, tenant_no=tenant_no ) return success_response(None, message="删除成功") except ValueError as e: return error_response(400, str(e)) # --------------------------------------------------------------------------- # 4. 输注计划 # --------------------------------------------------------------------------- @router.get("/pilot-infusion-schedule/list") async def api_list_schedules( plan_id: str = Query(..., alias="infusionPlanId"), user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): tenant_no = _get_tenant_no(db, user) result = pilot_application.list_schedules_by_plan( db, plan_id=plan_id, tenant_no=tenant_no ) return success_response(result) @router.post("/pilot-infusion-schedule/auto-generate") async def api_auto_generate( body: PlanIdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = pilot_application.auto_generate_schedules( db, plan_id=body.plan_id, tenant_no=tenant_no, user_id=user.get("user_id"), dept_id=user.get("region_id", ""), ) return success_response(result, message="生成成功") except ValueError as e: return error_response(400, str(e)) @router.post("/pilot-infusion-schedule/create") async def api_create_schedule( body: ScheduleCreateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) data = { "infusionTime": body.infusion_time, "remark": body.remark, "amount": body.amount, } result = pilot_application.create_schedule( db, plan_id=body.plan_id, data=data, tenant_no=tenant_no, user_id=user.get("user_id"), dept_id=user.get("region_id", ""), ) return success_response(result, message="创建成功") except ValueError as e: return error_response(400, str(e)) @router.put("/pilot-infusion-schedule/update") async def api_update_schedule( body: InfusionScheduleUpdateBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) data = {} if body.infusion_time is not None: data["infusionTime"] = body.infusion_time if body.remark is not None: data["remark"] = body.remark if body.attachments is not None: data["attachments"] = body.attachments result = pilot_application.update_schedule( db, schedule_id=body.id, data=data, tenant_no=tenant_no ) return success_response(result, message="更新成功") except ValueError as e: return error_response(400, str(e)) @router.delete("/pilot-infusion-schedule/delete") async def api_delete_schedule( body: IdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) pilot_application.soft_delete_schedule( db, schedule_id=body.id, tenant_no=tenant_no ) return success_response(None, message="删除成功") except ValueError as e: return error_response(400, str(e)) @router.post("/pilot-infusion-schedule/mark-complete") async def api_mark_complete( body: IdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) result = pilot_application.mark_schedule_complete( db, schedule_id=body.id, tenant_no=tenant_no, user_id=user.get("user_id") ) return success_response(result, message="操作成功") except ValueError as e: return error_response(400, str(e)) # --------------------------------------------------------------------------- # 出库生产工单 / 出库质检工单 联动 # --------------------------------------------------------------------------- class ScheduleIdBody(BaseModel): scheduleId: str @router.post("/pilot-infusion-schedule/create-production-work-order") async def api_create_outbound_production( body: ScheduleIdBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) from app.crud import production_work_order as pwo_svc from app.crud.qc_work_order import resolve_outbound_biz_type_for_schedule outbound_biz_type = resolve_outbound_biz_type_for_schedule( db, body.scheduleId, tenant_no ) wo = pwo_svc.create_outbound_production_work_order( db, schedule_id=body.scheduleId, tenant_no=tenant_no, user_id=user.get("user_id"), create_dept_id=user.get("dept_id", ""), biz_type=outbound_biz_type, ) db.commit() db.refresh(wo) return success_response(pwo_svc._wo_to_dict(wo), message="创建出库生产工单成功") except ValueError as e: return error_response(400, str(e)) except Exception as e: db.rollback() return error_response(400, str(e)) class OutboundQcBody(BaseModel): scheduleId: str cellProductId: str = "" @router.post("/pilot-infusion-schedule/create-qc-work-order") async def api_create_outbound_qc( body: OutboundQcBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) from app.crud import qc_work_order as qc_svc from app.crud.qc_work_order import resolve_outbound_biz_type_for_schedule outbound_biz_type = resolve_outbound_biz_type_for_schedule( db, body.scheduleId, tenant_no ) wo = qc_svc.create_outbound_qc_work_order( db, schedule_id=body.scheduleId, cell_product_id=body.cellProductId, tenant_no=tenant_no, user_id=user.get("user_id"), create_dept_id=user.get("dept_id", ""), biz_type=outbound_biz_type, ) return success_response(qc_svc._wo_to_dict(wo), message="创建出库质检工单成功") except ValueError as e: return error_response(400, str(e)) except Exception as e: db.rollback() return error_response(400, str(e))