Files
shanxia2.0/backend/app/api/pl/transport_address.py
T
zlwuandCursor 31b0db6840 feat(bx): 报销可代选申请人,并放宽主数据选项的数据权限过滤
支持具备 changeApplicant 权限时代填申请人及发票/余额联动;系统/CRM/BX/PL 主数据与下拉选项改为仅租户隔离,避免「仅本人」导致空选项。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 23:27:23 +08:00

193 lines
6.6 KiB
Python

"""运输通用地址库 API"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import case, func, or_
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.delete_guard import assert_row_deletable_by_creator, user_info_from_context
from app.core.tenant_context import get_tenant_no
from app.core.utils import generate_uuid
from app.models.pl_models import PlTransportAddress
from app.schemas.pl.transport_address import (
TransportAddressCreate,
TransportAddressListResponse,
TransportAddressRead,
TransportAddressUpdate,
)
router = APIRouter(tags=["运输地址库"])
def _address_to_read(row: PlTransportAddress) -> TransportAddressRead:
return TransportAddressRead.model_validate(row)
def _get_address(
db: Session, tenant_no: str, address_id: str, user: DbUserContext
) -> PlTransportAddress:
row = (
db.query(PlTransportAddress)
.filter(
PlTransportAddress.id == address_id,
PlTransportAddress.tenant_no == tenant_no,
PlTransportAddress.del_flag == 0,
)
.first()
)
if not row:
raise HTTPException(status_code=404, detail="地址不存在")
return row
def _build_address_query(db: Session, tenant_no: str, user: DbUserContext):
# 运输地址为主数据选项,不做 create_by 数据范围过滤
_ = user
query = db.query(PlTransportAddress).filter(
PlTransportAddress.tenant_no == tenant_no,
PlTransportAddress.del_flag == 0,
)
return query
@router.get("/address", response_model=TransportAddressListResponse)
def list_transport_addresses(
q: Optional[str] = Query(default=None, description="搜索名称或地址"),
status: Optional[str] = Query(default=None, description="状态 1-启用 0-停用"),
page: int = Query(default=1, ge=1),
page_size: int = Query(default=20, ge=1, le=100, alias="pageSize"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> TransportAddressListResponse:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
query = _build_address_query(db, tenant_no, user)
if status:
query = query.filter(PlTransportAddress.status == status)
if q:
keyword = q.strip().lower()
query = query.filter(
or_(
func.lower(PlTransportAddress.address_name).contains(keyword),
func.lower(PlTransportAddress.full_address).contains(keyword),
func.lower(PlTransportAddress.contact_name).contains(keyword),
PlTransportAddress.contact_phone.contains(q.strip()),
)
)
query = query.order_by(
case((PlTransportAddress.order_num.is_(None), 1), else_=0).asc(),
PlTransportAddress.create_time.desc(),
)
total = query.count()
offset = (page - 1) * page_size
rows = query.offset(offset).limit(page_size).all()
return TransportAddressListResponse(
total=total,
list=[_address_to_read(row) for row in rows],
)
@router.get("/address/all", response_model=TransportAddressListResponse)
def list_all_transport_addresses(
q: Optional[str] = Query(default=None, description="搜索名称或地址"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> TransportAddressListResponse:
"""选择器用:仅返回启用中的地址(不分页)。"""
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
query = _build_address_query(db, tenant_no, user).filter(PlTransportAddress.status == "1")
if q:
keyword = q.strip().lower()
query = query.filter(
or_(
func.lower(PlTransportAddress.address_name).contains(keyword),
func.lower(PlTransportAddress.full_address).contains(keyword),
)
)
query = query.order_by(
case((PlTransportAddress.order_num.is_(None), 1), else_=0).asc(),
PlTransportAddress.create_time.desc(),
)
rows = query.limit(200).all()
return TransportAddressListResponse(total=len(rows), list=[_address_to_read(row) for row in rows])
@router.post("/address", response_model=TransportAddressRead, status_code=status.HTTP_201_CREATED)
def create_transport_address(
payload: TransportAddressCreate,
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> TransportAddressRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
row = PlTransportAddress(
id=generate_uuid(),
tenant_no=tenant_no,
address_name=payload.address_name.strip(),
full_address=payload.full_address.strip(),
lng=payload.lng,
lat=payload.lat,
contact_name=payload.contact_name,
contact_phone=payload.contact_phone,
status=payload.status or "1",
remark=payload.remark,
create_by=user.id,
create_dept_id=user.dept_id,
)
db.add(row)
db.commit()
db.refresh(row)
return _address_to_read(row)
@router.get("/address/{id}", response_model=TransportAddressRead)
def get_transport_address(
id: str,
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> TransportAddressRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
row = _get_address(db, tenant_no, id, user)
return _address_to_read(row)
@router.put("/address/{id}", response_model=TransportAddressRead)
def update_transport_address(
id: str,
payload: TransportAddressUpdate,
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> TransportAddressRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
row = _get_address(db, tenant_no, id, user)
for key, value in payload.model_dump(exclude_unset=True).items():
if key in ("address_name", "full_address") and isinstance(value, str):
value = value.strip()
setattr(row, key, value)
row.update_by = user.id
db.commit()
db.refresh(row)
return _address_to_read(row)
@router.delete("/address/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_transport_address(
id: 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())
row = _get_address(db, tenant_no, id, user)
assert_row_deletable_by_creator(row, user_info_from_context(user))
row.del_flag = 1
row.update_by = user.id
db.commit()