200 lines
6.6 KiB
Python
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}
|