diff --git a/app/main.py b/app/main.py index 28c0aa4..39ac739 100644 --- a/app/main.py +++ b/app/main.py @@ -125,10 +125,17 @@ templates.env.loader = ChoiceLoader( ] ) -# 배송사명 → 로고 URL(dispatch 모듈). 템플릿에서 {{ provider | courier_logo }}. -from .modules.dispatch.store import courier_logo_url as _courier_logo_url # noqa: E402 +# dispatch 모듈 템플릿 필터: 배송사 로고 URL / 송장 조회 딥링크. +# {{ provider | courier_logo }} · {{ tracking | courier_track(provider) }} +from .modules.dispatch.store import ( # noqa: E402 + courier_logo_url as _courier_logo_url, +) +from .modules.dispatch.store import ( # noqa: E402 + courier_tracking_url as _courier_tracking_url, +) templates.env.filters["courier_logo"] = _courier_logo_url +templates.env.filters["courier_track"] = _courier_tracking_url oauth = build_google_oauth() user_store = UserStore(DATA_DIR / "users.json") diff --git a/app/modules/dispatch/store.py b/app/modules/dispatch/store.py index 0aa71b8..b4b6aad 100644 --- a/app/modules/dispatch/store.py +++ b/app/modules/dispatch/store.py @@ -17,6 +17,7 @@ from __future__ import annotations from functools import lru_cache from pathlib import Path from typing import Any, Iterable +from urllib.parse import quote # ── 작업 상태 필드(토글 대상) ── # 순서 = 작업 진행 순서. label_ko/label_en 은 버튼 표기. @@ -258,6 +259,30 @@ def courier_logo_url(provider: Any) -> str: return _logo_url_for_slug(courier_slug(provider)) +# ── 배송사별 송장 조회 딥링크 ── +# {t} 자리에 송장번호를 넣어 조회 페이지로 바로 이동(자동 입력 효과). +# ⚠️ 배송사 사이트 URL 은 바뀔 수 있다. 안 열리면 여기만 고치면 된다. +# 매핑 없는 배송사는 링크 없이 텍스트로 표시(courier_tracking_url 가 "" 반환). +_COURIER_TRACK_URL: dict[str, str] = { + "ninjavan": "https://www.ninjavan.co/en-my/tracking?id={t}", + "jnt": "https://www.jtexpress.my/tracking?billcode={t}", + "flash": "https://www.flashexpress.my/fle/tracking?se={t}", + "citylink": "https://www.citylinkexpress.com/tracking-result/?track_no={t}", + "poslaju": "https://track.pos.com.my/postal-services/quick-access/?track-trace&trackingNo03={t}", + "gdex": "https://web.gdexpress.com/official/web/ConsignmentSearch.html?capcheck=true&input_search={t}", + "spx": "https://spx.com.my/track?tracking_number={t}", +} + + +def courier_tracking_url(tracking: Any, provider: Any = "") -> str: + """송장번호 + 배송사명 → 조회 딥링크. 매핑/번호 없으면 "".""" + t = clean_text(tracking) + tmpl = _COURIER_TRACK_URL.get(courier_slug(provider)) + if not t or not tmpl: + return "" + return tmpl.replace("{t}", quote(t, safe="")) + + def sku_summary(parcels: list[dict[str, Any]]) -> list[dict[str, Any]]: """전체 박스 기준 SKU별 총 수량(피킹 요약). seller_sku 오름차순.""" totals: dict[str, dict[str, Any]] = {} diff --git a/app/modules/dispatch/templates/dispatch/detail.html b/app/modules/dispatch/templates/dispatch/detail.html index 1ae9e61..f34ec7c 100644 --- a/app/modules/dispatch/templates/dispatch/detail.html +++ b/app/modules/dispatch/templates/dispatch/detail.html @@ -13,6 +13,10 @@ .dsp-courier-text { position:absolute;top:12px;right:14px; } .dsp-meta { font-size:13px;line-height:1.5;color:#334155;word-break:break-all; } .dsp-meta b { color:#0f172a; } + .dsp-order { font-size:15px;line-height:1.3;color:#334155;word-break:break-all; } + .dsp-order b { display:block;font-size:11px;font-weight:600;color:#64748b;letter-spacing:.5px; } + .dsp-order span { font-size:22px;font-weight:800;color:#0f172a; } + .dsp-track-link { color:#2563eb;font-weight:700;text-decoration:underline;text-underline-offset:2px; } .dsp-items { list-style:none;margin:0;padding:8px 0;border-top:1px dashed #e2e8f0;border-bottom:1px dashed #e2e8f0; } .dsp-items li { font-size:16px;font-weight:600;color:#0f172a;padding:2px 0; } .dsp-items .qty { color:#2563eb; } @@ -67,10 +71,14 @@ {{ p.shipping_provider }} {% endif %}