feat(dispatch): 라벨 PDF에서 받는 사람(이름/전화/주소) 추출

xlsx 엔 받는 사람이 없거나(Shopee) 가려져(TikTok) 라벨 PDF 를 출처로 사용.
업로드 시 Order ID 로 박스와 조인해 채운다(하이브리드).

- pdf_parser.py 추가 (pdfplumber)
  · Shopee SPX 라벨: 2단 중 왼쪽 열만 읽어 Recipient 블록 추출(전화 미표시)
  · TikTok 라벨: 배송사별 양식 대응 — To <이름> (+60)…(Ninja), Receiver <이름>(NDD)
  · 주소 영역에 섞인 10자리+ 바코드 숫자 제거(우편번호 5자리 보존)
  · PDF 없거나 파싱 실패해도 업로드 진행(PII 만 빈 값)
- router: 라벨 PDF 파싱 결과를 parcels 에 머지 후 저장
- requirements: pdfplumber>=0.11 (Docker 재빌드 필요)
- new.html: PDF 가 받는 사람 출처임을 안내(권장)
- .gitignore: docs/samples/ (실제 고객 PII PDF — 커밋 금지)
- 문서 갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 14:45:35 +09:00
parent 691eecee52
commit 106cf70922
6 changed files with 244 additions and 6 deletions
+17
View File
@@ -30,6 +30,7 @@ from app.timezone import now_kst, today_kst
from . import export, store
from .parser import ParseError, parse_export
from .pdf_parser import PdfParseError, extract_recipients
logger = logging.getLogger("dispatch.router")
@@ -251,6 +252,22 @@ async def batch_create(
detail="엑셀에서 출고할 박스를 찾지 못했습니다. 컬럼/데이터를 확인하세요.",
)
# 라벨 PDF 가 있으면 받는 사람(이름/전화/주소)을 추출해 Order ID 로 박스에 채운다.
# 이름/주소는 xlsx 에 없거나 가려져 있어 PDF 가 출처. PDF 없거나 실패 시 빈 값.
if label_path:
try:
recipients = extract_recipients(label_path, platform)
except PdfParseError as exc:
logger.warning("라벨 PDF 파싱 실패 — PII 없이 진행: %s", exc)
recipients = {}
for p in parcels:
rec = recipients.get((p.get("order_id") or "").strip())
if not rec:
continue
for field in ("recipient_name", "recipient_phone", "recipient_address"):
if rec.get(field) and not (p.get(field) or "").strip():
p[field] = rec[field]
batch = st.create_batch(
platform=platform,
dispatch_date=dispatch_date,