Files
zkxw/backend/app/crud/sf_express_settlement.py
T

200 lines
6.6 KiB
Python

"""顺丰月结列表、结算、补填。只处理 transport_mode = sfkd。"""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy.orm import Session
from app.core.data_scope import apply_data_scope
from app.crud.sample_freight import _fmt_dt, _normalize_date_bound, _user_nick_name_map
from app.models.models import BizSampleFreight
from app.services.sf_express_settlement import (
SF_TRANSPORT_MODE,
SETTLE_SETTLED,
assert_settle_request,
freight_amount_label,
plan_fill_amount,
plan_settle,
)
def _base_query(db: Session, *, tenant_no: str, user_info: Optional[dict]):
query = db.query(BizSampleFreight).filter(
BizSampleFreight.tenant_no == tenant_no,
BizSampleFreight.del_flag == 0,
BizSampleFreight.transport_mode == SF_TRANSPORT_MODE,
)
return apply_data_scope(query, BizSampleFreight, user_info, db, tenant_no)
def _apply_filters(
query,
*,
waybill_no: Optional[str],
settle_status: Optional[str],
create_time_from: Optional[str],
create_time_to: Optional[str],
):
waybill = (waybill_no or "").strip()
if waybill:
query = query.filter(BizSampleFreight.waybill_no.like(f"%{waybill}%"))
status = (settle_status or "").strip()
if status:
query = query.filter(BizSampleFreight.settle_status == status)
if create_time_from and str(create_time_from).strip():
query = query.filter(
BizSampleFreight.create_time
>= _normalize_date_bound(str(create_time_from).strip())
)
if create_time_to and str(create_time_to).strip():
query = query.filter(
BizSampleFreight.create_time
<= _normalize_date_bound(str(create_time_to).strip(), end_of_day=True)
)
return query
def _to_row(obj: BizSampleFreight, create_by_name: str) -> Dict[str, Any]:
amount = obj.freight_amount
status = obj.settle_status or ""
return {
"id": obj.id,
"waybillNo": obj.waybill_no or "",
"createBy": obj.create_by or "",
"createByName": create_by_name or obj.create_by or "",
"createTime": _fmt_dt(obj.create_time),
"settleStatus": status,
"settleStatusLabel": "已结算" if status == SETTLE_SETTLED else "未结算",
"freightAmount": float(amount) if amount is not None else None,
"freightAmountLabel": freight_amount_label(obj.transport_mode, amount),
"settleTime": _fmt_dt(obj.settle_time) if obj.settle_time else "",
"transportMode": obj.transport_mode or "",
}
def list_sf_settlements(
db,
*,
tenant_no: str,
user_info: Optional[dict],
page: int = 1,
page_size: int = 10,
waybill_no: Optional[str] = None,
settle_status: Optional[str] = None,
create_time_from: Optional[str] = None,
create_time_to: Optional[str] = None,
) -> Tuple[List[Dict[str, Any]], int]:
page = max(1, int(page or 1))
page_size = max(1, min(100, int(page_size or 10)))
query = _apply_filters(
_base_query(db, tenant_no=tenant_no, user_info=user_info),
waybill_no=waybill_no,
settle_status=settle_status,
create_time_from=create_time_from,
create_time_to=create_time_to,
)
total = query.count()
rows = (
query.order_by(BizSampleFreight.create_time.desc(), BizSampleFreight.id.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
names = _user_nick_name_map(db, [row.create_by for row in rows if row.create_by])
return [_to_row(row, names.get(row.create_by or "", "")) for row in rows], total
def list_sf_settlements_for_export(
db,
*,
tenant_no: str,
user_info: Optional[dict],
waybill_no: Optional[str] = None,
settle_status: Optional[str] = None,
create_time_from: Optional[str] = None,
create_time_to: Optional[str] = None,
) -> List[Dict[str, Any]]:
query = _apply_filters(
_base_query(db, tenant_no=tenant_no, user_info=user_info),
waybill_no=waybill_no,
settle_status=settle_status,
create_time_from=create_time_from,
create_time_to=create_time_to,
)
rows = query.order_by(
BizSampleFreight.create_time.desc(), BizSampleFreight.id.desc()
).all()
if not rows:
raise ValueError("没有可导出的数据")
names = _user_nick_name_map(db, [row.create_by for row in rows if row.create_by])
return [_to_row(row, names.get(row.create_by or "", "")) for row in rows]
def _lock_rows(
db, *, tenant_no: str, user_info: Optional[dict], ids: List[str]
) -> List[BizSampleFreight]:
wanted = [str(item or "").strip() for item in ids if str(item or "").strip()]
rows = (
_base_query(db, tenant_no=tenant_no, user_info=user_info)
.filter(BizSampleFreight.id.in_(wanted))
.all()
)
if len(rows) != len(set(wanted)):
raise ValueError("运费批次不存在")
return rows
def settle_sf_freights(
db,
*,
tenant_no: str,
user_id: Optional[str],
user_info: Optional[dict],
ids: List[str],
freight_amount: Any = None,
):
assert_settle_request(ids, freight_amount)
rows = _lock_rows(db, tenant_no=tenant_no, user_info=user_info, ids=ids)
now = datetime.now()
submitted = None if len(ids) != 1 else freight_amount
for obj in rows:
if obj.transport_mode != SF_TRANSPORT_MODE:
raise ValueError("只能结算顺丰快递运费")
if (obj.settle_status or "") == SETTLE_SETTLED:
raise ValueError("该运费已结算")
amount, status, settle_time = plan_settle(
current_amount=obj.freight_amount, submitted_amount=submitted, now=now
)
obj.freight_amount = amount
obj.settle_status = status
obj.settle_time = settle_time
obj.update_by = user_id
obj.update_time = now
db.commit()
return {"ids": [row.id for row in rows]}
def fill_sf_freight_amount(
db,
*,
tenant_no: str,
user_id: Optional[str],
user_info: Optional[dict],
freight_id: str,
freight_amount: Any,
):
rows = _lock_rows(db, tenant_no=tenant_no, user_info=user_info, ids=[freight_id])
obj = rows[0]
if obj.transport_mode != SF_TRANSPORT_MODE:
raise ValueError("只能补填顺丰快递运费")
obj.freight_amount = plan_fill_amount(
current_amount=obj.freight_amount,
settle_status=obj.settle_status,
submitted_amount=freight_amount,
)
obj.update_by = user_id
obj.update_time = datetime.now()
db.commit()
return {"id": obj.id}