| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- """顺丰月结列表、结算、补填。只处理 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}
|