refactor(backend): update reimbursement and expense claims
- endpoints/reimbursements.py: update reimbursement API endpoint - schemas/reimbursement.py: update reimbursement data schemas - services/expense_claims.py: update expense claims service logic
This commit is contained in:
@@ -5,13 +5,21 @@ from typing import Annotated
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.api.deps import CurrentUserContext, get_current_user, get_db
|
||||
from app.schemas.common import ErrorResponse
|
||||
from app.schemas.reimbursement import ReimbursementCreate, ReimbursementRead
|
||||
from app.schemas.reimbursement import (
|
||||
ExpenseClaimActionResponse,
|
||||
ExpenseClaimItemUpdate,
|
||||
ExpenseClaimRead,
|
||||
ReimbursementCreate,
|
||||
ReimbursementRead,
|
||||
)
|
||||
from app.services.expense_claims import ExpenseClaimService
|
||||
from app.services.reimbursement import ReimbursementService
|
||||
|
||||
router = APIRouter()
|
||||
DbSession = Annotated[Session, Depends(get_db)]
|
||||
CurrentUser = Annotated[CurrentUserContext, Depends(get_current_user)]
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -35,6 +43,137 @@ def create_reimbursement(payload: ReimbursementCreate, db: DbSession) -> Reimbur
|
||||
return ReimbursementService(db).create_reimbursement(payload)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/claims",
|
||||
response_model=list[ExpenseClaimRead],
|
||||
summary="查询个人报销单列表",
|
||||
description="返回当前登录用户可见的真实个人报销单据列表。",
|
||||
)
|
||||
def list_expense_claims(db: DbSession, current_user: CurrentUser) -> list[ExpenseClaimRead]:
|
||||
return ExpenseClaimService(db).list_claims(current_user)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/claims/{claim_id}",
|
||||
response_model=ExpenseClaimRead,
|
||||
summary="读取个人报销单详情",
|
||||
description="根据报销单主键读取真实报销详情与费用明细。",
|
||||
responses={
|
||||
status.HTTP_404_NOT_FOUND: {
|
||||
"model": ErrorResponse,
|
||||
"description": "报销单不存在。",
|
||||
}
|
||||
},
|
||||
)
|
||||
def get_expense_claim(claim_id: str, db: DbSession, current_user: CurrentUser) -> ExpenseClaimRead:
|
||||
claim = ExpenseClaimService(db).get_claim(claim_id, current_user)
|
||||
if claim is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Claim not found")
|
||||
return claim
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/claims/{claim_id}/items/{item_id}",
|
||||
response_model=ExpenseClaimRead,
|
||||
summary="更新草稿费用明细",
|
||||
description="更新草稿报销单中的单条费用明细。",
|
||||
responses={
|
||||
status.HTTP_404_NOT_FOUND: {
|
||||
"model": ErrorResponse,
|
||||
"description": "报销单或费用明细不存在。",
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST: {
|
||||
"model": ErrorResponse,
|
||||
"description": "草稿状态校验失败或字段校验失败。",
|
||||
},
|
||||
},
|
||||
)
|
||||
def update_expense_claim_item(
|
||||
claim_id: str,
|
||||
item_id: str,
|
||||
payload: ExpenseClaimItemUpdate,
|
||||
db: DbSession,
|
||||
current_user: CurrentUser,
|
||||
) -> ExpenseClaimRead:
|
||||
service = ExpenseClaimService(db)
|
||||
try:
|
||||
claim = service.update_claim_item(
|
||||
claim_id=claim_id,
|
||||
item_id=item_id,
|
||||
payload=payload,
|
||||
current_user=current_user,
|
||||
)
|
||||
except LookupError as error:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(error)) from error
|
||||
except ValueError as error:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(error)) from error
|
||||
|
||||
if claim is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Claim not found")
|
||||
return claim
|
||||
|
||||
|
||||
@router.post(
|
||||
"/claims/{claim_id}/submit",
|
||||
response_model=ExpenseClaimRead,
|
||||
summary="提交个人报销草稿",
|
||||
description="校验草稿信息完整性后,将报销单提交到审批流程。",
|
||||
responses={
|
||||
status.HTTP_404_NOT_FOUND: {
|
||||
"model": ErrorResponse,
|
||||
"description": "报销单不存在。",
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST: {
|
||||
"model": ErrorResponse,
|
||||
"description": "草稿信息不完整或状态不允许提交。",
|
||||
},
|
||||
},
|
||||
)
|
||||
def submit_expense_claim(claim_id: str, db: DbSession, current_user: CurrentUser) -> ExpenseClaimRead:
|
||||
service = ExpenseClaimService(db)
|
||||
try:
|
||||
claim = service.submit_claim(claim_id, current_user)
|
||||
except ValueError as error:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(error)) from error
|
||||
|
||||
if claim is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Claim not found")
|
||||
return claim
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/claims/{claim_id}",
|
||||
response_model=ExpenseClaimActionResponse,
|
||||
summary="删除个人报销草稿",
|
||||
description="删除当前登录用户可见的草稿报销单。",
|
||||
responses={
|
||||
status.HTTP_404_NOT_FOUND: {
|
||||
"model": ErrorResponse,
|
||||
"description": "报销单不存在。",
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST: {
|
||||
"model": ErrorResponse,
|
||||
"description": "仅草稿状态允许删除。",
|
||||
},
|
||||
},
|
||||
)
|
||||
def delete_expense_claim(claim_id: str, db: DbSession, current_user: CurrentUser) -> ExpenseClaimActionResponse:
|
||||
service = ExpenseClaimService(db)
|
||||
try:
|
||||
claim = service.delete_claim(claim_id, current_user)
|
||||
except ValueError as error:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(error)) from error
|
||||
|
||||
if claim is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Claim not found")
|
||||
|
||||
return ExpenseClaimActionResponse(
|
||||
message=f"{claim.claim_no} 草稿已删除。",
|
||||
claim_id=claim.id,
|
||||
status="deleted",
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{request_id}",
|
||||
response_model=ReimbursementRead,
|
||||
|
||||
Reference in New Issue
Block a user