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
+4 -2
View File
@@ -43,7 +43,9 @@ router = APIRouter(prefix="/dispatch", tags=["dispatch"])
_DATA_SLOTS: dict[str, tuple[str, tuple[str, ...]]] = {
store.PLATFORM_TIKTOK: ("03_TikTok_Order_Export.xlsx", (".xlsx",)),
store.PLATFORM_SHOPEE: ("Packing_List.Doorstep_Delivery.xlsx", (".xlsx",)),
store.PLATFORM_MANUAL: ("Manual_Order.xlsx", (".xlsx",)),
}
# 라벨(PDF) 슬롯이 없는 플랫폼(Manual)은 받는 사람 정보가 데이터 엑셀에 직접 있다.
_LABEL_SLOTS: dict[str, tuple[str, tuple[str, ...]]] = {
store.PLATFORM_TIKTOK: ("02_Shipping_Label_Packing_Slip.pdf", (".pdf",)),
store.PLATFORM_SHOPEE: ("Shopee_Seller_Centre.pdf", (".pdf",)),
@@ -259,7 +261,7 @@ async def batch_create(
if platform not in _DATA_SLOTS:
raise HTTPException(status_code=400, detail=f"지원하지 않는 플랫폼입니다: {platform}")
data_slot = _DATA_SLOTS[platform]
label_slot = _LABEL_SLOTS[platform]
label_slot = _LABEL_SLOTS.get(platform) # Manual 은 라벨 없음(None)
if data_xlsx is None or not (data_xlsx.filename or "").strip():
raise HTTPException(
@@ -272,7 +274,7 @@ async def batch_create(
stamp = now_kst().strftime("%H%M%S")
dest_dir = _data_dir(request) / "dispatch" / dispatch_date.strip() / f"{_slugify(name)}_{stamp}"
label_path = _save_upload(label_pdf, dest_dir=dest_dir, slot=label_slot)
label_path = _save_upload(label_pdf, dest_dir=dest_dir, slot=label_slot) if label_slot else ""
order_path = _save_upload(data_xlsx, dest_dir=dest_dir, slot=data_slot)
# 엑셀 파싱 — 실패 시 사용자에게 한국어 메시지 그대로 보여준다.