"""顺丰月结抬头与结算规则。不访问数据库。""" from __future__ import annotations from dataclasses import dataclass from datetime import datetime from decimal import Decimal, InvalidOperation, ROUND_HALF_UP from typing import Any, Optional SF_TRANSPORT_MODE = "sfkd" SETTLE_UNSETTLED = "wjs" SETTLE_SETTLED = "yjs" _AMOUNT_QUANT = Decimal("0.01") _MAX_AMOUNT = Decimal("9999999999999999.99") @dataclass(frozen=True) class FreightHeaderPlan: transport_mode: str waybill_no: Optional[str] freight_amount: Optional[Decimal] settle_status: Optional[str] clear_settle_time: bool def normalize_waybill(value: Any) -> str: return str(value or "").strip() def _is_blank(value: Any) -> bool: return value is None or (isinstance(value, str) and value.strip() == "") def parse_optional_amount(value: Any) -> Optional[Decimal]: if _is_blank(value): return None try: amount = Decimal(str(value)) except (InvalidOperation, ValueError): raise ValueError("运费金额格式不正确") if not amount.is_finite(): raise ValueError("运费金额格式不正确") if amount < 0: raise ValueError("运费不能为负数") if amount > _MAX_AMOUNT: raise ValueError("运费金额超出范围") return amount.quantize(_AMOUNT_QUANT, rounding=ROUND_HALF_UP) def freight_amount_label(transport_mode: Optional[str], amount: Optional[Decimal]) -> str: if (transport_mode or "") == SF_TRANSPORT_MODE and amount is None: return "月结" if amount is None: return "" return format(Decimal(amount).quantize(_AMOUNT_QUANT, rounding=ROUND_HALF_UP), "f") def plan_freight_header( *, transport_mode: str, waybill_no: Any = None, freight_amount: Any = None, ) -> FreightHeaderPlan: mode = str(transport_mode or "").strip() if not mode: raise ValueError("请选择运输方式") if mode == SF_TRANSPORT_MODE: waybill = normalize_waybill(waybill_no) if not waybill: raise ValueError("请填写顺丰单号") if len(waybill) > 64: raise ValueError("顺丰单号不能超过 64 个字符") return FreightHeaderPlan(mode, waybill, None, SETTLE_UNSETTLED, True) if normalize_waybill(waybill_no): raise ValueError("该运输方式不能填写顺丰单号") amount = parse_optional_amount(freight_amount) if amount is None: raise ValueError("请填写运费") return FreightHeaderPlan(mode, None, amount, None, True) def resolve_header_for_save( *, transport_mode: str, existing_waybill: Any, existing_amount: Any, existing_settle_status: Any, waybill_supplied: bool, waybill_no: Any, amount_supplied: bool, freight_amount: Any, ) -> FreightHeaderPlan: if (existing_settle_status or "") == SETTLE_SETTLED: raise ValueError("已结算的运费不能修改运输方式或顺丰单号") if transport_mode == SF_TRANSPORT_MODE: waybill = waybill_no if waybill_supplied else existing_waybill return plan_freight_header( transport_mode=transport_mode, waybill_no=waybill, freight_amount=None ) amount = freight_amount if amount_supplied else existing_amount return plan_freight_header( transport_mode=transport_mode, waybill_no="", freight_amount=amount ) def plan_settle(*, current_amount: Optional[Decimal], submitted_amount: Any, now: datetime): amount = current_amount if _is_blank(submitted_amount) else parse_optional_amount(submitted_amount) return amount, SETTLE_SETTLED, now def assert_settle_request(ids: list, submitted_amount: Any) -> None: if not ids: raise ValueError("请选择运费批次") if len(ids) != 1 and not _is_blank(submitted_amount): raise ValueError("批量结算不能填写运费") def plan_fill_amount(*, current_amount: Optional[Decimal], settle_status: Optional[str], submitted_amount: Any) -> Decimal: if settle_status != SETTLE_SETTLED or current_amount is not None: raise ValueError("只有已结算且仍为月结的运费可以补填金额") amount = parse_optional_amount(submitted_amount) if amount is None: raise ValueError("请填写运费") return amount def assert_freight_mutable(settle_status: Optional[str], action: str) -> None: if (settle_status or "") != SETTLE_SETTLED: return if action == "delete": raise ValueError("已结算的运费不能删除") if action == "items": raise ValueError("已结算的运费不能调整样本") raise ValueError("已结算的运费不能修改运输方式或顺丰单号")