| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- from typing import List, Optional
- from fastapi import APIRouter, Depends, HTTPException, status
- from pydantic import BaseModel, Field
- from sqlalchemy.orm import Session
- from app.api.bx.export_common import (
- ExportColumn,
- ExportValidationError,
- BxExportBodyBase,
- dump_export_row,
- respond_xlsx,
- status_label,
- )
- from app.core.bx_auth import BxUserContext, get_bx_current_user
- from app.core.database import get_db
- from app.core.tenant_context import get_tenant_no
- from app.schemas.bx.common import ApiResponse
- from app.schemas.bx.expense_item import ExpenseItemCreate, ExpenseItemOut, ExpenseItemUpdate
- from app.services.bx.expense_item import ExpenseItemService
- router = APIRouter(prefix="/expense-items", tags=["费用项目管理"])
- @router.get("/tree", response_model=ApiResponse[List[ExpenseItemOut]])
- def get_expense_item_tree(
- db: Session = Depends(get_db),
- user: BxUserContext = Depends(get_bx_current_user),
- ):
- tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
- tree = ExpenseItemService.get_item_tree(db, tenant_no, user=user)
- return ApiResponse(data=[ExpenseItemOut.model_validate(i) for i in tree])
- EXPENSE_ITEM_EXPORT_COLUMNS = [
- ExportColumn(title="项目名称", field="itemName"),
- ExportColumn(title="项目编码", field="itemCode"),
- ExportColumn(title="层级", field="level"),
- ExportColumn(title="子项数", field="childCount"),
- ExportColumn(title="排序", field="sortOrder"),
- ExportColumn(title="状态", field="status"),
- ]
- class ExpenseItemExportBody(BxExportBodyBase):
- name: Optional[str] = None
- status: Optional[str] = None
- def _flatten_expense_tree(nodes: list, out: list) -> None:
- for n in nodes:
- out.append(n)
- children = getattr(n, "children", None) or (n.get("children") if isinstance(n, dict) else None) or []
- if children:
- _flatten_expense_tree(list(children), out)
- @router.post("/export")
- def export_expense_items(
- body: ExpenseItemExportBody,
- db: Session = Depends(get_db),
- user: BxUserContext = Depends(get_bx_current_user),
- ):
- try:
- tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
- tree = ExpenseItemService.get_item_tree(db, tenant_no, user=user)
- flat: list = []
- _flatten_expense_tree(list(tree), flat)
- outs = [ExpenseItemOut.model_validate(i) for i in flat]
- if body.ids:
- id_set = {str(i) for i in body.ids}
- outs = [o for o in outs if str(o.id) in id_set]
- if body.name:
- kw = body.name.strip()
- outs = [o for o in outs if kw in (o.item_name or "")]
- if body.status not in (None, ""):
- outs = [o for o in outs if str(o.status) == str(body.status)]
- rows = []
- for o in outs:
- data = dump_export_row(o, status_fields=())
- data["status"] = status_label(data.get("status"))
- # childCount may be computed
- if "childCount" not in data:
- data["childCount"] = len(getattr(o, "children", None) or [])
- rows.append(data)
- return respond_xlsx(
- db, tenant_no=tenant_no, sheet_title="费用科目", columns=EXPENSE_ITEM_EXPORT_COLUMNS,
- rows=rows, filename_stem="费用科目", requested_columns=body.columns,
- )
- except ExportValidationError as e:
- raise HTTPException(status_code=400, detail=str(e)) from e
- @router.get("/{item_id}", response_model=ApiResponse[ExpenseItemOut])
- def get_expense_item(
- item_id: str,
- db: Session = Depends(get_db),
- user: BxUserContext = Depends(get_bx_current_user),
- ):
- tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
- item = ExpenseItemService.get_by_id(db, item_id, tenant_no, user=user)
- if not item:
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="费用项目不存在")
- return ApiResponse(data=ExpenseItemOut.model_validate(item))
- @router.post("/", response_model=ApiResponse[ExpenseItemOut])
- def create_expense_item(
- body: ExpenseItemCreate,
- db: Session = Depends(get_db),
- user: BxUserContext = Depends(get_bx_current_user),
- ):
- try:
- item = ExpenseItemService.create(db, body.model_dump(exclude_unset=True), user=user)
- db.commit()
- db.refresh(item)
- except ValueError as exc:
- db.rollback()
- raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
- return ApiResponse(data=ExpenseItemOut.model_validate(item))
- @router.put("/{item_id}", response_model=ApiResponse[ExpenseItemOut])
- def update_expense_item(
- item_id: str,
- body: ExpenseItemUpdate,
- db: Session = Depends(get_db),
- user: BxUserContext = Depends(get_bx_current_user),
- ):
- try:
- item = ExpenseItemService.update(
- db, item_id, body.model_dump(exclude_unset=True), user=user
- )
- if not item:
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="费用项目不存在")
- db.commit()
- db.refresh(item)
- except ValueError as exc:
- db.rollback()
- raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
- return ApiResponse(data=ExpenseItemOut.model_validate(item))
- @router.delete("/{item_id}", response_model=ApiResponse[None])
- def delete_expense_item(
- item_id: str,
- db: Session = Depends(get_db),
- user: BxUserContext = Depends(get_bx_current_user),
- ):
- tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
- item = ExpenseItemService.get_by_id(db, item_id, tenant_no, user=user)
- if not item:
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="费用项目不存在")
- ExpenseItemService.soft_delete(db, item_id, user=user)
- db.commit()
- return ApiResponse(data=None)
|