121 lines
3.5 KiB
Python
121 lines
3.5 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
|
|
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)
|
|
name = str(product.get("productName") or "")
|
|
if qty != line.qty:
|
|
continue
|
|
if pay_price != money4(line.pay_price):
|
|
continue
|
|
if name and line.goods_name and name != line.goods_name:
|
|
continue
|
|
return idx
|
|
return None
|