feat(dispatch): SPX 로고(svg) + 출고 엑셀 상품명 DB 조회
- spx.svg 추가(택배사 SPX 로고). courier 로고 슬러그 spx 가 사용 - 출고 엑셀 상품이름을 아이템코드(seller_sku)로 itemcode_db 에서 조회해 채움 · malaysia_itemcode.name_map(single_items+set_items) 재사용 · 변형 접미사(_숫자) 제거 후 재조회, 못 찾으면 파싱된 상품명 폴백 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ SKU 가 있으면 SKU 당 1행으로 펼치고 박스 단위(받는 사람/주
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import re
|
||||
from datetime import date, datetime
|
||||
from typing import Any
|
||||
|
||||
@@ -46,11 +47,36 @@ def export_filename(*, dispatch_date: str, platform: str) -> str:
|
||||
return f"{stamp}_{suffix}.xlsx"
|
||||
|
||||
|
||||
def build_workbook_bytes(*, batch: dict[str, Any], parcels: list[dict[str, Any]]) -> bytes:
|
||||
def resolve_product_name(seller_sku: str, name_map: dict[str, str], fallback: str = "") -> str:
|
||||
"""아이템코드(seller_sku)로 itemcode_db 상품명을 찾는다.
|
||||
|
||||
seller_sku 는 변형 접미사가 붙을 수 있다(예: MT-0320_3 → DB 는 MT-0320).
|
||||
정확 일치 → 접미사(_숫자) 제거 후 일치 → 없으면 fallback(파싱된 상품명).
|
||||
"""
|
||||
key = (seller_sku or "").strip().upper()
|
||||
if not key:
|
||||
return fallback
|
||||
if key in name_map:
|
||||
return name_map[key]
|
||||
base = re.sub(r"_\d+$", "", key)
|
||||
if base != key and base in name_map:
|
||||
return name_map[base]
|
||||
return fallback
|
||||
|
||||
|
||||
def build_workbook_bytes(
|
||||
*,
|
||||
batch: dict[str, Any],
|
||||
parcels: list[dict[str, Any]],
|
||||
name_map: dict[str, str] | None = None,
|
||||
) -> bytes:
|
||||
"""배치 + 박스(상품 포함) → xlsx 바이트.
|
||||
|
||||
parcels 각 항목은 db.list_parcels 결과(items 포함, recipient_* 포함).
|
||||
name_map: itemcode_db 의 {코드(대문자): 상품명}. 있으면 아이템코드로 상품명을
|
||||
조회해 채운다(없으면 파싱된 상품명 사용).
|
||||
"""
|
||||
name_map = name_map or {}
|
||||
from openpyxl import Workbook # noqa: WPS433 — 지연 import
|
||||
from openpyxl.styles import Font
|
||||
|
||||
@@ -80,8 +106,11 @@ def build_workbook_bytes(*, batch: dict[str, Any], parcels: list[dict[str, Any]]
|
||||
items = p.get("items") or [{}]
|
||||
for it in items:
|
||||
row_data = dict(box)
|
||||
row_data["product_name"] = it.get("product_name", "") or ""
|
||||
row_data["seller_sku"] = it.get("seller_sku", "") or ""
|
||||
sku = it.get("seller_sku", "") or ""
|
||||
row_data["product_name"] = resolve_product_name(
|
||||
sku, name_map, fallback=it.get("product_name", "") or ""
|
||||
)
|
||||
row_data["seller_sku"] = sku
|
||||
row_data["quantity"] = it.get("quantity", "") if it.get("quantity") is not None else ""
|
||||
ws.append([_cell(row_data.get(key, "")) for _, key in _COLUMNS])
|
||||
|
||||
|
||||
@@ -61,6 +61,21 @@ def _data_dir(request: Request) -> Path:
|
||||
return Path(getattr(request.app.state, "data_dir", Path("app/data")))
|
||||
|
||||
|
||||
def _itemcode_name_map(request: Request) -> dict[str, str]:
|
||||
"""itemcode_db {코드(대문자): 상품명}. 미설정/실패 시 빈 dict.
|
||||
|
||||
malaysia 모듈의 읽기 전용 리더(single_items + set_items)를 재사용한다.
|
||||
"""
|
||||
reader = getattr(request.app.state, "malaysia_itemcode", None)
|
||||
if reader is None or not getattr(reader, "enabled", False):
|
||||
return {}
|
||||
try:
|
||||
return reader.name_map()
|
||||
except Exception: # noqa: BLE001 — 다운로드를 막지 않는다.
|
||||
logger.exception("itemcode name_map 조회 실패")
|
||||
return {}
|
||||
|
||||
|
||||
def _require_user(request: Request) -> dict[str, Any]:
|
||||
from app.main import get_current_user_record # noqa: WPS433
|
||||
from app.store import has_module # noqa: WPS433
|
||||
@@ -335,13 +350,17 @@ async def batch_download(
|
||||
count += 1
|
||||
|
||||
# 취합 출고 엑셀(발송날짜/고객/주소/상품/택배 등)을 생성해 함께 넣는다.
|
||||
# 상품이름은 아이템코드로 itemcode_db 에서 조회해 채운다.
|
||||
parcels = st.list_parcels(batch_id=batch_id)
|
||||
name_map = _itemcode_name_map(request)
|
||||
xlsx_name = export.export_filename(
|
||||
dispatch_date=batch.get("dispatch_date", ""),
|
||||
platform=batch.get("platform", ""),
|
||||
)
|
||||
try:
|
||||
xlsx_bytes = export.build_workbook_bytes(batch=batch, parcels=parcels)
|
||||
xlsx_bytes = export.build_workbook_bytes(
|
||||
batch=batch, parcels=parcels, name_map=name_map
|
||||
)
|
||||
zf.writestr(xlsx_name, xlsx_bytes)
|
||||
count += 1
|
||||
except Exception: # noqa: BLE001 — 엑셀 생성 실패해도 원본 다운로드는 살린다.
|
||||
|
||||
Reference in New Issue
Block a user