"""预签约订单 / 意向金转账 / 转正式 API""" from __future__ import annotations from datetime import datetime from typing import Any, Dict, List, Optional from fastapi import APIRouter, Depends, Query from pydantic import BaseModel, ConfigDict, Field, field_validator from sqlalchemy.orm import Session from app.core.database import get_db from app.core.data_scope import get_user_dept_id from app.core.menu_guard import require_admin_or_super_admin from app.core.response import error_response, success_response from app.crud import presign_order as po_svc 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 def _get_current_user_id(user: dict) -> str: user_id = user.get("user_id") if not user_id: raise PermissionError("用户身份信息缺失") return str(user_id) class PresignOrderBody(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) order_date: Optional[str] = Field(None, alias="orderDate") protocol_no: Optional[str] = Field(None, alias="protocolNo") mother_name: Optional[str] = Field(None, alias="motherName") expected_delivery_date: Optional[str] = Field(None, alias="expectedDeliveryDate") region_id: Optional[str] = Field(None, alias="regionId") hospital_id: Optional[str] = Field(None, alias="hospitalId") sample_collect_time: Optional[str] = Field(None, alias="sampleCollectTime") pay_mode: Optional[str] = Field(None, alias="payMode") contract_amount: Optional[float] = Field(None, alias="contractAmount") collector_user_id: Optional[str] = Field(None, alias="collectorUserId") sales_user_id: Optional[str] = Field(None, alias="salesUserId") region_manager_user_id: Optional[str] = Field(None, alias="regionManagerUserId") order_status: Optional[str] = Field(None, alias="orderStatus") remark: Optional[str] = None sample_type_ids: Optional[List[str]] = Field(None, alias="sampleTypeIds") transfers: Optional[List[Dict[str, Any]]] = None @field_validator("order_date", "expected_delivery_date") @classmethod def validate_ymd(cls, value: Optional[str]) -> Optional[str]: if value in (None, ""): return value text = value.strip() if len(text) != 8 or not text.isdigit(): raise ValueError("日期必须使用 YYYYMMDD 格式") try: datetime.strptime(text, "%Y%m%d") except ValueError as exc: raise ValueError("日期必须是有效的 YYYYMMDD") from exc return text class PresignTransferBody(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) transfer_amount: Optional[float] = Field(None, alias="transferAmount") transfer_time: Optional[str] = Field(None, alias="transferTime") transfer_type: Optional[str] = Field(None, alias="transferType") transfer_attachments: Optional[str] = Field(None, alias="transferAttachments") remark: Optional[str] = None class FinanceConfirmBody(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) finance_confirm_attachments: Optional[str] = Field( None, alias="financeConfirmAttachments" ) finance_remark: Optional[str] = Field(None, alias="financeRemark") @router.get("/presign-orders") async def list_presign_orders( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100, alias="pageSize"), keyword: Optional[str] = None, orderStatus: Optional[str] = Query(None, alias="orderStatus"), user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) items, total = po_svc.list_presign_orders( db, tenant_no=tenant_no, page=page, page_size=pageSize, keyword=keyword, order_status=orderStatus, user_info=user, ) return success_response( {"list": items, "total": total, "page": page, "pageSize": pageSize} ) except PermissionError as e: return error_response(403, str(e)) @router.get("/presign-orders/{order_id}") async def get_presign_order( order_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access(db, order_id, tenant_no, user) data = po_svc.get_presign_order(db, order_id=order_id, tenant_no=tenant_no) if not data: return error_response(404, "预签约订单不存在") return success_response(data) except PermissionError as e: return error_response(403, str(e)) @router.post("/presign-orders") async def create_presign_order( body: PresignOrderBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) dumped = body.model_dump(by_alias=False) obj = po_svc.create_presign_order( db, create_dept_id=get_user_dept_id(db, user_id) or "", tenant_no=tenant_no, user_id=user_id, fields=dumped, sample_type_ids=body.sample_type_ids, transfers=body.transfers, ) return success_response( po_svc.order_to_dict(db, obj, tenant_no, with_children=True), message="创建成功", ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.put("/presign-orders/{order_id}") async def update_presign_order( order_id: str, body: PresignOrderBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access(db, order_id, tenant_no, user) user_id = _get_current_user_id(user) dumped = body.model_dump(by_alias=False, exclude_unset=True) sample_type_ids = dumped.pop("sample_type_ids", None) dumped.pop("transfers", None) obj = po_svc.update_presign_order( db, create_dept_id=get_user_dept_id(db, user_id) or "", order_id=order_id, tenant_no=tenant_no, user_id=user_id, fields=dumped, sample_type_ids=sample_type_ids, ) return success_response( po_svc.order_to_dict(db, obj, tenant_no, with_children=True), message="保存成功", ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.delete("/presign-orders/{order_id}") async def delete_presign_order( order_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access(db, order_id, tenant_no, user) user_id = _get_current_user_id(user) po_svc.soft_delete_presign_order( db, order_id=order_id, tenant_no=tenant_no, user_id=user_id ) return success_response(None, message="删除成功") except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.post("/presign-orders/{order_id}/convert") async def convert_presign_order( order_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access(db, order_id, tenant_no, user) user_id = _get_current_user_id(user) data = po_svc.convert_to_protocol( db, order_id=order_id, tenant_no=tenant_no, user_id=user_id, create_dept_id=get_user_dept_id(db, user_id) or "", ) return success_response(data, message="已转正式协议") except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.post("/presign-orders/{order_id}/transfers") async def create_presign_transfer( order_id: str, body: PresignTransferBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access(db, order_id, tenant_no, user) user_id = _get_current_user_id(user) obj = po_svc.add_transfer( db, create_dept_id=get_user_dept_id(db, user_id) or "", presign_order_id=order_id, tenant_no=tenant_no, user_id=user_id, fields=body.model_dump(by_alias=False), ) return success_response( po_svc._transfer_to_dict(db, obj, tenant_no), message="添加成功" ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.put("/presign-transfers/{transfer_id}") async def update_presign_transfer( transfer_id: str, body: PresignTransferBody, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access( db, transfer_id, tenant_no, user, transfer=True ) user_id = _get_current_user_id(user) obj = po_svc.update_transfer_draft( db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id, fields=body.model_dump(by_alias=False, exclude_unset=True), ) return success_response( po_svc._transfer_to_dict(db, obj, tenant_no), message="保存成功" ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.post("/presign-transfers/{transfer_id}/submit-receipt-finance") async def submit_presign_transfer_finance( transfer_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access( db, transfer_id, tenant_no, user, transfer=True ) user_id = _get_current_user_id(user) obj = po_svc.submit_receipt_finance( db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id ) return success_response( po_svc._transfer_to_dict(db, obj, tenant_no), message="已提交财务确认" ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.post("/presign-transfers/{transfer_id}/withdraw") async def withdraw_presign_transfer( transfer_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access( db, transfer_id, tenant_no, user, transfer=True ) user_id = _get_current_user_id(user) obj = po_svc.withdraw_receipt( db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id ) return success_response( po_svc._transfer_to_dict(db, obj, tenant_no), message="已撤回" ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.get("/presign-transfers/receipt-confirm") async def list_presign_receipt_confirm( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100, alias="pageSize"), keyword: Optional[str] = None, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) items, total = po_svc.list_presign_transfers_for_finance( db, pay_status="jfqrz", tenant_no=tenant_no, keyword=keyword, page=page, page_size=pageSize, user_info=user, ) return success_response( {"list": items, "total": total, "page": page, "pageSize": pageSize} ) except PermissionError as e: return error_response(403, str(e)) @router.get("/presign-transfers/receipt-confirmed") async def list_presign_receipt_confirmed( page: int = Query(1, ge=1), pageSize: int = Query(10, ge=1, le=100, alias="pageSize"), keyword: Optional[str] = None, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) items, total = po_svc.list_presign_transfers_for_finance( db, pay_status="yjf", tenant_no=tenant_no, keyword=keyword, page=page, page_size=pageSize, user_info=user, ) return success_response( {"list": items, "total": total, "page": page, "pageSize": pageSize} ) except PermissionError as e: return error_response(403, str(e)) @router.post("/presign-transfers/{transfer_id}/receipt-confirm") async def confirm_presign_transfer( transfer_id: str, body: Optional[FinanceConfirmBody] = None, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access( db, transfer_id, tenant_no, user, transfer=True ) user_id = _get_current_user_id(user) obj = po_svc.confirm_receipt( db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id, finance_confirm_attachments=body.finance_confirm_attachments if body else None, finance_remark=body.finance_remark if body else None, ) return success_response( po_svc._transfer_to_dict(db, obj, tenant_no), message="确认收款成功" ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e)) @router.post("/presign-transfers/{transfer_id}/receipt-reject") async def reject_presign_transfer( transfer_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) po_svc.assert_presign_data_access( db, transfer_id, tenant_no, user, transfer=True ) user_id = _get_current_user_id(user) obj = po_svc.reject_receipt( db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id ) return success_response( po_svc._transfer_to_dict(db, obj, tenant_no), message="已退回" ) except PermissionError as e: return error_response(403, str(e)) except ValueError as e: return error_response(400, str(e))