From f1097b85dcb96c5072ff822122be49140b3d1a2c Mon Sep 17 00:00:00 2001 From: king Date: Mon, 22 Jun 2026 14:16:11 +0900 Subject: [PATCH] =?UTF-8?q?feat(dispatch):=20Shopee=20=ED=94=8C=EB=9E=AB?= =?UTF-8?q?=ED=8F=BC=20=EC=B6=94=EA=B0=80=20+=20=EB=B0=9B=EB=8A=94=20?= =?UTF-8?q?=EC=82=AC=EB=9E=8C=20=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5/?= =?UTF-8?q?=EC=B6=9C=EA=B3=A0=20=EC=97=91=EC=85=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 플랫폼 TikTok/Shopee 분리: 업로드 화면이 선택에 따라 파일 안내 변경 (TikTok=Order Export xlsx, Shopee=Packing List xlsx / 라벨 PDF 선택) - TikTok Picking List.pdf 업로드 제거(피킹 요약은 데이터 엑셀로 생성) - 파서 공용화 + 헤더 행 자동탐지(Shopee 제목행 대응), 두 포맷 헤더 별칭 통합 - 받는 사람 이름/전화/주소를 박스 단위로 dispatch_db 에 저장(개인정보) · init SQL 갱신 + 기존 DB용 멱등 마이그레이션 dispatch_add_recipient_columns.sql - 출고 작업 카드: Package 제거, 받는 사람 이름(크게)+주소(여러 줄)+전화 표시 - 배치 다운로드 zip 에 취합 출고 엑셀 포함 파일명 YYYY.MM.DD(Ddd)_tictoc.xlsx / _shopee.xlsx (export.py) - 문서(CLAUDE.md, DISPATCH_MODULE.md) PII 보관·Shopee 반영 Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 2 +- app/modules/dispatch/db.py | 8 +- app/modules/dispatch/export.py | 125 ++++++++++++++++++ app/modules/dispatch/parser.py | 57 +++++--- app/modules/dispatch/router.py | 77 ++++++++--- app/modules/dispatch/store.py | 48 +++++-- .../dispatch/templates/dispatch/detail.html | 14 +- .../dispatch/templates/dispatch/new.html | 57 +++++--- app/static/dispatch/courier/jnt.png | Bin 0 -> 45483 bytes app/static/dispatch/courier/ninjavan.png | Bin 0 -> 68464 bytes docs/DISPATCH_MODULE.md | 54 ++++++-- .../sql/dispatch_add_recipient_columns.sql | 24 ++++ scripts/sql/dispatch_db_init.sql | 8 +- 13 files changed, 388 insertions(+), 86 deletions(-) create mode 100644 app/modules/dispatch/export.py create mode 100644 app/static/dispatch/courier/jnt.png create mode 100644 app/static/dispatch/courier/ninjavan.png create mode 100644 scripts/sql/dispatch_add_recipient_columns.sql diff --git a/CLAUDE.md b/CLAUDE.md index b9aa151..415e628 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ Claude Code는 이 저장소에서 작업을 시작하기 전에 **반드시 아 - 쿠팡 밀크런 (`app/modules/cupang/`, `cupang_db`) — 출고 달력/박스 입수량 계산/입고센터 관리, 상품은 `itemcode_db` 읽기 전용 - 휴가 관리 (`app/modules/vacation/`, `vacation_db`) — 월간 달력(구글식 bar)/연차·반차 신청/승인 워크플로/공휴일·연차 설정. 권한키 `vacation`·`vacation_approver` - 말레이시아 창고 재고관리 (`app/modules/malaysia/`, `malaysia_stock_db`) — 낱개(MT/MX/MZ) 입출고·조정, 세트(MY) BOM, 일일 재고조사(세트→낱개 자동 분해), 현재고 현황. 뚜껑(MD-)은 재고 집계 제외 — 단, 창고 랙에는 위치 확인용으로 배치 가능(`store.LID_ITEMS`). 상품은 `itemcode_db` 읽기 전용. 권한키 `malaysia` -- 말레이시아 배송 (`app/modules/dispatch/`, `dispatch_db`) — TikTok 출고관리. 03_TikTok_Order_Export.xlsx 업로드 → 1박스=1카드 출고 작업 리스트·SKU 피킹 요약·Kagayaku 전달표 자동 생성. 1박스 묶음 기준 Package ID > Tracking ID > Order ID, 같은 박스 같은 SKU 합산. 작업 상태 5단계 토글(`dispatch_logs` 기록). 고객 이름/주소/전화는 미저장(파싱 시 폐기). 엑셀은 openpyxl 파싱. 권한키 `dispatch`. 상세는 `docs/DISPATCH_MODULE.md` +- 말레이시아 배송 (`app/modules/dispatch/`, `dispatch_db`) — TikTok·Shopee 출고관리. 플랫폼별 데이터 엑셀 업로드(TikTok=03_TikTok_Order_Export.xlsx, Shopee=Packing List.Doorstep Delivery.xlsx) → 1박스=1카드 출고 작업 리스트·SKU 피킹 요약·Kagayaku 전달표 자동 생성. 1박스 묶음 기준 Package ID > Tracking ID > Order ID, 같은 박스 같은 SKU 합산. 작업 상태 토글(`dispatch_logs` 기록). 받는 사람 이름/전화/주소는 박스 단위로 저장(작업 카드 표시 + 출고 엑셀 생성용 — 개인정보). 배치 다운로드 zip 에 업로드 원본 + 취합 출고 엑셀(`YYYY.MM.DD(Ddd)_tictoc|shopee.xlsx`) 포함. 엑셀은 openpyxl 파싱/생성. 권한키 `dispatch`. 상세는 `docs/DISPATCH_MODULE.md` 상세는 `docs/PROJECT_OVERVIEW.md`. diff --git a/app/modules/dispatch/db.py b/app/modules/dispatch/db.py index eba5935..87d2056 100644 --- a/app/modules/dispatch/db.py +++ b/app/modules/dispatch/db.py @@ -89,8 +89,9 @@ class DispatchStore: """ INSERT INTO dispatch_parcels (batch_id, seq, order_id, package_id, - tracking_id, shipping_provider) - VALUES (%s,%s,%s,%s,%s,%s) + tracking_id, shipping_provider, + recipient_name, recipient_phone, recipient_address) + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s) RETURNING id """, ( @@ -100,6 +101,9 @@ class DispatchStore: (p.get("package_id") or "").strip(), (p.get("tracking_id") or "").strip(), (p.get("shipping_provider") or "").strip(), + (p.get("recipient_name") or "").strip(), + (p.get("recipient_phone") or "").strip(), + (p.get("recipient_address") or "").strip(), ), ).fetchone() parcel_id = parcel_row["id"] diff --git a/app/modules/dispatch/export.py b/app/modules/dispatch/export.py new file mode 100644 index 0000000..38edd78 --- /dev/null +++ b/app/modules/dispatch/export.py @@ -0,0 +1,125 @@ +"""출고 배치 → 취합 엑셀(openpyxl) 생성. + +다운로드 zip 에 업로드 원본과 함께 넣는 '출고 엑셀'을 만든다. 1박스 안에 여러 +SKU 가 있으면 SKU 당 1행으로 펼치고 박스 단위(받는 사람/주문/택배) 정보는 반복한다. + +파일명 형식: 2026.06.22(Mon)_tictoc.xlsx (Shopee 는 끝이 _shopee). + - TikTok 은 운영 요청 철자에 맞춰 'tictoc' 을 쓴다. +""" + +from __future__ import annotations + +import io +from datetime import date, datetime +from typing import Any + +# 엑셀 컬럼(요청 순서). (헤더, 박스/배치/상품 키) +_COLUMNS: tuple[tuple[str, str], ...] = ( + ("발송 날짜", "dispatch_date"), + ("고객 이름", "recipient_name"), + ("전화번호", "recipient_phone"), + ("주소", "recipient_address"), + ("상품이름", "product_name"), + ("아이템 코드", "seller_sku"), + ("주문 수량", "quantity"), + ("오더번호", "order_id"), + ("택배사", "shipping_provider"), + ("택배송장번호", "tracking_id"), + ("주문처", "platform"), +) + +# 플랫폼 → 파일명 접미사. +_PLATFORM_SUFFIX: dict[str, str] = { + "TikTok": "tictoc", + "Shopee": "shopee", +} + + +def export_filename(*, dispatch_date: str, platform: str) -> str: + """2026.06.22(Mon)_tictoc.xlsx 형식 파일명. 날짜 파싱 실패 시 원문 사용.""" + d = _parse_date(dispatch_date) + if d is not None: + stamp = d.strftime("%Y.%m.%d(%a)") + else: + stamp = (dispatch_date or "").strip() or "dispatch" + suffix = _PLATFORM_SUFFIX.get((platform or "").strip(), (platform or "dispatch").strip().lower()) + return f"{stamp}_{suffix}.xlsx" + + +def build_workbook_bytes(*, batch: dict[str, Any], parcels: list[dict[str, Any]]) -> bytes: + """배치 + 박스(상품 포함) → xlsx 바이트. + + parcels 각 항목은 db.list_parcels 결과(items 포함, recipient_* 포함). + """ + from openpyxl import Workbook # noqa: WPS433 — 지연 import + from openpyxl.styles import Font + + wb = Workbook() + ws = wb.active + ws.title = "출고" + + headers = [h for h, _ in _COLUMNS] + ws.append(headers) + for cell in ws[1]: + cell.font = Font(bold=True) + + dispatch_date = batch.get("dispatch_date", "") or "" + platform = batch.get("platform", "") or "" + + for p in parcels: + box = { + "dispatch_date": dispatch_date, + "platform": platform, + "recipient_name": p.get("recipient_name", "") or "", + "recipient_phone": p.get("recipient_phone", "") or "", + "recipient_address": p.get("recipient_address", "") or "", + "order_id": p.get("order_id", "") or "", + "shipping_provider": p.get("shipping_provider", "") or "", + "tracking_id": p.get("tracking_id", "") or "", + } + 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 "" + 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]) + + _autosize(ws, headers) + + buf = io.BytesIO() + wb.save(buf) + return buf.getvalue() + + +def _cell(value: Any) -> Any: + """송장/전화번호 등이 숫자로 보이면 엑셀이 과학표기/반올림 하므로 문자열 보존.""" + if isinstance(value, int): + return value + return "" if value is None else str(value) + + +def _autosize(ws: Any, headers: list[str]) -> None: + """대략적인 컬럼 폭. 주소는 길어 상한을 둔다.""" + from openpyxl.utils import get_column_letter + + widths: list[int] = [len(h) for h in headers] + for row in ws.iter_rows(min_row=2, values_only=True): + for i, val in enumerate(row): + ln = len(str(val)) if val is not None else 0 + if ln > widths[i]: + widths[i] = ln + for i, w in enumerate(widths, start=1): + ws.column_dimensions[get_column_letter(i)].width = min(max(w + 2, 8), 50) + + +def _parse_date(value: str) -> date | None: + s = (value or "").strip() + if not s: + return None + for fmt in ("%Y-%m-%d", "%Y.%m.%d", "%Y/%m/%d"): + try: + return datetime.strptime(s, fmt).date() + except ValueError: + continue + return None diff --git a/app/modules/dispatch/parser.py b/app/modules/dispatch/parser.py index ad08a4f..2cc40b7 100644 --- a/app/modules/dispatch/parser.py +++ b/app/modules/dispatch/parser.py @@ -1,11 +1,13 @@ -"""TikTok Order Export XLSX 파서 (openpyxl). +"""출고 XLSX 파서 (openpyxl) — TikTok / Shopee 공용. -요구사항 2/11 구현. pandas 대신 레포에 이미 있는 openpyxl 로 읽어 의존성을 -가볍게 유지한다(동작/결과는 동일: 헤더 정규화 → 표준키 매핑 → 박스 묶기). +pandas 대신 레포에 이미 있는 openpyxl 로 읽어 의존성을 가볍게 유지한다 +(동작: 헤더 정규화 → 표준키 매핑 → 박스 묶기). -- 첫 시트의 첫 행을 헤더로 본다. 헤더 앞뒤 공백 제거 후 표준키로 매핑한다. -- 매핑되지 않는 열(고객 이름/주소/전화 등)은 읽기 단계에서 버린다 → 개인정보 - 미보관(요구사항 11). +- TikTok: 03_TikTok_Order_Export.xlsx, Shopee: Packing List.Doorstep Delivery.xlsx. + 두 포맷 모두 같은 표준키로 매핑된다(store.COLUMN_ALIASES). 헤더가 첫 행이 아닐 + 수 있어(Shopee 는 제목 행이 위에 붙는 경우가 있다) 상단 몇 행을 훑어 필수 컬럼이 + 잡히는 첫 행을 헤더로 본다. +- 받는 사람(이름/전화/주소) 컬럼은 매핑되면 보존한다(작업 카드 표시 + 출고 엑셀). - Tracking ID 는 숫자로 읽혀도 문자열로 보존(store.clean_text). - Quantity 가 비면 1, NaN/None 은 빈 문자열로 처리. - 필수 컬럼이 없으면 어떤 컬럼이 없는지 ParseError 로 알린다. @@ -22,10 +24,15 @@ class ParseError(Exception): """엑셀 파싱 실패. message 는 사용자에게 그대로 보여줄 한국어 메시지.""" -def parse_order_export(path: str) -> dict[str, Any]: - """XLSX 경로 → {"parcels": [...], "row_count": int}. +# 헤더가 첫 행이 아닐 수 있어 상단에서 헤더 행을 찾을 때 훑는 최대 행 수. +_HEADER_SCAN_ROWS = 15 + + +def parse_export(path: str) -> dict[str, Any]: + """출고 XLSX 경로 → {"parcels": [...], "row_count": int}. parcels 는 store.group_parcels 결과(1박스=1항목). 실패 시 ParseError. + TikTok·Shopee 공용(표준키 매핑이 흡수한다). """ try: from openpyxl import load_workbook # noqa: WPS433 — 지연 import @@ -42,22 +49,20 @@ def parse_order_export(path: str) -> dict[str, Any]: try: ws = wb.worksheets[0] - rows_iter = ws.iter_rows(values_only=True) - try: - header = next(rows_iter) - except StopIteration: + all_rows = list(ws.iter_rows(values_only=True)) + if not all_rows: raise ParseError("엑셀에 데이터가 없습니다(빈 파일).") - mapping = store.resolve_columns(header) - missing = store.missing_required(mapping) - if missing: + header_idx, mapping = _find_header(all_rows) + if header_idx is None: + missing = store.missing_required({}) raise ParseError("엑셀에서 필요한 컬럼을 찾지 못했습니다: " + ", ".join(missing)) norm_rows: list[dict[str, str]] = [] - for raw in rows_iter: + for raw in all_rows[header_idx + 1:]: if raw is None: continue - # 표준키만 추출(매핑 안 된 열 = 개인정보 등은 버린다). + # 표준키만 추출(매핑 안 된 열은 버린다). record = { key: store.clean_text(raw[idx]) if idx < len(raw) else "" for key, idx in mapping.items() @@ -78,6 +83,24 @@ def parse_order_export(path: str) -> dict[str, Any]: return {"parcels": parcels, "row_count": len(norm_rows)} +# 하위 호환 별칭(기존 호출부/테스트가 쓰던 이름). +parse_order_export = parse_export + + +def _find_header(rows: list) -> tuple[int | None, dict[str, int]]: + """상단 행들을 훑어 필수 컬럼이 잡히는 첫 행을 헤더로 본다. + + 반환: (헤더 행 인덱스, {표준키: 열 인덱스}). 못 찾으면 (None, {}). + """ + for idx, raw in enumerate(rows[:_HEADER_SCAN_ROWS]): + if raw is None: + continue + mapping = store.resolve_columns(raw) + if not store.missing_required(mapping): + return idx, mapping + return None, {} + + def _is_description_row(record: dict[str, str]) -> bool: """헤더 아래 '컬럼 설명' 행 판별. diff --git a/app/modules/dispatch/router.py b/app/modules/dispatch/router.py index 6aa174a..1e97771 100644 --- a/app/modules/dispatch/router.py +++ b/app/modules/dispatch/router.py @@ -6,13 +6,15 @@ DISPATCH_DB_URL 미설정 시 store 가 None 이며, 각 페이지는 "설정 필요" 안내. - 업로드 원본은 DATA_DIR/dispatch/<날짜>/<배치slug>/ 에 저장, DB 엔 경로만 기록. -초보 물류 직원용 화면이라 고객정보는 일절 다루지 않는다. 작업 기준은 -Order ID / Package ID / Tracking ID / Seller SKU / Quantity 뿐이다. +초보 물류 직원용 화면. 작업 기준 키는 Order ID / Package ID / Tracking ID / +Seller SKU / Quantity 이고, 받는 사람(이름/전화/주소)은 작업 카드 표시 + 취합 +출고 엑셀 생성을 위해 박스 단위로 보관한다(개인정보). """ from __future__ import annotations import io +import logging import re import shutil import unicodedata @@ -26,15 +28,25 @@ from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Resp from app.timezone import now_kst, today_kst -from . import store -from .parser import ParseError, parse_order_export +from . import export, store +from .parser import ParseError, parse_export + +logger = logging.getLogger("dispatch.router") router = APIRouter(prefix="/dispatch", tags=["dispatch"]) -# 업로드 허용 파일 슬롯 (필드명 → (저장 파일명, 허용 확장자)) -_PICKING = ("01_Picking_List.pdf", (".pdf",)) -_LABEL = ("02_Shipping_Label_Packing_Slip.pdf", (".pdf",)) -_ORDER = ("03_TikTok_Order_Export.xlsx", (".xlsx",)) +# 업로드 슬롯 (저장 파일명, 허용 확장자). 플랫폼마다 올리는 파일이 다르다. +# - 데이터 슬롯(필수): 자동 출고 리스트의 기준 엑셀. +# - 라벨 슬롯(선택): 실제 포장/라벨 원본 PDF(보관용). +# TikTok 의 01_Picking_List.pdf 는 쓰지 않으므로 업로드 받지 않는다. +_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",)), +} +_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",)), +} # ──────────────────────────────────────────────────────────── @@ -176,8 +188,9 @@ async def batch_new(request: Request) -> HTMLResponse: ctx.update( { "page_title": "말레이시아 배송 — 업로드", - "page_subtitle": "TikTok 파일 업로드 → 출고 배치 자동 생성", + "page_subtitle": "플랫폼 파일 업로드 → 출고 배치 자동 생성", "today": today_kst().isoformat(), + "platforms": store.PLATFORMS, "active_tab": "new", } ) @@ -190,23 +203,31 @@ async def batch_create( dispatch_date: str = Form(...), platform: str = Form("TikTok"), batch_name: str = Form(""), - picking_pdf: UploadFile | None = File(None), label_pdf: UploadFile | None = File(None), - order_export: UploadFile | None = File(None), + data_xlsx: UploadFile | None = File(None), user: dict[str, Any] = Depends(_require_user), ) -> RedirectResponse: """파일 업로드 + 배치 생성 + 엑셀 파싱 + 박스 저장. - 03_TikTok_Order_Export.xlsx 는 필수(자동 출고 리스트의 기준). PDF 2개는 선택. - 같은 날짜/플랫폼이라도 새 배치로 생성한다(덮어쓰지 않음, 요구사항 12). + 플랫폼별 슬롯: + - TikTok: 데이터=03_TikTok_Order_Export.xlsx(필수), 라벨=Shipping Label PDF(선택) + - Shopee: 데이터=Packing List.Doorstep Delivery.xlsx(필수), 라벨=Shopee Seller Centre.pdf(선택) + 같은 날짜/플랫폼이라도 새 배치로 생성한다(덮어쓰지 않음). """ st = _store(request) if st is None: raise HTTPException(status_code=503, detail="dispatch_db 미설정") - if order_export is None or not (order_export.filename or "").strip(): + + platform = (platform or store.PLATFORM_TIKTOK).strip() + if platform not in _DATA_SLOTS: + raise HTTPException(status_code=400, detail=f"지원하지 않는 플랫폼입니다: {platform}") + data_slot = _DATA_SLOTS[platform] + label_slot = _LABEL_SLOTS[platform] + + if data_xlsx is None or not (data_xlsx.filename or "").strip(): raise HTTPException( status_code=400, - detail="03_TikTok_Order_Export.xlsx 는 필수입니다(자동 출고 리스트 기준 데이터).", + detail=f"{data_slot[0]} 는 필수입니다(자동 출고 리스트 기준 데이터).", ) # 저장 폴더: DATA_DIR/dispatch/<날짜>/<배치slug>_/ @@ -214,13 +235,12 @@ async def batch_create( stamp = now_kst().strftime("%H%M%S") dest_dir = _data_dir(request) / "dispatch" / dispatch_date.strip() / f"{_slugify(name)}_{stamp}" - picking_path = _save_upload(picking_pdf, dest_dir=dest_dir, slot=_PICKING) - label_path = _save_upload(label_pdf, dest_dir=dest_dir, slot=_LABEL) - order_path = _save_upload(order_export, dest_dir=dest_dir, slot=_ORDER) + label_path = _save_upload(label_pdf, dest_dir=dest_dir, slot=label_slot) + order_path = _save_upload(data_xlsx, dest_dir=dest_dir, slot=data_slot) # 엑셀 파싱 — 실패 시 사용자에게 한국어 메시지 그대로 보여준다. try: - parsed = parse_order_export(order_path) + parsed = parse_export(order_path) except ParseError as exc: raise HTTPException(status_code=400, detail=str(exc)) @@ -235,7 +255,7 @@ async def batch_create( platform=platform, dispatch_date=dispatch_date, batch_name=name, - picking_pdf_path=picking_path, + picking_pdf_path="", label_pdf_path=label_path, order_export_path=order_path, ) @@ -266,7 +286,7 @@ async def batch_delete( async def batch_download( request: Request, batch_id: int, user: dict[str, Any] = Depends(_require_user) ) -> Response: - """배치에 업로드된 원본 파일(피킹/라벨 PDF + 주문 엑셀)을 zip 으로 묶어 다운로드.""" + """배치 다운로드: 업로드 원본(라벨 PDF + 데이터 엑셀) + 취합 출고 엑셀을 zip 으로 묶음.""" st = _store(request) if st is None: raise HTTPException(status_code=503, detail="dispatch_db 미설정") @@ -297,8 +317,21 @@ async def batch_download( zf.write(p, arcname=arcname) count += 1 + # 취합 출고 엑셀(발송날짜/고객/주소/상품/택배 등)을 생성해 함께 넣는다. + parcels = st.list_parcels(batch_id=batch_id) + 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) + zf.writestr(xlsx_name, xlsx_bytes) + count += 1 + except Exception: # noqa: BLE001 — 엑셀 생성 실패해도 원본 다운로드는 살린다. + logger.exception("출고 엑셀 생성 실패 batch_id=%s", batch_id) + if count == 0: - raise HTTPException(status_code=404, detail="다운로드할 업로드 파일이 없습니다.") + raise HTTPException(status_code=404, detail="다운로드할 파일이 없습니다.") slug = _slugify(batch.get("batch_name") or "dispatch") fname = f"{batch.get('dispatch_date', '')}_{slug}.zip".lstrip("_") diff --git a/app/modules/dispatch/store.py b/app/modules/dispatch/store.py index 6168299..f303315 100644 --- a/app/modules/dispatch/store.py +++ b/app/modules/dispatch/store.py @@ -37,16 +37,33 @@ STATUS_LABELS: dict[str, dict[str, str]] = { "courier_scanned": {"ko": "택배스캔 확인", "en": "Courier Scanned"}, } +# ── 플랫폼 ── +# 주문처(엑셀 '주문처' 컬럼)이자 업로드 슬롯/파서 선택의 기준값. +PLATFORM_TIKTOK = "TikTok" +PLATFORM_SHOPEE = "Shopee" +PLATFORMS: tuple[str, ...] = (PLATFORM_TIKTOK, PLATFORM_SHOPEE) + + # ── 엑셀 컬럼명(앞뒤 공백 제거 후, 소문자 비교). 표준키 → 가능한 헤더들 ── -# 개인정보 컬럼은 의도적으로 매핑하지 않는다(이름/주소/전화는 버린다). +# TikTok(03_TikTok_Order_Export.xlsx)·Shopee(Packing List.Doorstep Delivery.xlsx) +# 두 포맷의 헤더를 한 테이블에 모은다(매칭되는 것만 사용). 헤더가 다르면 +# 해당 표준키 줄에 별칭 1개만 추가하면 된다. +# 받는 사람(recipient_*) 정보는 작업 카드 표시 + 출고 엑셀 생성을 위해 보관한다. COLUMN_ALIASES: dict[str, tuple[str, ...]] = { - "order_id": ("order id", "order_id", "주문번호"), - "package_id": ("package id", "package_id", "패키지번호"), - "tracking_id": ("tracking id", "tracking_id", "tracking number", "송장번호"), - "shipping_provider": ("shipping provider name", "shipping provider", "shipping_provider_name", "배송사", "택배사"), - "seller_sku": ("seller sku", "seller_sku", "sku", "판매자 sku"), - "product_name": ("product name", "product_name", "상품명"), - "quantity": ("quantity", "qty", "수량"), + "order_id": ("order id", "order_id", "order sn", "ordersn", "order no.", "order number", "주문번호"), + "package_id": ("package id", "package_id", "package number", "패키지번호"), + "tracking_id": ("tracking id", "tracking_id", "tracking number", "tracking no", "tracking no.", "awb", "awb no.", "송장번호"), + "shipping_provider": ("shipping provider name", "shipping provider", "shipping_provider_name", + "shipping option", "courier", "carrier", "logistics", "배송사", "택배사"), + "seller_sku": ("seller sku", "seller_sku", "sku", "sku reference no.", "sku reference no", + "parent sku reference no.", "판매자 sku", "상품코드"), + "product_name": ("product name", "product_name", "item name", "product", "상품명"), + "quantity": ("quantity", "qty", "수량", "수량(개)"), + "recipient_name": ("recipient", "recipient name", "buyer name", "consignee", "받는 사람", "받는사람", "수취인", "수령인"), + "recipient_phone": ("phone #", "phone#", "phone", "phone number", "contact number", "contact", + "전화", "전화번호", "연락처"), + "recipient_address": ("detail address", "detailed address", "address", "delivery address", + "shipping address", "full address", "주소", "배송지", "배송주소", "수령지 주소"), } # 사용자에게 보여줄 컬럼 이름(누락 안내 메시지용). 비교키는 소문자라 별도 표기. @@ -58,8 +75,14 @@ COLUMN_DISPLAY: dict[str, str] = { "seller_sku": "Seller SKU", "product_name": "Product Name", "quantity": "Quantity", + "recipient_name": "Recipient", + "recipient_phone": "Phone", + "recipient_address": "Address", } +# 박스 단위로 보존하는 받는 사람 필드(같은 박스의 첫 비어있지 않은 값 사용). +RECIPIENT_FIELDS: tuple[str, ...] = ("recipient_name", "recipient_phone", "recipient_address") + # 박스 묶음 기준 우선순위. 앞에서부터 비어있지 않은 첫 값으로 묶는다. BOX_KEY_PRIORITY: tuple[str, ...] = ("package_id", "tracking_id", "order_id") @@ -153,12 +176,16 @@ def group_parcels(rows: list[dict[str, str]]) -> list[dict[str, Any]]: "package_id": (row.get("package_id") or "").strip(), "tracking_id": (row.get("tracking_id") or "").strip(), "shipping_provider": (row.get("shipping_provider") or "").strip(), + "recipient_name": (row.get("recipient_name") or "").strip(), + "recipient_phone": (row.get("recipient_phone") or "").strip(), + "recipient_address": (row.get("recipient_address") or "").strip(), "_items": {}, # sku -> {product_name, quantity} } boxes[key] = box else: # 같은 박스인데 식별값이 빈 경우 뒤늦게 채운다(누락 보강). - for field in ("order_id", "package_id", "tracking_id", "shipping_provider"): + for field in ("order_id", "package_id", "tracking_id", "shipping_provider", + "recipient_name", "recipient_phone", "recipient_address"): if not box[field] and (row.get(field) or "").strip(): box[field] = (row.get(field) or "").strip() @@ -188,6 +215,9 @@ def group_parcels(rows: list[dict[str, str]]) -> list[dict[str, Any]]: "package_id": box["package_id"], "tracking_id": box["tracking_id"], "shipping_provider": box["shipping_provider"], + "recipient_name": box["recipient_name"], + "recipient_phone": box["recipient_phone"], + "recipient_address": box["recipient_address"], "items": items, } ) diff --git a/app/modules/dispatch/templates/dispatch/detail.html b/app/modules/dispatch/templates/dispatch/detail.html index f5feb6f..bb9d3ce 100644 --- a/app/modules/dispatch/templates/dispatch/detail.html +++ b/app/modules/dispatch/templates/dispatch/detail.html @@ -16,6 +16,10 @@ .dsp-order { font-size:15px;line-height:1.3;color:#334155;word-break:break-all; } .dsp-order b { display:block;font-size:14px;font-weight:700;color:#475569;letter-spacing:.5px; } .dsp-order span { font-size:27px;font-weight:800;color:#0f172a;letter-spacing:.2px; } + .dsp-recipient { border-top:1px dashed #e2e8f0;padding-top:8px; } + .dsp-rname { display:block;font-size:22px;font-weight:800;color:#0f172a;line-height:1.2;word-break:break-word; } + .dsp-raddr { font-size:14px;line-height:1.45;color:#334155;white-space:normal;word-break:break-word;margin-top:3px; } + .dsp-rphone { font-size:13px;color:#475569;margin-top:2px; } .dsp-track-link { color:#2563eb !important;font-size:18px;font-weight:800; text-decoration:underline !important;text-decoration-line:underline !important; text-decoration-thickness:2px;text-underline-offset:3px;cursor:pointer; } @@ -54,7 +58,7 @@ + placeholder="검색: Order ID / Tracking ID / 받는 사람 / SKU" />
@@ -64,7 +68,7 @@ data-parcel="{{ p.id }}" data-label_attached="{{ p.label_attached|lower }}" data-handed_to_kagayaku="{{ p.handed_to_kagayaku|lower }}" - data-search="{{ (p.order_id ~ ' ' ~ p.package_id ~ ' ' ~ p.tracking_id ~ ' ' ~ (p['items']|map(attribute='seller_sku')|join(' ')))|lower }}"> + data-search="{{ (p.order_id ~ ' ' ~ p.tracking_id ~ ' ' ~ p.recipient_name ~ ' ' ~ (p['items']|map(attribute='seller_sku')|join(' ')))|lower }}"> {% set logo = p.shipping_provider | courier_logo %} {% if logo %} {{ p.shipping_provider }} @@ -74,13 +78,17 @@
No. {{ p.seq }}
Order ID{{ p.order_id or '—' }}
-
Package {{ p.package_id or '—' }}
Tracking {% set turl = p.tracking_id | courier_track(p.shipping_provider) %} {% if turl %}{{ p.tracking_id }} {% else %}{{ p.tracking_id or '—' }}{% endif %}
+
+ {{ p.recipient_name or '—' }} + {% if p.recipient_address %}
{{ p.recipient_address }}
{% endif %} + {% if p.recipient_phone %}
{{ p.recipient_phone }}
{% endif %} +
    {% for it in p['items'] %}
  • {{ it.seller_sku }} × {{ it.quantity }}
  • diff --git a/app/modules/dispatch/templates/dispatch/new.html b/app/modules/dispatch/templates/dispatch/new.html index 216de28..0d4867e 100644 --- a/app/modules/dispatch/templates/dispatch/new.html +++ b/app/modules/dispatch/templates/dispatch/new.html @@ -5,11 +5,8 @@ {% include "dispatch/_nav.html" %}
    -

    TikTok 파일 업로드

    -

    - 03_TikTok_Order_Export.xlsx 만 있으면 출고 리스트가 자동 생성됩니다. - PDF 2개는 보관용(선택)입니다. 고객 이름/주소/전화는 저장하지 않습니다. -

    +

    출고 파일 업로드

    +

    {% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/app/static/dispatch/courier/jnt.png b/app/static/dispatch/courier/jnt.png new file mode 100644 index 0000000000000000000000000000000000000000..46e5cfbaba6c968d0a54e030374292b0cb9af13e GIT binary patch literal 45483 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

    yF=(c4n-z_^PT9Y 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 b 초보 물류 직원이 엑셀을 열지 않고 웹 화면만 보고 출고 작업을 하도록 만든 모듈. > main-app ERP 에 편입된 **모듈**이다(별도 앱/포트 아님). 인증은 기존 Google @@ -8,17 +8,29 @@ ## 1. 무엇을 하나 -TikTok Seller Center 에서 매일 받는 3개 파일을 업로드하면, 직원용 **출고 작업 -리스트(04_Daily_Dispatch_List)** 를 웹에서 자동 생성한다(엑셀 파일로 만들 필요 없음). +플랫폼(TikTok·Shopee)에서 매일 받는 파일을 업로드하면, 직원용 **출고 작업 +리스트** 를 웹에서 자동 생성한다(엑셀 파일로 만들 필요 없음). 플랫폼마다 올리는 +파일이 다르며, 업로드 화면이 플랫폼 선택에 따라 바뀐다. + +**TikTok** | 파일 | 용도 | 업로드 | | --- | --- | --- | -| 01_Picking_List.pdf | 전체 상품 피킹 참고용 | 선택(보관) | | 02_Shipping_Label_Packing_Slip.pdf | 실제 포장/라벨 부착 원본 | 선택(보관) | | 03_TikTok_Order_Export.xlsx | **자동 출고 리스트 기준 데이터** | **필수** | -작업 기준은 고객정보가 아니라 **Order ID / Package ID / Tracking ID / Seller SKU / -Quantity** 뿐이다. 고객 이름·주소·전화는 파싱 단계에서 버려 **DB 에 저장하지 않는다.** +> 01_Picking_List.pdf 는 더 이상 받지 않는다(피킹 요약은 데이터 엑셀로 생성). + +**Shopee** + +| 파일 | 용도 | 업로드 | +| --- | --- | --- | +| Shopee Seller Centre.pdf | 택배 송장(라벨) 원본 | 선택(보관) | +| Packing List.Doorstep Delivery.xlsx | **자동 출고 리스트 기준 데이터** | **필수** | + +작업 기준 키는 **Order ID / Package ID / Tracking ID / Seller SKU / Quantity** 이고, +**받는 사람 이름·전화·주소**는 출고 작업 카드 표시 + 취합 출고 엑셀 생성을 위해 +박스 단위로 **dispatch_db 에 저장한다**(개인정보 — 접근/보관 정책 검토 대상). ### 1박스 묶음 규칙 @@ -90,12 +102,21 @@ cd /opt/www/main && docker compose up -d --build web 업로드한 PDF/XLSX 원본은 아래에 저장되고 DB 에는 경로만 기록한다. ``` +# TikTok $DATA_DIR/dispatch/2026-06-19/<배치slug>_/ - 01_Picking_List.pdf 02_Shipping_Label_Packing_Slip.pdf 03_TikTok_Order_Export.xlsx +# Shopee +$DATA_DIR/dispatch/2026-06-19/<배치slug>_/ + Shopee_Seller_Centre.pdf + Packing_List.Doorstep_Delivery.xlsx ``` +배치 목록의 **다운로드** 버튼은 위 업로드 원본에 더해, 발송 날짜·고객 이름·전화· +주소·상품명·아이템 코드·주문 수량·오더번호·택배사·송장번호·주문처를 취합한 +**출고 엑셀**을 같은 zip 에 함께 넣는다. 엑셀 파일명: `YYYY.MM.DD(Ddd)_tictoc.xlsx` +(Shopee 는 끝이 `_shopee`). + 같은 날짜/플랫폼이라도 batch_name 으로 구분해 **새 배치**로 생성된다(덮어쓰지 않음). 예: "2026-06-19 TikTok 오전 출고", "2026-06-19 TikTok 오후 출고". @@ -107,10 +128,14 @@ $DATA_DIR/dispatch/2026-06-19/<배치slug>_/ - Quantity 가 비면 1 로 처리한다. - Tracking ID 가 숫자로 읽혀도 문자열로 보존한다(과학표기/`.0` 제거). - NaN/None 은 빈 문자열로 처리한다. -- 고객 이름/주소/전화 컬럼은 매핑하지 않아 **읽는 즉시 버린다**(미저장). +- 받는 사람 이름/전화/주소 컬럼은 매핑되면 **박스 단위로 보존**한다(작업 카드·엑셀). +- 헤더가 첫 행이 아닐 수 있어(Shopee 는 제목 행이 위에 붙을 수 있음) 상단 몇 행을 + 훑어 필수 컬럼이 잡히는 첫 행을 헤더로 본다. -가능한 헤더(앞뒤 공백·대소문자 무시): Order ID, Package ID, Tracking ID, -Shipping Provider Name, Seller SKU, Product Name, Quantity. +가능한 헤더(앞뒤 공백·대소문자 무시, TikTok·Shopee 별칭은 `store.COLUMN_ALIASES`): +Order ID(또는 Order SN), Package ID, Tracking ID(또는 Tracking Number), +Shipping Provider Name(또는 Shipping Option/Courier), Seller SKU(또는 SKU Reference No.), +Product Name, Quantity, Recipient(받는 사람), Phone(전화), Address(주소). --- @@ -128,6 +153,9 @@ python -m app.modules.dispatch.tests.test_grouping # 앱 의존성 설치 환 - 스택: FastAPI · PostgreSQL(psycopg3) · Jinja2 · 기존 ERP CSS(Bootstrap 미사용, 레포 공통 `erp.css` 재사용) · **openpyxl**(스펙의 pandas 대신, 레포 의존성 경량 유지). -- 코드: `app/modules/dispatch/` (`store.py` 순수 로직 / `parser.py` 엑셀 / `db.py` - DB I/O / `router.py` 라우팅 / `templates/dispatch/` 화면). -- 개인정보 미보관: 스키마에 이름/주소/전화 컬럼 자체가 없다. +- 코드: `app/modules/dispatch/` (`store.py` 순수 로직 / `parser.py` 엑셀 파싱 / + `export.py` 취합 출고 엑셀 생성 / `db.py` DB I/O / `router.py` 라우팅 / + `templates/dispatch/` 화면). +- 개인정보 보관: `dispatch_parcels.recipient_name/recipient_phone/recipient_address`. + 기존 DB 에는 마이그레이션 `scripts/sql/dispatch_add_recipient_columns.sql` 로 컬럼을 + 추가한다(멱등). 접근/보관 기간 정책을 별도로 검토할 것. diff --git a/scripts/sql/dispatch_add_recipient_columns.sql b/scripts/sql/dispatch_add_recipient_columns.sql new file mode 100644 index 0000000..8d40fb8 --- /dev/null +++ b/scripts/sql/dispatch_add_recipient_columns.sql @@ -0,0 +1,24 @@ +-- ===================================================================== +-- dispatch_db 마이그레이션 — 박스에 받는 사람(PII) 컬럼 추가 +-- ===================================================================== +-- 멱등(idempotent): 여러 번 실행해도 안전(ADD COLUMN IF NOT EXISTS). +-- +-- 배경: 기존 dispatch 모듈은 개인정보(이름/주소/전화)를 저장하지 않는 구조였다. +-- 운영 요청으로 출고 작업 카드 표시 + 출고 엑셀(취합본) 생성을 위해 +-- 받는 사람 정보를 박스 단위로 보관하도록 정책을 변경한다. +-- +-- 실행 (운영 PostgreSQL 컨테이너명: postgres-db): +-- docker exec -i postgres-db psql -U postgres -d dispatch_db \ +-- < scripts/sql/dispatch_add_recipient_columns.sql +-- +-- ⚠️ 개인정보 컬럼이다. 접근 권한/보관 기간 정책을 함께 검토할 것. +-- ===================================================================== + +\set ON_ERROR_STOP on +\connect dispatch_db + +ALTER TABLE dispatch_parcels ADD COLUMN IF NOT EXISTS recipient_name TEXT NOT NULL DEFAULT ''; +ALTER TABLE dispatch_parcels ADD COLUMN IF NOT EXISTS recipient_phone TEXT NOT NULL DEFAULT ''; +ALTER TABLE dispatch_parcels ADD COLUMN IF NOT EXISTS recipient_address TEXT NOT NULL DEFAULT ''; + +SELECT 'dispatch_parcels recipient columns ready' AS status; diff --git a/scripts/sql/dispatch_db_init.sql b/scripts/sql/dispatch_db_init.sql index cb8b524..0b5bcd6 100644 --- a/scripts/sql/dispatch_db_init.sql +++ b/scripts/sql/dispatch_db_init.sql @@ -22,7 +22,8 @@ -- 주의: -- - 기존 DB 가 있으면 DROP 하지 않는다(CREATE DATABASE 는 미존재 시에만). -- - 비밀번호는 절대 Git 에 커밋하지 않는다. psql -v 로만 전달. --- - 개인정보(고객 이름/주소/전화)는 저장하지 않는 구조다(컬럼 자체가 없음). +-- - 받는 사람 정보(이름/전화/주소)는 출고 작업 카드 표시 + 출고 엑셀 생성을 +-- 위해 박스 단위로 보관한다(개인정보 — 접근/보관 정책 검토 대상). -- - 작업 기준 키: order_id / package_id / tracking_id / seller_sku / quantity. -- ===================================================================== @@ -80,7 +81,7 @@ CREATE TRIGGER trg_dispatch_batches_updated -- ════════════════════════════════════════════════════════════ -- 2) 출고 박스 (1박스 = 1행 = 1작업카드) -- 묶음 기준: package_id > tracking_id > order_id (파서가 결정). --- 개인정보 컬럼 없음(이름/주소/전화 저장 안 함). +-- 받는 사람(recipient_*) 정보 보관 — 작업 카드 표시 + 출고 엑셀 생성용. -- seq 는 배치 내 박스 표시 순번(No). -- ════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS dispatch_parcels ( @@ -91,6 +92,9 @@ CREATE TABLE IF NOT EXISTS dispatch_parcels ( package_id TEXT NOT NULL DEFAULT '', tracking_id TEXT NOT NULL DEFAULT '', shipping_provider TEXT NOT NULL DEFAULT '', + recipient_name TEXT NOT NULL DEFAULT '', + recipient_phone TEXT NOT NULL DEFAULT '', + recipient_address TEXT NOT NULL DEFAULT '', product_ready BOOLEAN NOT NULL DEFAULT FALSE, packed BOOLEAN NOT NULL DEFAULT FALSE, label_attached BOOLEAN NOT NULL DEFAULT FALSE,