feat(dispatch): 작업 2단계로 축소 + J&T 조회 URL 수정 + Order ID 확대
- 택배스캔 단계 제거 → 라벨부착 / Kagayaku 전달 2단계 - 버튼 라벨에서 '완료' 제거(라벨부착 / Kagayaku 전달) - 완료 판정/배치목록 완료수를 label_attached AND handed_to_kagayaku 기준으로 - J&T 송장 조회 딥링크를 gzquery.html?bills= 로 교체(billcode 미입력 문제) - Order ID 27px 로 확대, 송장 링크 밑줄 강조(굵기/오프셋) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -127,7 +127,9 @@ class DispatchStore:
|
||||
"""
|
||||
SELECT b.*,
|
||||
COUNT(p.id) AS parcel_count,
|
||||
COUNT(p.id) FILTER (WHERE p.courier_scanned) AS scanned_count
|
||||
COUNT(p.id) FILTER (
|
||||
WHERE p.label_attached AND p.handed_to_kagayaku
|
||||
) AS done_count
|
||||
FROM dispatch_batches b
|
||||
LEFT JOIN dispatch_parcels p ON p.batch_id = b.id
|
||||
GROUP BY b.id
|
||||
@@ -301,7 +303,7 @@ class DispatchStore:
|
||||
elif isinstance(v, date):
|
||||
out[k] = v.isoformat()
|
||||
for k in ("id", "batch_id", "parcel_id", "seq", "quantity",
|
||||
"parcel_count", "scanned_count", "total_qty", "box_count"):
|
||||
"parcel_count", "done_count", "total_qty", "box_count"):
|
||||
if k in out and out[k] is not None:
|
||||
try:
|
||||
out[k] = int(out[k])
|
||||
|
||||
@@ -27,14 +27,13 @@ from urllib.parse import quote
|
||||
STATUS_FIELDS: tuple[str, ...] = (
|
||||
"label_attached",
|
||||
"handed_to_kagayaku",
|
||||
"courier_scanned",
|
||||
)
|
||||
|
||||
STATUS_LABELS: dict[str, dict[str, str]] = {
|
||||
"product_ready": {"ko": "상품준비 완료", "en": "Product Ready"},
|
||||
"product_ready": {"ko": "상품준비", "en": "Product Ready"},
|
||||
"packed": {"ko": "포장완료", "en": "Packed"},
|
||||
"label_attached": {"ko": "라벨부착 완료", "en": "Label Attached"},
|
||||
"handed_to_kagayaku": {"ko": "Kagayaku 전달 완료", "en": "Handed to Kagayaku"},
|
||||
"label_attached": {"ko": "라벨부착", "en": "Label Attached"},
|
||||
"handed_to_kagayaku": {"ko": "Kagayaku 전달", "en": "Handed to Kagayaku"},
|
||||
"courier_scanned": {"ko": "택배스캔 확인", "en": "Courier Scanned"},
|
||||
}
|
||||
|
||||
@@ -265,7 +264,7 @@ def courier_logo_url(provider: Any) -> str:
|
||||
# 매핑 없는 배송사는 링크 없이 텍스트로 표시(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}",
|
||||
"jnt": "https://www.jtexpress.my/index/query/gzquery.html?bills={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}",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<tr>
|
||||
<th>날짜</th><th>플랫폼</th><th>배치명</th>
|
||||
<th style="text-align:right">박스</th>
|
||||
<th style="text-align:right">스캔완료</th>
|
||||
<th style="text-align:right">완료</th>
|
||||
<th>생성시각</th><th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -27,9 +27,9 @@
|
||||
<td><a href="/dispatch/batches/{{ b.id }}" style="font-weight:600;">{{ b.batch_name or '(이름없음)' }}</a></td>
|
||||
<td style="text-align:right;">{{ b.parcel_count }}</td>
|
||||
<td style="text-align:right;">
|
||||
{% if b.parcel_count and b.scanned_count == b.parcel_count %}
|
||||
<span style="color:#16a34a;font-weight:600;">{{ b.scanned_count }} ✓</span>
|
||||
{% else %}{{ b.scanned_count }}{% endif %}
|
||||
{% if b.parcel_count and b.done_count == b.parcel_count %}
|
||||
<span style="color:#16a34a;font-weight:600;">{{ b.done_count }} ✓</span>
|
||||
{% else %}{{ b.done_count }}{% endif %}
|
||||
</td>
|
||||
<td class="erp-muted" style="white-space:nowrap;">{{ b.created_at }}</td>
|
||||
<td style="text-align:right;white-space:nowrap;">
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
.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-order span { font-size:27px;font-weight:800;color:#0f172a;letter-spacing:.2px; }
|
||||
.dsp-track-link { color:#2563eb;font-weight:700;text-decoration:underline;
|
||||
text-decoration-thickness:2px;text-underline-offset:3px;cursor:pointer; }
|
||||
.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; }
|
||||
@@ -49,7 +50,6 @@
|
||||
<button class="erp-btn erp-btn-outline" data-filter="incomplete" type="button">미완료</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="label_attached" type="button">라벨부착</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="handed_to_kagayaku" type="button">Kagayaku 전달</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="courier_scanned" type="button">택배스캔 확인</button>
|
||||
</div>
|
||||
<input class="erp-input dsp-search" id="dsp-search" type="search"
|
||||
placeholder="검색: Order ID / Tracking ID / Package ID / SKU" />
|
||||
@@ -57,12 +57,11 @@
|
||||
|
||||
<div class="dsp-cards" id="dsp-cards">
|
||||
{% for p in parcels %}
|
||||
{% set is_done = p.label_attached and p.handed_to_kagayaku and p.courier_scanned %}
|
||||
{% set is_done = p.label_attached and p.handed_to_kagayaku %}
|
||||
<article class="dsp-card {% if is_done %}is-done{% endif %}"
|
||||
data-parcel="{{ p.id }}"
|
||||
data-label_attached="{{ p.label_attached|lower }}"
|
||||
data-handed_to_kagayaku="{{ p.handed_to_kagayaku|lower }}"
|
||||
data-courier_scanned="{{ p.courier_scanned|lower }}"
|
||||
data-search="{{ (p.order_id ~ ' ' ~ p.package_id ~ ' ' ~ p.tracking_id ~ ' ' ~ (p['items']|map(attribute='seller_sku')|join(' ')))|lower }}">
|
||||
{% set logo = p.shipping_provider | courier_logo %}
|
||||
{% if logo %}
|
||||
|
||||
Reference in New Issue
Block a user