|
|
@@ -0,0 +1,133 @@
|
|
|
+from typing import List
|
|
|
+from fastapi import APIRouter, Depends, HTTPException, status
|
|
|
+from sqlalchemy import or_
|
|
|
+from sqlalchemy.orm import Session
|
|
|
+
|
|
|
+from app.database import get_db
|
|
|
+from app.core.deps import get_current_user
|
|
|
+from app.models.user import User
|
|
|
+from app.models.reimburse import ReimburseApply
|
|
|
+from app.schemas.common import ApiResponse, PagedData
|
|
|
+from app.schemas.reimburse import ReimburseApplyCreate, ReimburseApplyUpdate, ReimburseApplyOut, ReimburseApprovalCreate, ReimburseApprovalOut
|
|
|
+from app.services.reimburse import ReimburseService
|
|
|
+from app.services.expense_item import ExpenseItemService
|
|
|
+from app.models.workflow import WorkflowDef
|
|
|
+from app.utils.codegen import generate_apply_code
|
|
|
+
|
|
|
+router = APIRouter(prefix="/api/reimburses", tags=["报销管理"])
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/", response_model=ApiResponse[PagedData[ReimburseApplyOut]])
|
|
|
+def list_reimburses(
|
|
|
+ page: int = 1,
|
|
|
+ pageSize: int = 10,
|
|
|
+ status: str | None = None,
|
|
|
+ keyword: str = "",
|
|
|
+ expense_id: int | None = None,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ current_user: User = Depends(get_current_user),
|
|
|
+):
|
|
|
+ filters = [ReimburseApply.del_flag == 0]
|
|
|
+ if status:
|
|
|
+ filters.append(ReimburseApply.status == status)
|
|
|
+ if keyword:
|
|
|
+ filters.append(or_(ReimburseApply.apply_code.contains(keyword), ReimburseApply.applicant_name.contains(keyword)))
|
|
|
+ if expense_id:
|
|
|
+ filters.append(ReimburseApply.expense_id == expense_id)
|
|
|
+ rows, total = ReimburseService.get_paged(db, page=page, page_size=pageSize, filters=filters)
|
|
|
+ out_rows = [ReimburseApplyOut.model_validate(r) for r in rows]
|
|
|
+ paged_data = PagedData(rows=out_rows, total=total, page=page, page_size=pageSize)
|
|
|
+ return ApiResponse(data=paged_data)
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/my", response_model=ApiResponse[PagedData[ReimburseApplyOut]])
|
|
|
+def my_reimburses(
|
|
|
+ page: int = 1,
|
|
|
+ pageSize: int = 10,
|
|
|
+ status: str | None = None,
|
|
|
+ keyword: str = "",
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ current_user: User = Depends(get_current_user),
|
|
|
+):
|
|
|
+ filters = [ReimburseApply.del_flag == 0, ReimburseApply.applicant_id == current_user.id]
|
|
|
+ if status:
|
|
|
+ filters.append(ReimburseApply.status == status)
|
|
|
+ if keyword:
|
|
|
+ filters.append(or_(ReimburseApply.apply_code.contains(keyword), ReimburseApply.applicant_name.contains(keyword)))
|
|
|
+ rows, total = ReimburseService.get_paged(db, page=page, page_size=pageSize, filters=filters)
|
|
|
+ out_rows = [ReimburseApplyOut.model_validate(r) for r in rows]
|
|
|
+ paged_data = PagedData(rows=out_rows, total=total, page=page, page_size=pageSize)
|
|
|
+ return ApiResponse(data=paged_data)
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/{reimburse_id}", response_model=ApiResponse[ReimburseApplyOut])
|
|
|
+def get_reimburse(reimburse_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ reimburse = ReimburseService.get_by_id(db, reimburse_id)
|
|
|
+ if not reimburse:
|
|
|
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
|
|
|
+ return ApiResponse(data=ReimburseApplyOut.model_validate(reimburse))
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/{reimburse_id}/approvals", response_model=ApiResponse[List[ReimburseApprovalOut]])
|
|
|
+def get_approvals(reimburse_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ approvals = ReimburseService.get_approvals(db, reimburse_id)
|
|
|
+ return ApiResponse(data=[ReimburseApprovalOut.model_validate(a) for a in approvals])
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/", response_model=ApiResponse[ReimburseApplyOut])
|
|
|
+def create_reimburse(body: ReimburseApplyCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ data = body.model_dump(exclude_unset=True)
|
|
|
+ data["applicant_id"] = current_user.id
|
|
|
+ data["applicant_name"] = current_user.real_name or current_user.username
|
|
|
+ data["apply_code"] = generate_apply_code(db, "BX")
|
|
|
+ data["status"] = "draft"
|
|
|
+ wf = db.query(WorkflowDef).filter(WorkflowDef.biz_type == "reimburse", WorkflowDef.del_flag == 0).first()
|
|
|
+ if wf:
|
|
|
+ data["wf_id"] = wf.id
|
|
|
+ if "expense_item_id" in data:
|
|
|
+ eid = data.pop("expense_item_id")
|
|
|
+ data["expense_id"] = eid
|
|
|
+ item = ExpenseItemService.get_by_id(db, eid)
|
|
|
+ if item:
|
|
|
+ data["expense_name"] = item.item_name
|
|
|
+ reimburse = ReimburseService.create(db, data)
|
|
|
+ return ApiResponse(data=ReimburseApplyOut.model_validate(reimburse))
|
|
|
+
|
|
|
+
|
|
|
+@router.put("/{reimburse_id}", response_model=ApiResponse[ReimburseApplyOut])
|
|
|
+def update_reimburse(reimburse_id: int, body: ReimburseApplyUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ data = body.model_dump(exclude_unset=True)
|
|
|
+ reimburse = ReimburseService.update(db, reimburse_id, data)
|
|
|
+ if not reimburse:
|
|
|
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
|
|
|
+ return ApiResponse(data=ReimburseApplyOut.model_validate(reimburse))
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/{reimburse_id}/submit", response_model=ApiResponse[ReimburseApplyOut])
|
|
|
+def submit_reimburse(reimburse_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ reimburse = ReimburseService.submit(db, reimburse_id, current_user)
|
|
|
+ if not reimburse:
|
|
|
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
|
|
|
+ return ApiResponse(data=ReimburseApplyOut.model_validate(reimburse))
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/{reimburse_id}/approve", response_model=ApiResponse[ReimburseApplyOut])
|
|
|
+def approve_reimburse(reimburse_id: int, body: ReimburseApprovalCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ reimburse = ReimburseService.approve(db, reimburse_id, current_user, body.approval_opinion or "")
|
|
|
+ if not reimburse:
|
|
|
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
|
|
|
+ return ApiResponse(data=ReimburseApplyOut.model_validate(reimburse))
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/{reimburse_id}/reject", response_model=ApiResponse[ReimburseApplyOut])
|
|
|
+def reject_reimburse(reimburse_id: int, body: ReimburseApprovalCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ reimburse = ReimburseService.reject(db, reimburse_id, current_user, body.approval_opinion or "")
|
|
|
+ if not reimburse:
|
|
|
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="报销单不存在")
|
|
|
+ return ApiResponse(data=ReimburseApplyOut.model_validate(reimburse))
|
|
|
+
|
|
|
+
|
|
|
+@router.delete("/{reimburse_id}", response_model=ApiResponse[None])
|
|
|
+def delete_reimburse(reimburse_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
|
|
|
+ ReimburseService.soft_delete(db, reimburse_id)
|
|
|
+ return ApiResponse(data=None)
|