feat: 展示PDD商品和采购订单信息 (#164)
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, Iterable, List, Mapping
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Optional
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
|
||||
from PyQt5.QtGui import QCloseEvent, QKeySequence
|
||||
@@ -148,6 +148,21 @@ class ColorPriceView:
|
||||
price_inconsistent: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PurchaseResultView:
|
||||
"""采购任务的下单与核单结果。"""
|
||||
|
||||
mode: str
|
||||
confirmed_spec: str
|
||||
quantity: str
|
||||
unit_price: str
|
||||
total_price: str
|
||||
order_no: str
|
||||
ordered_at: str
|
||||
match_status: str
|
||||
payment_status: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TaskDetailViewData:
|
||||
"""详情窗口直接使用的展示数据。"""
|
||||
@@ -169,6 +184,7 @@ class TaskDetailViewData:
|
||||
last_error: str
|
||||
colors: List[ColorPriceView]
|
||||
sizes: List[str]
|
||||
purchase: Optional[PurchaseResultView] = None
|
||||
|
||||
|
||||
def _non_empty_text(value: Any) -> str:
|
||||
@@ -228,6 +244,72 @@ def _format_metric(value: Any) -> str:
|
||||
return "未采集"
|
||||
|
||||
|
||||
def _format_cent(value: Any) -> str:
|
||||
if isinstance(value, int) and not isinstance(value, bool) and value >= 0:
|
||||
return f"¥{value / 100:.2f}"
|
||||
return "—"
|
||||
|
||||
|
||||
def _purchase_result_view(
|
||||
task_type: TaskType, pdd_data: Mapping[str, Any]
|
||||
) -> Optional[PurchaseResultView]:
|
||||
"""整理采购结果;非采购任务不显示采购信息卡片。"""
|
||||
|
||||
if task_type is not TaskType.PURCHASE:
|
||||
return None
|
||||
purchase = pdd_data.get("purchase")
|
||||
if not isinstance(purchase, Mapping):
|
||||
purchase = {}
|
||||
confirmed = purchase.get("confirmed")
|
||||
if not isinstance(confirmed, Mapping):
|
||||
confirmed = {}
|
||||
options = confirmed.get("options")
|
||||
if not isinstance(options, Mapping):
|
||||
options = {}
|
||||
confirmed_spec = " / ".join(
|
||||
_non_empty_text(value) for value in options.values()
|
||||
if _non_empty_text(value)
|
||||
) or "—"
|
||||
mode_text = {"live": "真实采购", "dry_run": "采购演练"}
|
||||
match_text = {
|
||||
"matched": "已匹配",
|
||||
"not_submitted": "未提交",
|
||||
"not_found": "未找到",
|
||||
"ambiguous": "多个候选",
|
||||
"unknown": "结果不确定",
|
||||
}
|
||||
payment_text = {
|
||||
"unpaid": "待付款",
|
||||
"paid": "已支付",
|
||||
"other": "其他状态",
|
||||
"unknown": "未知",
|
||||
}
|
||||
quantity = confirmed.get("quantity")
|
||||
return PurchaseResultView(
|
||||
mode=mode_text.get(_non_empty_text(purchase.get("mode")), "—"),
|
||||
confirmed_spec=confirmed_spec,
|
||||
quantity=(
|
||||
str(quantity)
|
||||
if isinstance(quantity, int) and not isinstance(quantity, bool)
|
||||
else "—"
|
||||
),
|
||||
unit_price=_format_cent(confirmed.get("unit_price_cent")),
|
||||
total_price=_format_cent(confirmed.get("total_price_cent")),
|
||||
order_no=_non_empty_text(purchase.get("order_no")) or "—",
|
||||
ordered_at=(
|
||||
_non_empty_text(purchase.get("ordered_at_raw"))
|
||||
or _non_empty_text(purchase.get("ordered_at"))
|
||||
or "—"
|
||||
),
|
||||
match_status=match_text.get(
|
||||
_non_empty_text(purchase.get("match_status")), "—"
|
||||
),
|
||||
payment_status=payment_text.get(
|
||||
_non_empty_text(purchase.get("payment_status")), "—"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def build_task_detail_view_data(detail: TaskDetail) -> TaskDetailViewData:
|
||||
"""从完整任务记录生成详情展示数据。
|
||||
|
||||
@@ -310,6 +392,7 @@ def build_task_detail_view_data(detail: TaskDetail) -> TaskDetailViewData:
|
||||
last_error=last_error,
|
||||
colors=colors,
|
||||
sizes=sizes,
|
||||
purchase=_purchase_result_view(detail.task_type, pdd_data),
|
||||
)
|
||||
|
||||
|
||||
@@ -401,6 +484,8 @@ class TaskDetailWindow(QWidget):
|
||||
)
|
||||
)
|
||||
content_layout.addWidget(self._build_basic_card(content))
|
||||
if self.data.purchase is not None:
|
||||
content_layout.addWidget(self._build_purchase_card(content))
|
||||
content_layout.addWidget(
|
||||
self._build_spec_card("颜色", self.data.colors, content)
|
||||
)
|
||||
@@ -508,6 +593,37 @@ class TaskDetailWindow(QWidget):
|
||||
layout.setColumnStretch(1, 1)
|
||||
return card
|
||||
|
||||
def _build_purchase_card(self, parent: QWidget) -> CardWidget:
|
||||
"""显示采购任务已经保存在本地的下单和核单结果。"""
|
||||
|
||||
purchase = self.data.purchase
|
||||
assert purchase is not None
|
||||
card = CardWidget(parent)
|
||||
card.setObjectName("purchaseResultCard")
|
||||
layout = QGridLayout(card)
|
||||
layout.setContentsMargins(16, 14, 16, 14)
|
||||
layout.setHorizontalSpacing(18)
|
||||
layout.setVerticalSpacing(8)
|
||||
values = (
|
||||
("采购模式", purchase.mode),
|
||||
("确认规格", purchase.confirmed_spec),
|
||||
("确认数量", purchase.quantity),
|
||||
("商品单价", purchase.unit_price),
|
||||
("订单总价", purchase.total_price),
|
||||
("订单号", purchase.order_no),
|
||||
("下单时间", purchase.ordered_at),
|
||||
("订单匹配", purchase.match_status),
|
||||
("付款状态", purchase.payment_status),
|
||||
)
|
||||
layout.addWidget(SubtitleLabel("采购结果", card), 0, 0, 1, 2)
|
||||
for row, (label, value) in enumerate(values, start=1):
|
||||
layout.addWidget(CaptionLabel(label, card), row, 0)
|
||||
text = self._copyable_label(value, label, card)
|
||||
text.setWordWrap(True)
|
||||
layout.addWidget(text, row, 1)
|
||||
layout.setColumnStretch(1, 1)
|
||||
return card
|
||||
|
||||
def _copyable_label(
|
||||
self, value: str, field_name: str, parent: QWidget
|
||||
) -> CopyableBodyLabel:
|
||||
|
||||
Reference in New Issue
Block a user