AutoRpa/rpa_selection/tests/test_persist.py

202 lines
6.5 KiB
Python

from __future__ import annotations
from decimal import Decimal
from persist import build_result
from pipeline import KIND_BUSINESS_FAIL, KIND_SUCCESS, KIND_SYSTEM_FAIL, SelectionResult
from rules import Allocation, Candidate, PurchaseLine, Rejected, normalize_vendor
HANGZHOU = "杭州小药药自营旗舰店"
CHANGSHA = "长沙小药药自营旗舰店"
GUANGDONG = "广东壹号药业有限公司"
def _purchase(**overrides) -> PurchaseLine:
data = {
"approval_no": "国药准字H20100001",
"sale_price": 100.0,
"qty": 10.0,
"oms_id": 2026100201,
"bar_code": "06901234567890",
"generic_name": "阿莫西林",
"org_code": "901",
}
data.update(overrides)
return PurchaseLine(**data)
def _candidate(**overrides) -> Candidate:
vendor_raw = overrides.get("vendor_raw", GUANGDONG)
data = {
"mall_name": "1号药城",
"sku_id": "S1",
"name": "阿莫西林",
"spec": "0.25g",
"price": 90.0,
"expiry_raw": "13",
"expiry_months": 13.0,
"vendor_raw": vendor_raw,
"vendor_norm": normalize_vendor(vendor_raw),
"stock": 20.0,
"extra": {},
}
data.update(overrides)
if "vendor_raw" in overrides and "vendor_norm" not in overrides:
data["vendor_norm"] = normalize_vendor(overrides["vendor_raw"])
return Candidate(**data)
def _success(allocations: list[Allocation], malls: list[str] | None = None) -> SelectionResult:
return SelectionResult(
kind=KIND_SUCCESS,
reason="SELECTED",
allocations=allocations,
rejected=[],
malls=malls or ["1号药城", "药帮忙"],
)
def test_split_two_platforms_writes_allocated_qty():
result = build_result(
_purchase(),
_success(
[
Allocation(
_candidate(
mall_name="药帮忙",
vendor_raw=HANGZHOU,
price=88.0,
),
3,
),
Allocation(_candidate(price=90.0), 7),
]
),
)
assert result.deal_flag == 1
assert "选品成功" in result.deal_result
assert len(result.cart_rows) == 2
ybm = next(row for row in result.cart_rows if row["platform_type"] == 48)
yaoex = next(row for row in result.cart_rows if row["platform_type"] == 66)
assert ybm["purchase_qty"] == 3
assert ybm["purchase_price"] == Decimal("88.0000")
assert ybm["purchase_total"] == Decimal("264.0000")
assert ybm["supplier_name"] == HANGZHOU
assert yaoex["purchase_qty"] == 7
assert yaoex["purchase_total"] == Decimal("630.0000")
assert all(row["status"] == 1 for row in result.cart_rows)
assert all(row["multi_platform_flag"] == 1 for row in result.cart_rows)
assert all(row["push_status"] == 0 for row in result.cart_rows)
def test_two_warehouses_same_platform_flag_off():
result = build_result(
_purchase(),
_success(
[
Allocation(
_candidate(mall_name="药帮忙", vendor_raw=HANGZHOU, price=88.0),
3,
),
Allocation(
_candidate(mall_name="药帮忙", vendor_raw=CHANGSHA, price=90.0),
7,
),
],
malls=["药帮忙"],
),
)
assert len(result.cart_rows) == 2
assert all(row["platform_type"] == 48 for row in result.cart_rows)
assert all(row["multi_platform_flag"] == 0 for row in result.cart_rows)
def test_stock_insufficient_business_fail():
result = build_result(
_purchase(),
SelectionResult(
kind=KIND_BUSINESS_FAIL,
reason="STOCK_INSUFFICIENT",
allocations=[],
rejected=[Rejected(_candidate(vendor_raw="路边店"), "NOT_SELF_WAREHOUSE")],
malls=["1号药城", "药帮忙"],
),
)
assert result.deal_flag == 2
assert "库存不足" in result.deal_result
assert len(result.cart_rows) == 2
assert all(row["status"] == 2 for row in result.cart_rows)
assert all("库存不足" in (row["fail_reason"] or "") for row in result.cart_rows)
assert all(row["multi_platform_flag"] == 1 for row in result.cart_rows)
def test_margin_fail_text_is_4_percent():
result = build_result(
_purchase(),
SelectionResult(
kind=KIND_BUSINESS_FAIL,
reason="MARGIN_LT_4PCT",
allocations=[],
rejected=[],
malls=["1号药城"],
),
)
assert "毛利不足 4%" in result.deal_result
assert "毛利不足 4%" in (result.cart_rows[0]["fail_reason"] or "")
def test_system_fault_deal_result_fits_varchar2_200_byte():
blob = "药帮忙接口返回非JSON" + ("<html>probe" * 40)
result = build_result(
_purchase(),
SelectionResult(
kind=KIND_SYSTEM_FAIL,
reason="SYSTEM_FAULT",
allocations=[],
rejected=[],
malls=["1号药城", "药帮忙"],
mall_errors={"药帮忙": f"QUERY_FAILED:{blob}"},
),
)
assert len(result.deal_result.encode("gbk")) <= 200
assert result.deal_result.startswith("系统故障")
def test_system_fault_uses_per_mall_errors():
result = build_result(
_purchase(),
SelectionResult(
kind=KIND_SYSTEM_FAIL,
reason="SYSTEM_FAULT",
allocations=[],
rejected=[],
malls=["1号药城", "药帮忙"],
mall_errors={"药帮忙": "LOGIN_FAILED:timeout"},
),
)
assert result.deal_flag == 2
assert result.deal_result.startswith("系统故障")
ybm = next(row for row in result.cart_rows if row["platform_type"] == 48)
yaoex = next(row for row in result.cart_rows if row["platform_type"] == 66)
assert "LOGIN_FAILED:timeout" in (ybm["fail_reason"] or "")
assert "对端平台不可用" in (yaoex["fail_reason"] or "")
def test_failure_total_is_price_times_plan_qty():
result = build_result(
_purchase(sale_price=12.5, qty=3),
SelectionResult(
kind=KIND_BUSINESS_FAIL,
reason="NO_HIT",
allocations=[],
rejected=[],
malls=["1号药城"],
),
)
row = result.cart_rows[0]
assert row["purchase_price"] == Decimal("12.5000")
assert row["purchase_qty"] == 3
assert row["purchase_total"] == Decimal("37.5000")
assert row["supplier_name"] == "无符合规则供应商"