presign_order.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. """预签约订单 / 意向金转账 / 转正式 API"""
  2. from __future__ import annotations
  3. from datetime import datetime
  4. from typing import Any, Dict, List, Optional
  5. from fastapi import APIRouter, Depends, Query
  6. from pydantic import BaseModel, ConfigDict, Field, field_validator
  7. from sqlalchemy.orm import Session
  8. from app.core.database import get_db
  9. from app.core.data_scope import get_user_dept_id
  10. from app.core.menu_guard import require_admin_or_super_admin
  11. from app.core.response import error_response, success_response
  12. from app.crud import presign_order as po_svc
  13. from app.models.models import SysUser
  14. router = APIRouter(prefix="/web/admin", tags=["细胞存储-预签约订单"])
  15. def _get_tenant_no(db: Session, user: dict) -> str:
  16. user_id = user.get("user_id")
  17. if not user_id:
  18. raise PermissionError("用户身份信息缺失,无法确定租户")
  19. sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
  20. if not sys_user or not sys_user.tenant_no:
  21. raise PermissionError("用户租户信息缺失")
  22. return sys_user.tenant_no
  23. def _get_current_user_id(user: dict) -> str:
  24. user_id = user.get("user_id")
  25. if not user_id:
  26. raise PermissionError("用户身份信息缺失")
  27. return str(user_id)
  28. class PresignOrderBody(BaseModel):
  29. model_config = ConfigDict(extra="ignore", populate_by_name=True)
  30. order_date: Optional[str] = Field(None, alias="orderDate")
  31. protocol_no: Optional[str] = Field(None, alias="protocolNo")
  32. mother_name: Optional[str] = Field(None, alias="motherName")
  33. expected_delivery_date: Optional[str] = Field(None, alias="expectedDeliveryDate")
  34. region_id: Optional[str] = Field(None, alias="regionId")
  35. hospital_id: Optional[str] = Field(None, alias="hospitalId")
  36. sample_collect_time: Optional[str] = Field(None, alias="sampleCollectTime")
  37. pay_mode: Optional[str] = Field(None, alias="payMode")
  38. contract_amount: Optional[float] = Field(None, alias="contractAmount")
  39. collector_user_id: Optional[str] = Field(None, alias="collectorUserId")
  40. sales_user_id: Optional[str] = Field(None, alias="salesUserId")
  41. region_manager_user_id: Optional[str] = Field(None, alias="regionManagerUserId")
  42. order_status: Optional[str] = Field(None, alias="orderStatus")
  43. remark: Optional[str] = None
  44. sample_type_ids: Optional[List[str]] = Field(None, alias="sampleTypeIds")
  45. transfers: Optional[List[Dict[str, Any]]] = None
  46. @field_validator("order_date", "expected_delivery_date")
  47. @classmethod
  48. def validate_ymd(cls, value: Optional[str]) -> Optional[str]:
  49. if value in (None, ""):
  50. return value
  51. text = value.strip()
  52. if len(text) != 8 or not text.isdigit():
  53. raise ValueError("日期必须使用 YYYYMMDD 格式")
  54. try:
  55. datetime.strptime(text, "%Y%m%d")
  56. except ValueError as exc:
  57. raise ValueError("日期必须是有效的 YYYYMMDD") from exc
  58. return text
  59. class PresignTransferBody(BaseModel):
  60. model_config = ConfigDict(extra="ignore", populate_by_name=True)
  61. transfer_amount: Optional[float] = Field(None, alias="transferAmount")
  62. transfer_time: Optional[str] = Field(None, alias="transferTime")
  63. transfer_type: Optional[str] = Field(None, alias="transferType")
  64. transfer_attachments: Optional[str] = Field(None, alias="transferAttachments")
  65. remark: Optional[str] = None
  66. class FinanceConfirmBody(BaseModel):
  67. model_config = ConfigDict(extra="ignore", populate_by_name=True)
  68. finance_confirm_attachments: Optional[str] = Field(
  69. None, alias="financeConfirmAttachments"
  70. )
  71. finance_remark: Optional[str] = Field(None, alias="financeRemark")
  72. @router.get("/presign-orders")
  73. async def list_presign_orders(
  74. page: int = Query(1, ge=1),
  75. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  76. keyword: Optional[str] = None,
  77. orderStatus: Optional[str] = Query(None, alias="orderStatus"),
  78. user: dict = Depends(require_admin_or_super_admin),
  79. db: Session = Depends(get_db),
  80. ):
  81. try:
  82. tenant_no = _get_tenant_no(db, user)
  83. items, total = po_svc.list_presign_orders(
  84. db,
  85. tenant_no=tenant_no,
  86. page=page,
  87. page_size=pageSize,
  88. keyword=keyword,
  89. order_status=orderStatus,
  90. user_info=user,
  91. )
  92. return success_response(
  93. {"list": items, "total": total, "page": page, "pageSize": pageSize}
  94. )
  95. except PermissionError as e:
  96. return error_response(403, str(e))
  97. @router.get("/presign-orders/{order_id}")
  98. async def get_presign_order(
  99. order_id: str,
  100. user: dict = Depends(require_admin_or_super_admin),
  101. db: Session = Depends(get_db),
  102. ):
  103. try:
  104. tenant_no = _get_tenant_no(db, user)
  105. po_svc.assert_presign_data_access(db, order_id, tenant_no, user)
  106. data = po_svc.get_presign_order(db, order_id=order_id, tenant_no=tenant_no)
  107. if not data:
  108. return error_response(404, "预签约订单不存在")
  109. return success_response(data)
  110. except PermissionError as e:
  111. return error_response(403, str(e))
  112. @router.post("/presign-orders")
  113. async def create_presign_order(
  114. body: PresignOrderBody,
  115. user: dict = Depends(require_admin_or_super_admin),
  116. db: Session = Depends(get_db),
  117. ):
  118. try:
  119. tenant_no = _get_tenant_no(db, user)
  120. user_id = _get_current_user_id(user)
  121. dumped = body.model_dump(by_alias=False)
  122. obj = po_svc.create_presign_order(
  123. db,
  124. create_dept_id=get_user_dept_id(db, user_id) or "",
  125. tenant_no=tenant_no,
  126. user_id=user_id,
  127. fields=dumped,
  128. sample_type_ids=body.sample_type_ids,
  129. transfers=body.transfers,
  130. )
  131. return success_response(
  132. po_svc.order_to_dict(db, obj, tenant_no, with_children=True),
  133. message="创建成功",
  134. )
  135. except PermissionError as e:
  136. return error_response(403, str(e))
  137. except ValueError as e:
  138. return error_response(400, str(e))
  139. @router.put("/presign-orders/{order_id}")
  140. async def update_presign_order(
  141. order_id: str,
  142. body: PresignOrderBody,
  143. user: dict = Depends(require_admin_or_super_admin),
  144. db: Session = Depends(get_db),
  145. ):
  146. try:
  147. tenant_no = _get_tenant_no(db, user)
  148. po_svc.assert_presign_data_access(db, order_id, tenant_no, user)
  149. user_id = _get_current_user_id(user)
  150. dumped = body.model_dump(by_alias=False, exclude_unset=True)
  151. sample_type_ids = dumped.pop("sample_type_ids", None)
  152. dumped.pop("transfers", None)
  153. obj = po_svc.update_presign_order(
  154. db,
  155. create_dept_id=get_user_dept_id(db, user_id) or "",
  156. order_id=order_id,
  157. tenant_no=tenant_no,
  158. user_id=user_id,
  159. fields=dumped,
  160. sample_type_ids=sample_type_ids,
  161. )
  162. return success_response(
  163. po_svc.order_to_dict(db, obj, tenant_no, with_children=True),
  164. message="保存成功",
  165. )
  166. except PermissionError as e:
  167. return error_response(403, str(e))
  168. except ValueError as e:
  169. return error_response(400, str(e))
  170. @router.delete("/presign-orders/{order_id}")
  171. async def delete_presign_order(
  172. order_id: str,
  173. user: dict = Depends(require_admin_or_super_admin),
  174. db: Session = Depends(get_db),
  175. ):
  176. try:
  177. tenant_no = _get_tenant_no(db, user)
  178. po_svc.assert_presign_data_access(db, order_id, tenant_no, user)
  179. user_id = _get_current_user_id(user)
  180. po_svc.soft_delete_presign_order(
  181. db, order_id=order_id, tenant_no=tenant_no, user_id=user_id
  182. )
  183. return success_response(None, message="删除成功")
  184. except PermissionError as e:
  185. return error_response(403, str(e))
  186. except ValueError as e:
  187. return error_response(400, str(e))
  188. @router.post("/presign-orders/{order_id}/convert")
  189. async def convert_presign_order(
  190. order_id: str,
  191. user: dict = Depends(require_admin_or_super_admin),
  192. db: Session = Depends(get_db),
  193. ):
  194. try:
  195. tenant_no = _get_tenant_no(db, user)
  196. po_svc.assert_presign_data_access(db, order_id, tenant_no, user)
  197. user_id = _get_current_user_id(user)
  198. data = po_svc.convert_to_protocol(
  199. db, order_id=order_id, tenant_no=tenant_no, user_id=user_id,
  200. create_dept_id=get_user_dept_id(db, user_id) or "",
  201. )
  202. return success_response(data, message="已转正式协议")
  203. except PermissionError as e:
  204. return error_response(403, str(e))
  205. except ValueError as e:
  206. return error_response(400, str(e))
  207. @router.post("/presign-orders/{order_id}/transfers")
  208. async def create_presign_transfer(
  209. order_id: str,
  210. body: PresignTransferBody,
  211. user: dict = Depends(require_admin_or_super_admin),
  212. db: Session = Depends(get_db),
  213. ):
  214. try:
  215. tenant_no = _get_tenant_no(db, user)
  216. po_svc.assert_presign_data_access(db, order_id, tenant_no, user)
  217. user_id = _get_current_user_id(user)
  218. obj = po_svc.add_transfer(
  219. db,
  220. create_dept_id=get_user_dept_id(db, user_id) or "",
  221. presign_order_id=order_id,
  222. tenant_no=tenant_no,
  223. user_id=user_id,
  224. fields=body.model_dump(by_alias=False),
  225. )
  226. return success_response(
  227. po_svc._transfer_to_dict(db, obj, tenant_no), message="添加成功"
  228. )
  229. except PermissionError as e:
  230. return error_response(403, str(e))
  231. except ValueError as e:
  232. return error_response(400, str(e))
  233. @router.put("/presign-transfers/{transfer_id}")
  234. async def update_presign_transfer(
  235. transfer_id: str,
  236. body: PresignTransferBody,
  237. user: dict = Depends(require_admin_or_super_admin),
  238. db: Session = Depends(get_db),
  239. ):
  240. try:
  241. tenant_no = _get_tenant_no(db, user)
  242. po_svc.assert_presign_data_access(
  243. db, transfer_id, tenant_no, user, transfer=True
  244. )
  245. user_id = _get_current_user_id(user)
  246. obj = po_svc.update_transfer_draft(
  247. db,
  248. transfer_id=transfer_id,
  249. tenant_no=tenant_no,
  250. user_id=user_id,
  251. fields=body.model_dump(by_alias=False, exclude_unset=True),
  252. )
  253. return success_response(
  254. po_svc._transfer_to_dict(db, obj, tenant_no), message="保存成功"
  255. )
  256. except PermissionError as e:
  257. return error_response(403, str(e))
  258. except ValueError as e:
  259. return error_response(400, str(e))
  260. @router.post("/presign-transfers/{transfer_id}/submit-receipt-finance")
  261. async def submit_presign_transfer_finance(
  262. transfer_id: str,
  263. user: dict = Depends(require_admin_or_super_admin),
  264. db: Session = Depends(get_db),
  265. ):
  266. try:
  267. tenant_no = _get_tenant_no(db, user)
  268. po_svc.assert_presign_data_access(
  269. db, transfer_id, tenant_no, user, transfer=True
  270. )
  271. user_id = _get_current_user_id(user)
  272. obj = po_svc.submit_receipt_finance(
  273. db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id
  274. )
  275. return success_response(
  276. po_svc._transfer_to_dict(db, obj, tenant_no), message="已提交财务确认"
  277. )
  278. except PermissionError as e:
  279. return error_response(403, str(e))
  280. except ValueError as e:
  281. return error_response(400, str(e))
  282. @router.post("/presign-transfers/{transfer_id}/withdraw")
  283. async def withdraw_presign_transfer(
  284. transfer_id: str,
  285. user: dict = Depends(require_admin_or_super_admin),
  286. db: Session = Depends(get_db),
  287. ):
  288. try:
  289. tenant_no = _get_tenant_no(db, user)
  290. po_svc.assert_presign_data_access(
  291. db, transfer_id, tenant_no, user, transfer=True
  292. )
  293. user_id = _get_current_user_id(user)
  294. obj = po_svc.withdraw_receipt(
  295. db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id
  296. )
  297. return success_response(
  298. po_svc._transfer_to_dict(db, obj, tenant_no), message="已撤回"
  299. )
  300. except PermissionError as e:
  301. return error_response(403, str(e))
  302. except ValueError as e:
  303. return error_response(400, str(e))
  304. @router.get("/presign-transfers/receipt-confirm")
  305. async def list_presign_receipt_confirm(
  306. page: int = Query(1, ge=1),
  307. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  308. keyword: Optional[str] = None,
  309. user: dict = Depends(require_admin_or_super_admin),
  310. db: Session = Depends(get_db),
  311. ):
  312. try:
  313. tenant_no = _get_tenant_no(db, user)
  314. items, total = po_svc.list_presign_transfers_for_finance(
  315. db,
  316. pay_status="jfqrz",
  317. tenant_no=tenant_no,
  318. keyword=keyword,
  319. page=page,
  320. page_size=pageSize,
  321. user_info=user,
  322. )
  323. return success_response(
  324. {"list": items, "total": total, "page": page, "pageSize": pageSize}
  325. )
  326. except PermissionError as e:
  327. return error_response(403, str(e))
  328. @router.get("/presign-transfers/receipt-confirmed")
  329. async def list_presign_receipt_confirmed(
  330. page: int = Query(1, ge=1),
  331. pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
  332. keyword: Optional[str] = None,
  333. user: dict = Depends(require_admin_or_super_admin),
  334. db: Session = Depends(get_db),
  335. ):
  336. try:
  337. tenant_no = _get_tenant_no(db, user)
  338. items, total = po_svc.list_presign_transfers_for_finance(
  339. db,
  340. pay_status="yjf",
  341. tenant_no=tenant_no,
  342. keyword=keyword,
  343. page=page,
  344. page_size=pageSize,
  345. user_info=user,
  346. )
  347. return success_response(
  348. {"list": items, "total": total, "page": page, "pageSize": pageSize}
  349. )
  350. except PermissionError as e:
  351. return error_response(403, str(e))
  352. @router.post("/presign-transfers/{transfer_id}/receipt-confirm")
  353. async def confirm_presign_transfer(
  354. transfer_id: str,
  355. body: Optional[FinanceConfirmBody] = None,
  356. user: dict = Depends(require_admin_or_super_admin),
  357. db: Session = Depends(get_db),
  358. ):
  359. try:
  360. tenant_no = _get_tenant_no(db, user)
  361. po_svc.assert_presign_data_access(
  362. db, transfer_id, tenant_no, user, transfer=True
  363. )
  364. user_id = _get_current_user_id(user)
  365. obj = po_svc.confirm_receipt(
  366. db,
  367. transfer_id=transfer_id,
  368. tenant_no=tenant_no,
  369. user_id=user_id,
  370. finance_confirm_attachments=body.finance_confirm_attachments if body else None,
  371. finance_remark=body.finance_remark if body else None,
  372. )
  373. return success_response(
  374. po_svc._transfer_to_dict(db, obj, tenant_no), message="确认收款成功"
  375. )
  376. except PermissionError as e:
  377. return error_response(403, str(e))
  378. except ValueError as e:
  379. return error_response(400, str(e))
  380. @router.post("/presign-transfers/{transfer_id}/receipt-reject")
  381. async def reject_presign_transfer(
  382. transfer_id: str,
  383. user: dict = Depends(require_admin_or_super_admin),
  384. db: Session = Depends(get_db),
  385. ):
  386. try:
  387. tenant_no = _get_tenant_no(db, user)
  388. po_svc.assert_presign_data_access(
  389. db, transfer_id, tenant_no, user, transfer=True
  390. )
  391. user_id = _get_current_user_id(user)
  392. obj = po_svc.reject_receipt(
  393. db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id
  394. )
  395. return success_response(
  396. po_svc._transfer_to_dict(db, obj, tenant_no), message="已退回"
  397. )
  398. except PermissionError as e:
  399. return error_response(403, str(e))
  400. except ValueError as e:
  401. return error_response(400, str(e))