"""加购班次:领表 2 选品成功行 → 再搜条形码对仓 → 加车。成功盖章,失败改 status=2。""" from __future__ import annotations from dataclasses import dataclass from typing import Callable from cart_add import ( # noqa: F401 KIND_BUSINESS, KIND_OK, KIND_SYSTEM, AddOutcome, ) from cart_match import CartOffer, match_offer, middle_pack, qty_fits_pack from cart_repo import CartRow, clip_reason Searcher = Callable[[str], list[CartOffer]] Adder = Callable[[CartOffer, int], AddOutcome] @dataclass class CartSummary: claimed: int = 0 added: int = 0 failed: int = 0 def run_cart_batch( repo, *, searchers: dict[int, Searcher], adders: dict[int, Adder], login_errors: dict[int, str], limit: int | None = None, platform_types: list[int] | None = None, ) -> CartSummary: rows = repo.claim_pending(limit=limit, platform_types=platform_types) summary = CartSummary(claimed=len(rows)) for row in rows: reason = _add_one(row, searchers, adders, login_errors) if reason is None: repo.mark_success(row.fid) summary.added += 1 else: repo.mark_fail(row.fid, reason) summary.failed += 1 return summary def _add_one( row: CartRow, searchers: dict[int, Searcher], adders: dict[int, Adder], login_errors: dict[int, str], ) -> str | None: platform = row.platform_type if platform in login_errors: return _system(login_errors[platform]) searcher = searchers.get(platform) if searcher is None: return _system("未登录该平台") adder = adders.get(platform) if adder is None: return _system("未配置加车") bar_code = (row.bar_code or "").strip() if not bar_code: return "缺少条形码" qty = int(row.purchase_qty) if qty <= 0: return "采购数量无效" try: offers = searcher(bar_code) except Exception as exc: return _system(f"查询失败:{exc}") offer = match_offer(offers, row.supplier_name) if offer is None: return "未找到对应仓" if offer.stock is not None and offer.stock < qty: return "库存不足" pack = middle_pack(offer) if not qty_fits_pack(qty, pack): return f"中包装加不进:数量须为{pack}的倍数" try: outcome = adder(offer, qty) except Exception as exc: return _system(f"加购失败:{exc}") if outcome.kind == KIND_OK: return None reason = (outcome.reason or "加购失败").strip() if outcome.kind == KIND_SYSTEM: return _system(reason) return clip_reason(reason) def _system(detail: str) -> str: text = (detail or "未知错误").strip() if text.startswith("系统故障"): return clip_reason(text) return clip_reason(f"系统故障:{text}")