"""协议级退费登记 API""" from __future__ import annotations from typing import Optional from fastapi import APIRouter, Depends 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, require_strict_menu_code from app.core.response import error_response, success_response from app.crud import protocol_refund_record as rr_svc from app.models.models import ( BizCollectionProtocol, BizProtocolRefundRecord, SysUser, ) router = APIRouter(prefix="/web/admin", tags=["细胞存储-退费登记"]) REFUND_CONFIRM_MENU = "biz:cellStorageRefundConfirm:list" 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) def _protocol_accessible(db: Session, protocol_id: str, tenant_no: str, user: dict) -> bool: from app.core.data_scope import apply_data_scope q = db.query(BizCollectionProtocol).filter( BizCollectionProtocol.id == protocol_id, BizCollectionProtocol.del_flag == 0, BizCollectionProtocol.tenant_no == tenant_no, ) q = apply_data_scope(q, BizCollectionProtocol, user, db, tenant_no) return q.first() is not None class RefundRecordBody(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) refund_amount: float = Field(..., alias="refundAmount") refund_time: Optional[str] = Field(None, alias="refundTime") refund_remark: Optional[str] = Field(None, alias="refundRemark") refund_attachments: Optional[str] = Field(None, alias="refundAttachments") workflow_instance_id: Optional[str] = Field(None, alias="workflowInstanceId") remark: Optional[str] = None class RefundRecordUpdateBody(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) refund_amount: Optional[float] = Field(None, alias="refundAmount") refund_time: Optional[str] = Field(None, alias="refundTime") refund_remark: Optional[str] = Field(None, alias="refundRemark") refund_attachments: Optional[str] = Field(None, alias="refundAttachments") workflow_instance_id: Optional[str] = Field(None, alias="workflowInstanceId") remark: Optional[str] = None @router.get("/collection-protocols/{protocol_id}/refund-records") async def list_protocol_refund_records( protocol_id: str, user: dict = Depends(require_admin_or_super_admin), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) if not _protocol_accessible(db, protocol_id, tenant_no, user): return error_response(404, "协议单不存在或无权查看") data = rr_svc.list_refund_records( db, collection_protocol_id=protocol_id, tenant_no=tenant_no ) return success_response(data) except PermissionError as e: return error_response(403, str(e)) @router.post("/collection-protocols/{protocol_id}/refund-records") async def create_protocol_refund_record( protocol_id: str, body: RefundRecordBody, user: dict = Depends(require_strict_menu_code(REFUND_CONFIRM_MENU)), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) if not _protocol_accessible(db, protocol_id, tenant_no, user): return error_response(404, "协议单不存在或无权操作") obj = rr_svc.create_refund_record( db, collection_protocol_id=protocol_id, tenant_no=tenant_no, user_id=user_id, refund_amount=body.refund_amount, refund_time=body.refund_time, refund_remark=body.refund_remark, refund_attachments=body.refund_attachments, workflow_instance_id=body.workflow_instance_id, remark=body.remark, ) return success_response( rr_svc._record_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("/protocol-refund-records/{record_id}") async def update_protocol_refund_record( record_id: str, body: RefundRecordUpdateBody, user: dict = Depends(require_strict_menu_code(REFUND_CONFIRM_MENU)), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) existing = ( db.query(BizProtocolRefundRecord) .filter( BizProtocolRefundRecord.id == record_id, BizProtocolRefundRecord.tenant_no == tenant_no, BizProtocolRefundRecord.del_flag == 0, ) .first() ) if not existing: return error_response(404, "退费登记不存在") if not _protocol_accessible( db, existing.collection_protocol_id, tenant_no, user ): return error_response(403, "无权操作该协议") dumped = body.model_dump(by_alias=False, exclude_unset=True) obj = rr_svc.update_refund_record( db, record_id=record_id, tenant_no=tenant_no, user_id=user_id, refund_amount=dumped.get("refund_amount"), refund_time=dumped.get("refund_time"), refund_remark=dumped.get("refund_remark"), refund_attachments=dumped.get("refund_attachments"), workflow_instance_id=dumped.get("workflow_instance_id"), remark=dumped.get("remark"), fields_set=set(dumped.keys()), ) return success_response( rr_svc._record_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.delete("/protocol-refund-records/{record_id}") async def delete_protocol_refund_record( record_id: str, user: dict = Depends(require_strict_menu_code(REFUND_CONFIRM_MENU)), db: Session = Depends(get_db), ): try: tenant_no = _get_tenant_no(db, user) user_id = _get_current_user_id(user) existing = ( db.query(BizProtocolRefundRecord) .filter( BizProtocolRefundRecord.id == record_id, BizProtocolRefundRecord.tenant_no == tenant_no, BizProtocolRefundRecord.del_flag == 0, ) .first() ) if not existing: return error_response(404, "退费登记不存在") if not _protocol_accessible( db, existing.collection_protocol_id, tenant_no, user ): return error_response(403, "无权操作该协议") rr_svc.soft_delete_refund_record( db, record_id=record_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))