AutoRpa/rpa_cart/cart_job.py

99 lines
2.8 KiB
Python

"""加购班次:领表 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}")