| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- """输注计划管理 · 出库交接 CRUD"""
- from __future__ import annotations
- from datetime import datetime
- from typing import Any, Dict, List, Optional
- from fastapi import HTTPException
- from sqlalchemy.orm import Session
- from app.core.utils import generate_uuid
- from app.crud import outbound_release_material as orm_material_svc
- from app.crud import outbound_handover_inventory as handover_inv_svc
- from app.crud import pilot_infusion_schedule_mgmt as schedule_mgmt_svc
- from app.models.models import BizOutboundHandover, SysUser
- def _fmt_dt(t: Optional[datetime]) -> str:
- return t.strftime("%Y-%m-%d %H:%M:%S") if t else ""
- def _parse_handover_time(value: str) -> datetime:
- raw = (value or "").strip()
- if not raw:
- raise HTTPException(status_code=400, detail="交接时间不能为空")
- for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M"):
- try:
- return datetime.strptime(raw, fmt)
- except ValueError:
- continue
- raise HTTPException(status_code=400, detail="交接时间格式无效")
- def _handover_row_to_dict(row: Optional[BizOutboundHandover]) -> Dict[str, Any]:
- if not row:
- return {
- "handoverUserId": "",
- "handoverPerson": "",
- "handoverTime": "",
- "handoverLocation": "",
- "receiverPerson": "",
- "cellPurpose": "",
- "attachments": "",
- "remark": "",
- }
- return {
- "handoverUserId": row.handover_user_id or "",
- "handoverPerson": row.handover_person or "",
- "handoverTime": _fmt_dt(row.handover_time),
- "handoverLocation": row.handover_location or "",
- "receiverPerson": row.receiver_person or "",
- "cellPurpose": row.cell_purpose or "",
- "attachments": row.attachments or "",
- "remark": row.remark or "",
- }
- def _handover_view_dict(row: Optional[BizOutboundHandover]) -> Dict[str, Any]:
- if not row:
- return {
- "handoverPerson": "",
- "handoverTime": "",
- "handoverLocation": "",
- "receiverPerson": "",
- "cellPurpose": "",
- "attachments": "",
- "remark": "",
- }
- return {
- "handoverPerson": row.handover_person or "",
- "handoverTime": _fmt_dt(row.handover_time),
- "handoverLocation": row.handover_location or "",
- "receiverPerson": row.receiver_person or "",
- "cellPurpose": row.cell_purpose or "",
- "attachments": row.attachments or "",
- "remark": row.remark or "",
- }
- def get_handover_by_schedule(
- db: Session,
- schedule_id: str,
- tenant_no: str,
- ) -> Optional[BizOutboundHandover]:
- sid = (schedule_id or "").strip()
- if not sid:
- return None
- return (
- db.query(BizOutboundHandover)
- .filter(
- BizOutboundHandover.infusion_schedule_id == sid,
- BizOutboundHandover.tenant_no == tenant_no,
- BizOutboundHandover.del_flag == 0,
- )
- .first()
- )
- def get_outbound_handover_view(
- db: Session,
- schedule_id: str,
- tenant_no: str,
- ) -> Dict[str, Any]:
- """供输注计划 detail 聚合的交接只读信息。"""
- row = get_handover_by_schedule(db, schedule_id, tenant_no)
- return _handover_view_dict(row)
- def get_outbound_handover(
- db: Session,
- schedule_id: str,
- tenant_no: str,
- ) -> Dict[str, Any]:
- """获取出库交接编辑数据(物料 + 交接信息 + 摘要)。"""
- detail = schedule_mgmt_svc.get_infusion_schedule_detail(db, schedule_id, tenant_no)
- if not detail:
- raise HTTPException(status_code=404, detail="输注计划不存在")
- outbound_material = orm_material_svc.get_outbound_material(db, schedule_id, tenant_no)
- handover_row = get_handover_by_schedule(db, schedule_id, tenant_no)
- return {
- "scheduleSummary": {
- "applicationNo": detail.get("applicationNo") or "",
- "patientName": detail.get("patientName") or "",
- "infusionTime": detail.get("infusionTime") or "",
- },
- "outboundMaterial": {
- "remark": outbound_material.get("remark") or "",
- "cells": outbound_material.get("cells") or [],
- "qcMaterials": outbound_material.get("qcMaterials") or [],
- "paperMaterials": outbound_material.get("paperMaterials") or [],
- },
- "handover": _handover_row_to_dict(handover_row),
- }
- def save_outbound_handover(
- db: Session,
- schedule_id: str,
- *,
- outbound_material: Dict[str, Any],
- handover: Dict[str, Any],
- tenant_no: str,
- user_id: str,
- create_dept_id: str = "",
- ) -> Dict[str, Any]:
- """保存出库物料台账 + 出库交接(单事务,调用方 commit)。"""
- detail = schedule_mgmt_svc.get_infusion_schedule_detail(db, schedule_id, tenant_no)
- if not detail:
- raise HTTPException(status_code=404, detail="输注计划不存在")
- handover_user_id = (handover.get("handoverUserId") or handover.get("handover_user_id") or "").strip()
- receiver_person = (handover.get("receiverPerson") or handover.get("receiver_person") or "").strip()
- handover_location = (handover.get("handoverLocation") or handover.get("handover_location") or "").strip()
- handover_remark = (handover.get("remark") or "").strip()
- cell_purpose = (handover.get("cellPurpose") or handover.get("cell_purpose") or "").strip()
- attachments = (handover.get("attachments") or "").strip()
- handover_time_raw = handover.get("handoverTime") or handover.get("handover_time") or ""
- if not handover_user_id:
- raise HTTPException(status_code=400, detail="交接人不能为空")
- if not receiver_person:
- raise HTTPException(status_code=400, detail="接收人不能为空")
- sys_user = (
- db.query(SysUser)
- .filter(
- SysUser.id == handover_user_id,
- SysUser.del_flag == 0,
- )
- .first()
- )
- if not sys_user:
- raise HTTPException(status_code=400, detail="交接人用户不存在或已删除")
- handover_time = _parse_handover_time(str(handover_time_raw))
- handover_person = (sys_user.nick_name or sys_user.user_no or "").strip()
- if not handover_person:
- handover_person = handover_user_id
- om = outbound_material or {}
- cells = om.get("cells") or []
- qc_materials = om.get("qcMaterials") or om.get("qc_materials") or []
- paper_materials = om.get("paperMaterials") or om.get("paper_materials") or []
- material_remark = om.get("remark")
- # 读取保存前台账,用于回滚上次出库交接的库存/状态变更
- previous_material = orm_material_svc.get_outbound_material(db, schedule_id, tenant_no)
- previous_cell_ids = [
- (c.get("cellProductId") or c.get("cell_product_id") or "").strip()
- for c in (previous_material.get("cells") or [])
- ]
- had_previous_handover = get_handover_by_schedule(db, schedule_id, tenant_no) is not None
- handover_inv_svc.revert_handover_inventory(
- db,
- schedule_id,
- cell_product_ids=previous_cell_ids if had_previous_handover else [],
- tenant_no=tenant_no,
- user_id=user_id,
- )
- orm_material_svc.save_outbound_material(
- db,
- schedule_id,
- paper_attachments=None,
- paper_materials=paper_materials,
- remark=material_remark,
- cells=cells,
- qc_materials=qc_materials,
- tenant_no=tenant_no,
- user_id=user_id,
- create_dept_id=create_dept_id,
- )
- handover_inv_svc.apply_handover_inventory(
- db,
- schedule_id,
- cells=cells,
- qc_materials=qc_materials,
- tenant_no=tenant_no,
- user_id=user_id,
- create_dept_id=create_dept_id,
- receiver_person=receiver_person,
- )
- now = datetime.now()
- row = get_handover_by_schedule(db, schedule_id, tenant_no)
- if not row:
- row = BizOutboundHandover(
- id=generate_uuid(),
- infusion_schedule_id=schedule_id,
- tenant_no=tenant_no,
- order_num=0,
- create_by=user_id,
- create_dept_id=create_dept_id or "",
- create_time=now,
- del_flag=0,
- )
- db.add(row)
- db.flush()
- else:
- row.update_by = user_id
- row.update_time = now
- row.handover_user_id = handover_user_id
- row.handover_person = handover_person
- row.handover_time = handover_time
- row.handover_location = handover_location or None
- row.receiver_person = receiver_person
- row.cell_purpose = cell_purpose or None
- row.attachments = attachments or None
- row.remark = handover_remark or None
- db.add(row)
- db.flush()
- return get_outbound_handover(db, schedule_id, tenant_no)
|