80 lines
2.1 KiB
Python
80 lines
2.1 KiB
Python
"""按供应商名对上搜到的仓。无 sku_id 时靠条形码结果 + 商家全称。"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
SELF_OPERATED_PREFIX = "[自营]"
|
|
PACK_KEYS = ("middlePackNum", "midPackQty", "packNum", "packQty")
|
|
|
|
|
|
@dataclass
|
|
class CartOffer:
|
|
sku_id: str
|
|
vendor_raw: str
|
|
vendor_norm: str
|
|
stock: float | None
|
|
price: float | None = None
|
|
extra: dict[str, Any] = field(default_factory=dict)
|
|
|
|
|
|
def normalize_supplier(raw: str) -> str:
|
|
text = (raw or "").replace("\u3000", " ").strip()
|
|
if text.startswith(SELF_OPERATED_PREFIX):
|
|
text = text[len(SELF_OPERATED_PREFIX) :]
|
|
return " ".join(text.split())
|
|
|
|
|
|
def match_offer(offers: list[CartOffer], supplier_name: str) -> CartOffer | None:
|
|
target = normalize_supplier(supplier_name)
|
|
if not target:
|
|
return None
|
|
matched = [
|
|
offer
|
|
for offer in offers
|
|
if normalize_supplier(offer.vendor_raw) == target
|
|
or normalize_supplier(offer.vendor_norm) == target
|
|
]
|
|
if not matched:
|
|
return None
|
|
return min(matched, key=_offer_rank)
|
|
|
|
|
|
def middle_pack(offer: CartOffer) -> int | None:
|
|
raw = offer.extra.get("raw") if isinstance(offer.extra.get("raw"), dict) else offer.extra
|
|
if not isinstance(raw, dict):
|
|
return None
|
|
for key in PACK_KEYS:
|
|
value = _positive_int(raw.get(key))
|
|
if value is not None:
|
|
return value
|
|
return None
|
|
|
|
|
|
def qty_fits_pack(qty: int, pack: int | None) -> bool:
|
|
if pack is None or pack <= 1:
|
|
return True
|
|
return qty % pack == 0
|
|
|
|
|
|
def _offer_rank(offer: CartOffer) -> tuple[int, float, float]:
|
|
price = offer.price
|
|
stock = offer.stock
|
|
return (
|
|
0 if price is not None else 1,
|
|
float(price) if price is not None else 0.0,
|
|
-float(stock) if stock is not None else 0.0,
|
|
)
|
|
|
|
|
|
def _positive_int(value: object) -> int | None:
|
|
if value is None or value == "":
|
|
return None
|
|
try:
|
|
number = int(float(str(value)))
|
|
except (TypeError, ValueError):
|
|
return None
|
|
if number <= 0:
|
|
return None
|
|
return number
|