outbound_handover.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. """输注计划管理 · 出库交接 CRUD"""
  2. from __future__ import annotations
  3. from datetime import datetime
  4. from typing import Any, Dict, List, Optional
  5. from fastapi import HTTPException
  6. from sqlalchemy.orm import Session
  7. from app.core.utils import generate_uuid
  8. from app.crud import outbound_release_material as orm_material_svc
  9. from app.crud import outbound_handover_inventory as handover_inv_svc
  10. from app.crud import pilot_infusion_schedule_mgmt as schedule_mgmt_svc
  11. from app.models.models import BizOutboundHandover, SysUser
  12. def _fmt_dt(t: Optional[datetime]) -> str:
  13. return t.strftime("%Y-%m-%d %H:%M:%S") if t else ""
  14. def _parse_handover_time(value: str) -> datetime:
  15. raw = (value or "").strip()
  16. if not raw:
  17. raise HTTPException(status_code=400, detail="交接时间不能为空")
  18. for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M"):
  19. try:
  20. return datetime.strptime(raw, fmt)
  21. except ValueError:
  22. continue
  23. raise HTTPException(status_code=400, detail="交接时间格式无效")
  24. def _handover_row_to_dict(row: Optional[BizOutboundHandover]) -> Dict[str, Any]:
  25. if not row:
  26. return {
  27. "handoverUserId": "",
  28. "handoverPerson": "",
  29. "handoverTime": "",
  30. "handoverLocation": "",
  31. "receiverPerson": "",
  32. "cellPurpose": "",
  33. "attachments": "",
  34. "remark": "",
  35. }
  36. return {
  37. "handoverUserId": row.handover_user_id or "",
  38. "handoverPerson": row.handover_person or "",
  39. "handoverTime": _fmt_dt(row.handover_time),
  40. "handoverLocation": row.handover_location or "",
  41. "receiverPerson": row.receiver_person or "",
  42. "cellPurpose": row.cell_purpose or "",
  43. "attachments": row.attachments or "",
  44. "remark": row.remark or "",
  45. }
  46. def _handover_view_dict(row: Optional[BizOutboundHandover]) -> Dict[str, Any]:
  47. if not row:
  48. return {
  49. "handoverPerson": "",
  50. "handoverTime": "",
  51. "handoverLocation": "",
  52. "receiverPerson": "",
  53. "cellPurpose": "",
  54. "attachments": "",
  55. "remark": "",
  56. }
  57. return {
  58. "handoverPerson": row.handover_person or "",
  59. "handoverTime": _fmt_dt(row.handover_time),
  60. "handoverLocation": row.handover_location or "",
  61. "receiverPerson": row.receiver_person or "",
  62. "cellPurpose": row.cell_purpose or "",
  63. "attachments": row.attachments or "",
  64. "remark": row.remark or "",
  65. }
  66. def get_handover_by_schedule(
  67. db: Session,
  68. schedule_id: str,
  69. tenant_no: str,
  70. ) -> Optional[BizOutboundHandover]:
  71. sid = (schedule_id or "").strip()
  72. if not sid:
  73. return None
  74. return (
  75. db.query(BizOutboundHandover)
  76. .filter(
  77. BizOutboundHandover.infusion_schedule_id == sid,
  78. BizOutboundHandover.tenant_no == tenant_no,
  79. BizOutboundHandover.del_flag == 0,
  80. )
  81. .first()
  82. )
  83. def get_outbound_handover_view(
  84. db: Session,
  85. schedule_id: str,
  86. tenant_no: str,
  87. ) -> Dict[str, Any]:
  88. """供输注计划 detail 聚合的交接只读信息。"""
  89. row = get_handover_by_schedule(db, schedule_id, tenant_no)
  90. return _handover_view_dict(row)
  91. def get_outbound_handover(
  92. db: Session,
  93. schedule_id: str,
  94. tenant_no: str,
  95. ) -> Dict[str, Any]:
  96. """获取出库交接编辑数据(物料 + 交接信息 + 摘要)。"""
  97. detail = schedule_mgmt_svc.get_infusion_schedule_detail(db, schedule_id, tenant_no)
  98. if not detail:
  99. raise HTTPException(status_code=404, detail="输注计划不存在")
  100. outbound_material = orm_material_svc.get_outbound_material(db, schedule_id, tenant_no)
  101. handover_row = get_handover_by_schedule(db, schedule_id, tenant_no)
  102. return {
  103. "scheduleSummary": {
  104. "applicationNo": detail.get("applicationNo") or "",
  105. "patientName": detail.get("patientName") or "",
  106. "infusionTime": detail.get("infusionTime") or "",
  107. },
  108. "outboundMaterial": {
  109. "remark": outbound_material.get("remark") or "",
  110. "cells": outbound_material.get("cells") or [],
  111. "qcMaterials": outbound_material.get("qcMaterials") or [],
  112. "paperMaterials": outbound_material.get("paperMaterials") or [],
  113. },
  114. "handover": _handover_row_to_dict(handover_row),
  115. }
  116. def save_outbound_handover(
  117. db: Session,
  118. schedule_id: str,
  119. *,
  120. outbound_material: Dict[str, Any],
  121. handover: Dict[str, Any],
  122. tenant_no: str,
  123. user_id: str,
  124. create_dept_id: str = "",
  125. ) -> Dict[str, Any]:
  126. """保存出库物料台账 + 出库交接(单事务,调用方 commit)。"""
  127. detail = schedule_mgmt_svc.get_infusion_schedule_detail(db, schedule_id, tenant_no)
  128. if not detail:
  129. raise HTTPException(status_code=404, detail="输注计划不存在")
  130. handover_user_id = (handover.get("handoverUserId") or handover.get("handover_user_id") or "").strip()
  131. receiver_person = (handover.get("receiverPerson") or handover.get("receiver_person") or "").strip()
  132. handover_location = (handover.get("handoverLocation") or handover.get("handover_location") or "").strip()
  133. handover_remark = (handover.get("remark") or "").strip()
  134. cell_purpose = (handover.get("cellPurpose") or handover.get("cell_purpose") or "").strip()
  135. attachments = (handover.get("attachments") or "").strip()
  136. handover_time_raw = handover.get("handoverTime") or handover.get("handover_time") or ""
  137. if not handover_user_id:
  138. raise HTTPException(status_code=400, detail="交接人不能为空")
  139. if not receiver_person:
  140. raise HTTPException(status_code=400, detail="接收人不能为空")
  141. sys_user = (
  142. db.query(SysUser)
  143. .filter(
  144. SysUser.id == handover_user_id,
  145. SysUser.del_flag == 0,
  146. )
  147. .first()
  148. )
  149. if not sys_user:
  150. raise HTTPException(status_code=400, detail="交接人用户不存在或已删除")
  151. handover_time = _parse_handover_time(str(handover_time_raw))
  152. handover_person = (sys_user.nick_name or sys_user.user_no or "").strip()
  153. if not handover_person:
  154. handover_person = handover_user_id
  155. om = outbound_material or {}
  156. cells = om.get("cells") or []
  157. qc_materials = om.get("qcMaterials") or om.get("qc_materials") or []
  158. paper_materials = om.get("paperMaterials") or om.get("paper_materials") or []
  159. material_remark = om.get("remark")
  160. # 读取保存前台账,用于回滚上次出库交接的库存/状态变更
  161. previous_material = orm_material_svc.get_outbound_material(db, schedule_id, tenant_no)
  162. previous_cell_ids = [
  163. (c.get("cellProductId") or c.get("cell_product_id") or "").strip()
  164. for c in (previous_material.get("cells") or [])
  165. ]
  166. had_previous_handover = get_handover_by_schedule(db, schedule_id, tenant_no) is not None
  167. handover_inv_svc.revert_handover_inventory(
  168. db,
  169. schedule_id,
  170. cell_product_ids=previous_cell_ids if had_previous_handover else [],
  171. tenant_no=tenant_no,
  172. user_id=user_id,
  173. )
  174. orm_material_svc.save_outbound_material(
  175. db,
  176. schedule_id,
  177. paper_attachments=None,
  178. paper_materials=paper_materials,
  179. remark=material_remark,
  180. cells=cells,
  181. qc_materials=qc_materials,
  182. tenant_no=tenant_no,
  183. user_id=user_id,
  184. create_dept_id=create_dept_id,
  185. )
  186. handover_inv_svc.apply_handover_inventory(
  187. db,
  188. schedule_id,
  189. cells=cells,
  190. qc_materials=qc_materials,
  191. tenant_no=tenant_no,
  192. user_id=user_id,
  193. create_dept_id=create_dept_id,
  194. receiver_person=receiver_person,
  195. )
  196. now = datetime.now()
  197. row = get_handover_by_schedule(db, schedule_id, tenant_no)
  198. if not row:
  199. row = BizOutboundHandover(
  200. id=generate_uuid(),
  201. infusion_schedule_id=schedule_id,
  202. tenant_no=tenant_no,
  203. order_num=0,
  204. create_by=user_id,
  205. create_dept_id=create_dept_id or "",
  206. create_time=now,
  207. del_flag=0,
  208. )
  209. db.add(row)
  210. db.flush()
  211. else:
  212. row.update_by = user_id
  213. row.update_time = now
  214. row.handover_user_id = handover_user_id
  215. row.handover_person = handover_person
  216. row.handover_time = handover_time
  217. row.handover_location = handover_location or None
  218. row.receiver_person = receiver_person
  219. row.cell_purpose = cell_purpose or None
  220. row.attachments = attachments or None
  221. row.remark = handover_remark or None
  222. db.add(row)
  223. db.flush()
  224. return get_outbound_handover(db, schedule_id, tenant_no)