"""车辆 ETC/加油充值 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_recharge import ( VehicleRechargeApplyCreate, VehicleRechargeApplyListResponse, VehicleRechargeApplyOption, VehicleRechargeApplyRead, VehicleRechargeApplyUpdate, VehicleRechargeDisburseBoardListResponse, VehicleRechargeRecordCreate, VehicleRechargeRecordListResponse, VehicleRechargeRecordRead, VehicleRechargeRecordUpdate, ) from app.services import pl_vehicle_recharge_service as svc apply_router = APIRouter(prefix="/vehicle-recharge-apply", tags=["运输计划-ETC加油申请"]) record_router = APIRouter(prefix="/vehicle-recharge-record", tags=["运输计划-ETC加油发放"]) @apply_router.get("/", response_model=VehicleRechargeApplyListResponse) def list_applies( q: Optional[str] = Query(default=None), recharge_type: Optional[str] = Query(default=None, alias="rechargeType"), 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), ) -> VehicleRechargeApplyListResponse: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) items, total = svc.list_applies( db, tenant_no, user, q=q, recharge_type=recharge_type, 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 VehicleRechargeApplyListResponse( items=items, total=total, page=page, pageSize=page_size ) @apply_router.get("/pending-options", response_model=list[VehicleRechargeApplyOption]) def pending_apply_options( db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> list[VehicleRechargeApplyOption]: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) rows = svc.list_pending_apply_options(db, tenant_no, user) return [VehicleRechargeApplyOption.model_validate(row) for row in rows] RECHARGE_APPLY_EXPORT_COLUMNS = [ ExportColumn(title="申请单号", field="applyNo"), ExportColumn(title="车牌号", field="licensePlate"), ExportColumn(title="充值类型", field="rechargeType"), ExportColumn(title="申请日期", field="applyDate"), ExportColumn(title="期初公里数", field="odometerKm"), ExportColumn(title="申请人", field="applicantName"), ExportColumn(title="状态", field="status"), ExportColumn(title="创建时间", field="createTime"), ] class RechargeApplyExportBody(BaseModel): q: Optional[str] = None recharge_type: Optional[str] = Field(default=None, alias="rechargeType") 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_recharge_applies( body: RechargeApplyExportBody, 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, recharge_type=body.recharge_type, 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(RECHARGE_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=VehicleRechargeApplyRead) def get_apply( id: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleRechargeApplyRead: 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=VehicleRechargeApplyRead, status_code=status.HTTP_201_CREATED) def create_apply( payload: VehicleRechargeApplyCreate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleRechargeApplyRead: 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=VehicleRechargeApplyRead) def update_apply( id: str, payload: VehicleRechargeApplyUpdate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleRechargeApplyRead: 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=VehicleRechargeApplyRead) def cancel_apply( id: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleRechargeApplyRead: 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) @record_router.get("/", response_model=VehicleRechargeRecordListResponse) def list_records( q: Optional[str] = Query(default=None), recharge_type: Optional[str] = Query(default=None, alias="rechargeType"), recharge_date_from: Optional[date] = Query(default=None, alias="rechargeDateFrom"), recharge_date_to: Optional[date] = Query(default=None, alias="rechargeDateTo"), gas_station: Optional[str] = Query(default=None, alias="gasStation"), apply_no: Optional[str] = Query(default=None, alias="applyNo"), 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), ) -> VehicleRechargeRecordListResponse: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) items, total = svc.list_records( db, tenant_no, user, q=q, recharge_type=recharge_type, recharge_date_from=recharge_date_from, recharge_date_to=recharge_date_to, gas_station=gas_station, apply_no=apply_no, page=page, page_size=page_size, ) return VehicleRechargeRecordListResponse( items=items, total=total, page=page, pageSize=page_size ) @record_router.get("/disburse-board", response_model=VehicleRechargeDisburseBoardListResponse) def list_disburse_board( q: Optional[str] = Query(default=None), recharge_type: Optional[str] = Query(default=None, alias="rechargeType"), 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"), recharge_date_from: Optional[date] = Query(default=None, alias="rechargeDateFrom"), recharge_date_to: Optional[date] = Query(default=None, alias="rechargeDateTo"), 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), ) -> VehicleRechargeDisburseBoardListResponse: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) items, total = svc.list_disburse_board( db, tenant_no, user, q=q, recharge_type=recharge_type, apply_status=apply_status, apply_date_from=apply_date_from, apply_date_to=apply_date_to, recharge_date_from=recharge_date_from, recharge_date_to=recharge_date_to, page=page, page_size=page_size, ) return VehicleRechargeDisburseBoardListResponse( items=items, total=total, page=page, pageSize=page_size ) RECHARGE_DISBURSE_EXPORT_COLUMNS = [ ExportColumn(title="申请单号", field="applyNo"), ExportColumn(title="车牌号", field="licensePlate"), ExportColumn(title="充值类型", field="rechargeType"), ExportColumn(title="申请日期", field="applyDate"), ExportColumn(title="申请人", field="applicantName"), ExportColumn(title="申请状态", field="applyStatus"), ExportColumn(title="期初公里数", field="odometerKm"), ExportColumn(title="充值日期", field="rechargeDate"), ExportColumn(title="充值金额", field="amount"), ExportColumn(title="加油站", field="gasStation"), ] class RechargeDisburseExportBody(BaseModel): q: Optional[str] = None recharge_type: Optional[str] = Field(default=None, alias="rechargeType") 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") recharge_date_from: Optional[date] = Field(default=None, alias="rechargeDateFrom") recharge_date_to: Optional[date] = Field(default=None, alias="rechargeDateTo") ids: Optional[List[str]] = None columns: Optional[List[str]] = None model_config = {"populate_by_name": True} @record_router.post("/disburse-board/export") def export_recharge_disburse_board( body: RechargeDisburseExportBody, 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, recharge_type=body.recharge_type, apply_status=body.apply_status, apply_date_from=body.apply_date_from, apply_date_to=body.apply_date_to, recharge_date_from=body.recharge_date_from, recharge_date_to=body.recharge_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(RECHARGE_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 @record_router.get("/{id}", response_model=VehicleRechargeRecordRead) def get_record( id: str, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleRechargeRecordRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.get_record(db, tenant_no, user, id) @record_router.post("/", response_model=VehicleRechargeRecordRead, status_code=status.HTTP_201_CREATED) def create_record( payload: VehicleRechargeRecordCreate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleRechargeRecordRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.create_record(db, tenant_no, user, payload) @record_router.put("/{id}", response_model=VehicleRechargeRecordRead) def update_record( id: str, payload: VehicleRechargeRecordUpdate, db: Session = Depends(get_db), user: DbUserContext = Depends(get_db_current_user), ) -> VehicleRechargeRecordRead: tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) return svc.update_record(db, tenant_no, user, id, payload) @record_router.delete("/{ids}", status_code=status.HTTP_204_NO_CONTENT) def delete_records( 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()) record_ids = [item for item in ids.split(",") if item] svc.delete_records(db, tenant_no, user, record_ids)