293 lines
11 KiB
Python
293 lines
11 KiB
Python
"""车辆加油登记 API"""
|
|
from datetime import date, datetime
|
|
from typing import Any, List, Optional
|
|
|
|
from fastapi import APIRouter, Body, Depends, HTTPException, Query, Request, status
|
|
from pydantic import BaseModel, Field
|
|
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.schemas.pl.cancel_reason import CancelReasonBody
|
|
from app.schemas.pl.vehicle_fuel import (
|
|
VehicleFuelPlateMonthSummaryResponse,
|
|
VehicleFuelRegisterCreate,
|
|
VehicleFuelRegisterListResponse,
|
|
VehicleFuelRegisterRead,
|
|
VehicleFuelRegisterUpdate,
|
|
)
|
|
from app.services import pl_vehicle_fuel_service as svc
|
|
from app.utils.excel_export import (
|
|
EXPORT_MAX_ROWS,
|
|
ExportColumn,
|
|
ExportValidationError,
|
|
export_rows_to_xlsx_response,
|
|
filter_export_columns,
|
|
)
|
|
|
|
router = APIRouter(prefix="/vehicle-fuel-register", tags=["运输计划-加油登记"])
|
|
|
|
FUEL_CATEGORY_LABELS = {
|
|
"bailian": "百链",
|
|
"gas_station": "加油站",
|
|
"shanxia_factory": "山峡",
|
|
}
|
|
FUEL_STATUS_LABELS = {"normal": "正常", "cancelled": "已作废"}
|
|
|
|
|
|
def _merge_query(request: Request, **known) -> dict:
|
|
out = {k: v for k, v in known.items() if v is not None and v != ""}
|
|
for k, v in request.query_params.multi_items():
|
|
if k in ("page", "pageSize"):
|
|
continue
|
|
if k not in out and v not in (None, ""):
|
|
out[k] = v
|
|
return out
|
|
|
|
|
|
def _param_get(params: dict, *keys: str) -> Any:
|
|
for key in keys:
|
|
if key in params and params[key] not in (None, ""):
|
|
return params[key]
|
|
return None
|
|
|
|
|
|
def _as_bool(value: Any) -> bool:
|
|
if isinstance(value, bool):
|
|
return value
|
|
if value is None:
|
|
return False
|
|
return str(value).strip().lower() in {"1", "true", "yes", "y"}
|
|
|
|
|
|
def _body_to_params(body: BaseModel) -> dict:
|
|
data = body.model_dump(by_alias=True, exclude_none=True)
|
|
for key in ("ids", "columns"):
|
|
data.pop(key, None)
|
|
extra = getattr(body, "model_extra", None) or {}
|
|
for key, value in extra.items():
|
|
if key not in data and value not in (None, ""):
|
|
data[key] = value
|
|
return data
|
|
|
|
|
|
def _list_kwargs(params: dict) -> dict:
|
|
return dict(
|
|
q=_param_get(params, "q"),
|
|
register_no=_param_get(params, "registerNo", "register_no"),
|
|
license_plate=_param_get(params, "licensePlate", "license_plate"),
|
|
fuel_category=_param_get(params, "fuelCategory", "fuel_category"),
|
|
fuel_category_in=_param_get(params, "fuelCategoryIn", "fuel_category_in"),
|
|
status=_param_get(params, "status"),
|
|
status_in=_param_get(params, "statusIn", "status_in"),
|
|
register_date_from=_param_get(params, "registerDateFrom", "register_date_from"),
|
|
register_date_to=_param_get(params, "registerDateTo", "register_date_to"),
|
|
registrant_id=_param_get(params, "registrantId", "registrant_id"),
|
|
registrant_name=_param_get(params, "registrantName", "registrant_name"),
|
|
odometer_km_op=_param_get(params, "odometerKmOp", "odometer_km_op"),
|
|
odometer_km=_param_get(params, "odometerKm", "odometer_km"),
|
|
odometer_km_min=_param_get(params, "odometerKmMin", "odometer_km_min"),
|
|
odometer_km_max=_param_get(params, "odometerKmMax", "odometer_km_max"),
|
|
fuel_liters_op=_param_get(params, "fuelLitersOp", "fuel_liters_op"),
|
|
fuel_liters=_param_get(params, "fuelLiters", "fuel_liters"),
|
|
fuel_liters_min=_param_get(params, "fuelLitersMin", "fuel_liters_min"),
|
|
fuel_liters_max=_param_get(params, "fuelLitersMax", "fuel_liters_max"),
|
|
fuel_tons_op=_param_get(params, "fuelTonsOp", "fuel_tons_op"),
|
|
fuel_tons=_param_get(params, "fuelTons", "fuel_tons"),
|
|
fuel_tons_min=_param_get(params, "fuelTonsMin", "fuel_tons_min"),
|
|
fuel_tons_max=_param_get(params, "fuelTonsMax", "fuel_tons_max"),
|
|
create_time_from=_param_get(params, "createTimeFrom", "create_time_from"),
|
|
create_time_to=_param_get(params, "createTimeTo", "create_time_to"),
|
|
mine_only=_as_bool(_param_get(params, "mineOnly", "mine_only")),
|
|
)
|
|
|
|
|
|
@router.get("/", response_model=VehicleFuelRegisterListResponse)
|
|
def list_registers(
|
|
request: Request,
|
|
q: Optional[str] = Query(default=None),
|
|
fuel_category: Optional[str] = Query(default=None, alias="fuelCategory"),
|
|
status: Optional[str] = Query(default=None),
|
|
register_date_from: Optional[date] = Query(default=None, alias="registerDateFrom"),
|
|
register_date_to: Optional[date] = Query(default=None, alias="registerDateTo"),
|
|
registrant_id: Optional[str] = Query(default=None, alias="registrantId"),
|
|
mine_only: Optional[bool] = Query(default=None, alias="mineOnly"),
|
|
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),
|
|
) -> VehicleFuelRegisterListResponse:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
params = _merge_query(
|
|
request,
|
|
q=q,
|
|
fuelCategory=fuel_category,
|
|
status=status,
|
|
registerDateFrom=register_date_from,
|
|
registerDateTo=register_date_to,
|
|
registrantId=registrant_id,
|
|
mineOnly=mine_only,
|
|
)
|
|
items, total = svc.list_registers(
|
|
db,
|
|
tenant_no,
|
|
user,
|
|
**_list_kwargs(params),
|
|
page=page,
|
|
page_size=page_size,
|
|
)
|
|
return VehicleFuelRegisterListResponse(
|
|
items=items, total=total, page=page, pageSize=page_size
|
|
)
|
|
|
|
|
|
FUEL_EXPORT_COLUMNS = [
|
|
ExportColumn(title="序号", field="seqNo"),
|
|
ExportColumn(title="登记单号", field="registerNo"),
|
|
ExportColumn(title="加油类别", field="fuelCategory"),
|
|
ExportColumn(title="日期", field="registerDate"),
|
|
ExportColumn(title="登记人", field="registrantName"),
|
|
ExportColumn(title="车牌号", field="licensePlate"),
|
|
ExportColumn(title="期初公里数", field="odometerKm"),
|
|
ExportColumn(title="加油升数", field="fuelLiters"),
|
|
ExportColumn(title="加油/吨", field="fuelTons"),
|
|
ExportColumn(title="状态", field="status"),
|
|
ExportColumn(title="备注", field="remark"),
|
|
ExportColumn(title="创建时间", field="createTime"),
|
|
]
|
|
|
|
|
|
class FuelRegisterExportBody(BaseModel):
|
|
q: Optional[str] = None
|
|
fuel_category: Optional[str] = Field(default=None, alias="fuelCategory")
|
|
status: Optional[str] = None
|
|
register_date_from: Optional[date] = Field(default=None, alias="registerDateFrom")
|
|
register_date_to: Optional[date] = Field(default=None, alias="registerDateTo")
|
|
registrant_id: Optional[str] = Field(default=None, alias="registrantId")
|
|
mine_only: Optional[bool] = Field(default=None, alias="mineOnly")
|
|
ids: Optional[List[str]] = None
|
|
columns: Optional[List[str]] = None
|
|
model_config = {"populate_by_name": True, "extra": "allow"}
|
|
|
|
|
|
@router.post("/export")
|
|
def export_registers(
|
|
body: FuelRegisterExportBody,
|
|
db: Session = Depends(get_db),
|
|
user: DbUserContext = Depends(get_db_current_user),
|
|
):
|
|
try:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
params = _body_to_params(body)
|
|
items, _ = svc.list_registers(
|
|
db,
|
|
tenant_no,
|
|
user,
|
|
**_list_kwargs(params),
|
|
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)
|
|
data["fuelCategory"] = FUEL_CATEGORY_LABELS.get(
|
|
data.get("fuelCategory") or "", data.get("fuelCategory")
|
|
)
|
|
data["status"] = FUEL_STATUS_LABELS.get(
|
|
data.get("status") or "", data.get("status")
|
|
)
|
|
for k, v in list(data.items()):
|
|
if hasattr(v, "isoformat"):
|
|
data[k] = str(v)[:19]
|
|
rows.append(data)
|
|
|
|
cols = filter_export_columns(FUEL_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
|
|
|
|
|
|
@router.get("/plate-month-summary", response_model=VehicleFuelPlateMonthSummaryResponse)
|
|
def plate_month_summary(
|
|
db: Session = Depends(get_db),
|
|
user: DbUserContext = Depends(get_db_current_user),
|
|
) -> VehicleFuelPlateMonthSummaryResponse:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
return svc.list_plate_month_summary(db, tenant_no, user)
|
|
|
|
|
|
@router.get("/{id}", response_model=VehicleFuelRegisterRead)
|
|
def get_register(
|
|
id: str,
|
|
db: Session = Depends(get_db),
|
|
user: DbUserContext = Depends(get_db_current_user),
|
|
) -> VehicleFuelRegisterRead:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
return svc.get_register(db, tenant_no, user, id)
|
|
|
|
|
|
@router.post("/", response_model=VehicleFuelRegisterRead, status_code=status.HTTP_201_CREATED)
|
|
def create_register(
|
|
payload: VehicleFuelRegisterCreate,
|
|
mine_only: bool = Query(default=False, alias="mineOnly"),
|
|
db: Session = Depends(get_db),
|
|
user: DbUserContext = Depends(get_db_current_user),
|
|
) -> VehicleFuelRegisterRead:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
return svc.create_register(db, tenant_no, user, payload, force_self=mine_only)
|
|
|
|
|
|
@router.put("/{id}", response_model=VehicleFuelRegisterRead)
|
|
def update_register(
|
|
id: str,
|
|
payload: VehicleFuelRegisterUpdate,
|
|
mine_only: bool = Query(default=False, alias="mineOnly"),
|
|
db: Session = Depends(get_db),
|
|
user: DbUserContext = Depends(get_db_current_user),
|
|
) -> VehicleFuelRegisterRead:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
return svc.update_register(
|
|
db, tenant_no, user, id, payload, force_self=mine_only
|
|
)
|
|
|
|
|
|
@router.post("/{id}/cancel", response_model=VehicleFuelRegisterRead)
|
|
def cancel_register(
|
|
id: str,
|
|
body: Optional[CancelReasonBody] = Body(default=None),
|
|
mine_only: bool = Query(default=False, alias="mineOnly"),
|
|
db: Session = Depends(get_db),
|
|
user: DbUserContext = Depends(get_db_current_user),
|
|
) -> VehicleFuelRegisterRead:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
return svc.cancel_register(
|
|
db,
|
|
tenant_no,
|
|
user,
|
|
id,
|
|
force_self=mine_only,
|
|
cancel_reason=body.cancel_reason if body else None,
|
|
)
|
|
|
|
|
|
@router.post("/{id}/restore", response_model=VehicleFuelRegisterRead)
|
|
def restore_register(
|
|
id: str,
|
|
mine_only: bool = Query(default=False, alias="mineOnly"),
|
|
db: Session = Depends(get_db),
|
|
user: DbUserContext = Depends(get_db_current_user),
|
|
) -> VehicleFuelRegisterRead:
|
|
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
return svc.restore_register(db, tenant_no, user, id, force_self=mine_only)
|