"""车辆尿素 API""" from datetime import date, datetime from typing import List, Optional from pydantic import BaseModel, Field from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.orm import Session from app.core.database import get_db from app.core.db_auth import DbUserContext, get_db_current_user from app.core.tenant_context import get_tenant_no from app.utils.excel_export import ( EXPORT_MAX_ROWS, ExportColumn, ExportValidationError, export_rows_to_xlsx_response, filter_export_columns, ) from app.schemas.pl.vehicle_urea import ( VehicleUreaApplyCreate, VehicleUreaApplyListResponse, VehicleUreaApplyRead, VehicleUreaApplyUpdate, VehicleUreaDisburseBoardListResponse, VehicleUreaDisburseCreate, VehicleUreaDisburseRead, VehicleUreaDisburseUpdate, ) from app.services import pl_vehicle_urea_service as svc apply_router = APIRouter(prefix="/vehicle-urea-apply", tags=["运输计划-车辆尿素申请"]) disburse_router = APIRouter(prefix="/vehicle-urea-disburse", tags=["运输计划-车辆尿素发放"]) @apply_router.get("/", response_model=VehicleUreaApplyListResponse) def list_applies( q: Optional[str] = Query(default=None), status: Optional[str] = Query(default=None), apply_date_from: Optional[date] = Query(default=None, alias="applyDateFrom"), apply_date_to: Optional[date] = Query(default=None, alias="applyDateTo"), applicant_id: Optional[str] = Query(default=None, alias="applicantId"), page: int = Query(default=1, ge=1), page_size: int = Query(default=20, ge=1, le=500, alias="pageSize"), db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaApplyListResponse: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) items, total = svc.list_applies( db, tenant_no, user, q=q, status=status, apply_date_from=apply_date_from, apply_date_to=apply_date_to, applicant_id=applicant_id, page=page, page_size=page_size, ) return VehicleUreaApplyListResponse( items=items, total=total, page=page, pageSize=page_size ) UREA_APPLY_EXPORT_COLUMNS = [ ExportColumn(title="申请单号", field="applyNo"), ExportColumn(title="车牌号", field="licensePlate"), ExportColumn(title="申请时间", field="applyDate"), ExportColumn(title="入库时间", field="warehouseInDate"), ExportColumn(title="单位", field="unit"), ExportColumn(title="入库数量", field="quantity"), ExportColumn(title="申请人", field="applicantName"), ExportColumn(title="状态", field="status"), ] class UreaApplyExportBody(BaseModel): q: Optional[str] = None status: Optional[str] = None apply_date_from: Optional[date] = Field(default=None, alias="applyDateFrom") apply_date_to: Optional[date] = Field(default=None, alias="applyDateTo") ids: Optional[List[str]] = None columns: Optional[List[str]] = None model_config = {"populate_by_name": True} @apply_router.post("/export") def export_urea_applies( body: UreaApplyExportBody, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ): try: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) items, _ = svc.list_applies( db, tenant_no, user, q=body.q, status=body.status, apply_date_from=body.apply_date_from, apply_date_to=body.apply_date_to, ids=body.ids, page=1, page_size=EXPORT_MAX_ROWS, max_page_size=EXPORT_MAX_ROWS, ) rows = [] for item in items: data = item.model_dump(by_alias=True) for k, v in list(data.items()): if hasattr(v, 'isoformat'): data[k] = str(v)[:19] rows.append(data) cols = filter_export_columns(UREA_APPLY_EXPORT_COLUMNS, body.columns) date_str = datetime.now().strftime("%Y%m%d") return export_rows_to_xlsx_response( db, tenant_no=tenant_no, sheet_title="尿素申请", columns=cols, rows=rows, filename_stem=f"尿素申请_{date_str}", ) except ExportValidationError as e: raise HTTPException(status_code=400, detail=str(e)) from e @apply_router.get("/{id}", response_model=VehicleUreaApplyRead) def get_apply( id: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaApplyRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.get_apply(db, tenant_no, user, id) @apply_router.post("/", response_model=VehicleUreaApplyRead, status_code=status.HTTP_201_CREATED) def create_apply( payload: VehicleUreaApplyCreate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaApplyRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.create_apply(db, tenant_no, user, payload) @apply_router.put("/{id}", response_model=VehicleUreaApplyRead) def update_apply( id: str, payload: VehicleUreaApplyUpdate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaApplyRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.update_apply(db, tenant_no, user, id, payload) @apply_router.post("/{id}/cancel", response_model=VehicleUreaApplyRead) def cancel_apply( id: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaApplyRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.cancel_apply(db, tenant_no, user, id) @apply_router.delete("/{ids}", status_code=status.HTTP_204_NO_CONTENT) def delete_applies( ids: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> None: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) apply_ids = [item for item in ids.split(",") if item] svc.delete_applies(db, tenant_no, user, apply_ids) @disburse_router.get("/disburse-board", response_model=VehicleUreaDisburseBoardListResponse) def list_disburse_board( q: Optional[str] = Query(default=None), apply_status: Optional[str] = Query(default=None, alias="applyStatus"), apply_date_from: Optional[date] = Query(default=None, alias="applyDateFrom"), apply_date_to: Optional[date] = Query(default=None, alias="applyDateTo"), disburse_date_from: Optional[date] = Query(default=None, alias="disburseDateFrom"), disburse_date_to: Optional[date] = Query(default=None, alias="disburseDateTo"), page: int = Query(default=1, ge=1), page_size: int = Query(default=20, ge=1, le=500, alias="pageSize"), db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaDisburseBoardListResponse: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) items, total = svc.list_disburse_board( db, tenant_no, user, q=q, apply_status=apply_status, apply_date_from=apply_date_from, apply_date_to=apply_date_to, disburse_date_from=disburse_date_from, disburse_date_to=disburse_date_to, page=page, page_size=page_size, ) return VehicleUreaDisburseBoardListResponse( items=items, total=total, page=page, pageSize=page_size ) UREA_DISBURSE_EXPORT_COLUMNS = [ ExportColumn(title="申请单号", field="applyNo"), ExportColumn(title="车牌号", field="licensePlate"), ExportColumn(title="申请时间", field="applyDate"), ExportColumn(title="入库时间", field="warehouseInDate"), ExportColumn(title="申请人", field="applicantName"), ExportColumn(title="单位", field="unit"), ExportColumn(title="入库数量", field="quantity"), ExportColumn(title="申请状态", field="applyStatus"), ExportColumn(title="发放日期", field="disburseDate"), ExportColumn(title="发放金额", field="amount"), ] class UreaDisburseExportBody(BaseModel): q: Optional[str] = None apply_status: Optional[str] = Field(default=None, alias="applyStatus") apply_date_from: Optional[date] = Field(default=None, alias="applyDateFrom") apply_date_to: Optional[date] = Field(default=None, alias="applyDateTo") disburse_date_from: Optional[date] = Field(default=None, alias="disburseDateFrom") disburse_date_to: Optional[date] = Field(default=None, alias="disburseDateTo") ids: Optional[List[str]] = None columns: Optional[List[str]] = None model_config = {"populate_by_name": True} @disburse_router.post("/disburse-board/export") def export_urea_disburse_board( body: UreaDisburseExportBody, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ): try: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) items, _ = svc.list_disburse_board( db, tenant_no, user, q=body.q, apply_status=body.apply_status, apply_date_from=body.apply_date_from, apply_date_to=body.apply_date_to, disburse_date_from=body.disburse_date_from, disburse_date_to=body.disburse_date_to, ids=body.ids, page=1, page_size=EXPORT_MAX_ROWS, max_page_size=EXPORT_MAX_ROWS, ) rows = [] for item in items: data = item.model_dump(by_alias=True) for k, v in list(data.items()): if hasattr(v, 'isoformat'): data[k] = str(v)[:19] rows.append(data) cols = filter_export_columns(UREA_DISBURSE_EXPORT_COLUMNS, body.columns) date_str = datetime.now().strftime("%Y%m%d") return export_rows_to_xlsx_response( db, tenant_no=tenant_no, sheet_title="尿素发放", columns=cols, rows=rows, filename_stem=f"尿素发放_{date_str}", ) except ExportValidationError as e: raise HTTPException(status_code=400, detail=str(e)) from e @disburse_router.get("/{id}", response_model=VehicleUreaDisburseRead) def get_disburse( id: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaDisburseRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.get_disburse(db, tenant_no, user, id) @disburse_router.post("/", response_model=VehicleUreaDisburseRead, status_code=status.HTTP_201_CREATED) def create_disburse( payload: VehicleUreaDisburseCreate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaDisburseRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.create_disburse(db, tenant_no, user, payload) @disburse_router.put("/{id}", response_model=VehicleUreaDisburseRead) def update_disburse( id: str, payload: VehicleUreaDisburseUpdate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleUreaDisburseRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.update_disburse(db, tenant_no, user, id, payload) @disburse_router.delete("/{ids}", status_code=status.HTTP_204_NO_CONTENT) def delete_disburses( ids: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> None: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) disburse_ids = [item for item in ids.split(",") if item] svc.delete_disburses(db, tenant_no, user, disburse_ids)