AutoRpa/rpa_cart/settle.py

135 lines
4.0 KiB
Python

"""1 号药城导出行 → 表 2 operate_type=2。不对加购行。"""
from __future__ import annotations
from dataclasses import dataclass, replace
from decimal import Decimal, ROUND_HALF_UP
from typing import Any
YAOEX_PLATFORM_TYPE = 66
OPERATE_TYPE_PAY = 2
PUSH_NOT_SENT = 0
UNASSIGNED_OMS_ID = 0
PAID_STATUSES = {"待发货", "待收货", "已完成"}
def money4(value: object) -> Decimal:
return Decimal(str(value)).quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
@dataclass(frozen=True)
class MallLine:
order_no: str
parent_order_no: str
supplier_name: str
status: str
goods_name: str
product_code: str
bar_code: str
unit_price: Decimal
pay_price: Decimal
qty: int
goods_amount: Decimal
order_paid: Decimal
pay_order_dtl_id: str = ""
@dataclass
class MatchResult:
rows: list[dict[str, Any]]
unmatched: list[tuple[MallLine, str]]
skipped_dup: list[MallLine]
skipped_status: list[MallLine]
def attach_detail_ids(
lines: list[MallLine], details: dict[str, dict[str, Any]]
) -> list[MallLine]:
remaining: dict[str, list[dict[str, Any]]] = {}
for order_no, payload in details.items():
remaining[order_no] = list(payload.get("productList") or [])
attached: list[MallLine] = []
for line in lines:
pool = remaining.get(line.order_no) or []
idx = _find_product(pool, line)
if idx is None:
attached.append(line)
continue
product = pool.pop(idx)
dtl_id = product.get("orderDetailId")
attached.append(replace(line, pay_order_dtl_id="" if dtl_id is None else str(dtl_id)))
return attached
def match_pay_lines(
lines: list[MallLine],
existing_keys: set[tuple[str, str]],
) -> MatchResult:
result = MatchResult(rows=[], unmatched=[], skipped_dup=[], skipped_status=[])
seen = set(existing_keys)
for line in lines:
if line.status not in PAID_STATUSES:
result.skipped_status.append(line)
continue
if not line.pay_order_dtl_id:
result.unmatched.append((line, "NO_DTL_ID"))
continue
if len(line.bar_code) > 40:
result.unmatched.append((line, "BAR_CODE_TOO_LONG"))
continue
key = (line.order_no, line.pay_order_dtl_id)
if key in seen:
result.skipped_dup.append(line)
continue
seen.add(key)
result.rows.append(build_pay_row(line))
return result
def build_pay_row(line: MallLine) -> dict[str, Any]:
price = money4(line.pay_price)
qty = int(line.qty)
return {
"operate_type": OPERATE_TYPE_PAY,
"oms_id": UNASSIGNED_OMS_ID,
"platform_type": YAOEX_PLATFORM_TYPE,
"supplier_name": line.supplier_name,
"bar_code": line.bar_code,
"purchase_price": price,
"purchase_qty": qty,
"purchase_total": money4(price * qty),
"status": None,
"fail_reason": None,
"pay_order_no": line.order_no,
"pay_order_dtl_id": line.pay_order_dtl_id,
"push_status": PUSH_NOT_SENT,
"org_code": None,
"multi_platform_flag": 0,
}
def _find_product(pool: list[dict[str, Any]], line: MallLine) -> int | None:
for idx, product in enumerate(pool):
qty = int(Decimal(str(product.get("quantity") or 0)))
pay_price = money4(product.get("payPrice4") or 0)
if qty != line.qty:
continue
if pay_price != money4(line.pay_price):
continue
if not _goods_name_matches(product, line.goods_name):
continue
return idx
return None
def _goods_name_matches(product: dict[str, Any], goods_name: str) -> bool:
if not goods_name:
return True
product_name = str(product.get("productName") or "")
short_name = str(product.get("shortName") or "")
if not product_name and not short_name:
return True
if goods_name in {product_name, short_name}:
return True
return goods_name in product_name