feat(dispatch): 메뉴얼 오더 플랫폼 추가(엑셀 단독)

- 플랫폼 Manual 추가. 엑셀 첫 시트(Receiver Name/Phone/Address/Product Name/
  Item Code/Quantity)를 전용 파서로 읽어 1행=1박스 생성
  · 받는 사람 정보가 엑셀에 직접 있어 PDF 불필요(라벨 슬롯 없음)
  · 묶음키 없음 → 행마다 별도 박스
- 업로드 화면: Manual 선택 시 라벨 PDF 칸 숨김 + 안내문
- 출고 카드: Order ID/Tracking 은 값 있을 때만 표시(Manual 은 숨김),
  Manual 은 상품명 표기(받는사람/전화/주소/상품명/수량)
- 재고 차감/사방넷 출고는 기존 집계 로직 그대로(콤보 _N 분해 포함)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 12:16:19 +09:00
parent c4abafd883
commit 8d7fe01c2d
5 changed files with 114 additions and 9 deletions
@@ -89,14 +89,18 @@
<span class="dsp-courier-text erp-badge">{{ p.shipping_provider }}</span>
{% endif %}
<div class="dsp-no">No. {{ p.seq }}</div>
<div class="dsp-order"><b>Order ID</b><span>{{ p.order_id or '—' }}</span></div>
{% if p.order_id %}
<div class="dsp-order"><b>Order ID</b><span>{{ p.order_id }}</span></div>
{% endif %}
{% if p.tracking_id %}
<div class="dsp-meta">
<div><b>Tracking</b>
{% set turl = p.tracking_id | courier_track(p.shipping_provider) %}
{% if turl %}<a class="dsp-track-link" href="{{ turl }}" target="_blank" rel="noopener noreferrer">{{ p.tracking_id }}</a>
{% else %}{{ p.tracking_id or '—' }}{% endif %}
{% else %}{{ p.tracking_id }}{% endif %}
</div>
</div>
{% endif %}
<div class="dsp-recipient">
<span class="dsp-rname">{{ p.recipient_name or '—' }}</span>
{% if p.recipient_address %}<div class="dsp-raddr">{{ p.recipient_address }}</div>{% endif %}
@@ -104,7 +108,7 @@
</div>
<ul class="dsp-items">
{% for it in p['items'] %}
<li>{{ it.seller_sku }} <span class="qty">× {{ it.quantity }}</span></li>
<li>{% if batch.platform == 'Manual' %}{{ it.product_name or it.seller_sku }}{% else %}{{ it.seller_sku }}{% endif %} <span class="qty">× {{ it.quantity }}</span></li>
{% else %}
<li class="erp-muted" style="font-weight:400;" data-ko="상품 없음" data-en="No items">상품 없음</li>
{% endfor %}
@@ -31,7 +31,7 @@
placeholder="오전 출고 / 오후 출고 등으로 구분" />
</label>
<label style="display:flex;flex-direction:column;gap:4px;">
<label id="dsp-label-row" style="display:flex;flex-direction:column;gap:4px;">
<span id="dsp-label-lbl"></span>
<input class="erp-input" type="file" name="label_pdf" id="dsp-label" accept="application/pdf" />
</label>
@@ -66,6 +66,11 @@
intro: "Shopee: Packing List.Doorstep Delivery.xlsx 로 출고 리스트가 생성됩니다. 받는 사람 이름/주소는 Shopee Seller Centre.pdf 에서 가져오니 PDF 도 함께 올리세요.",
label: "📄 Shopee Seller Centre.pdf <span class='erp-muted'>(받는 사람 이름/주소 추출 · 권장)</span>",
data: "📊 Packing List.Doorstep Delivery.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(자동 출고 리스트 기준)</span>"
},
"Manual": {
intro: "메뉴얼 오더: 엑셀 파일만 올리세요. 첫 시트의 받는 사람/전화/주소/상품/수량을 읽어 출고 리스트를 만듭니다(PDF 불필요).",
label: "",
data: "📊 Manual Order.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(받는사람·상품·수량 포함)</span>"
}
},
en: {
@@ -78,6 +83,11 @@
intro: "Shopee: the dispatch list is built from Packing List.Doorstep Delivery.xlsx. Recipient name/address come from Shopee Seller Centre.pdf, so please upload the PDF too.",
label: "📄 Shopee Seller Centre.pdf <span class='erp-muted'>(recipient name/address · recommended)</span>",
data: "📊 Packing List.Doorstep Delivery.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(dispatch list source)</span>"
},
"Manual": {
intro: "Manual order: upload the Excel file only. Recipient/phone/address/product/qty are read from the first sheet (no PDF needed).",
label: "",
data: "📊 Manual Order.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(includes recipient, product, qty)</span>"
}
}
};
@@ -88,6 +98,8 @@
document.getElementById('dsp-intro').textContent = c.intro;
document.getElementById('dsp-label-lbl').innerHTML = c.label;
document.getElementById('dsp-data-lbl').innerHTML = c.data;
// 라벨(PDF) 슬롯이 없는 플랫폼(Manual)은 라벨 업로드 칸을 숨긴다.
document.getElementById('dsp-label-row').style.display = c.label ? '' : 'none';
}
sel.addEventListener('change', apply);
document.addEventListener('dsp:lang', apply); // 언어 토글 시 재적용