feat(cafe24): 옵션값+품목 통합 목록, 드래그 순서, 옵션값 불러오기

- 옵션 1개 상품은 한 행에 썸네일·옵션값 이름·품목코드(복사)·자체코드·
  추가금액·진열·판매를 두고 「저장」 한 번으로 옵션 PUT → 품목 PUT.
- 옵션 없는 상품: 행 추가/삭제 후 저장 → 옵션 생성 → 카페24가 부여한
  품목코드를 재시도 조회(products.wait_for_variants)로 받아 자체코드·
  추가금액·썸네일까지 이어서 반영.
- 순서 드래그는 품목 display_order 로 저장(옵션값 재배열 PUT 은 위치 짝맞춤
  때문에 품목코드↔이름이 뒤바뀔 수 있어 사용하지 않음).
- 「옵션값 불러오기」: 탭/공백 구분 텍스트(이름·판매가·추가금액·자체코드)
  붙여넣기 → 행 자동 채움. 기존 옵션은 이름으로 짝지어 코드/금액만.
- 옵션 2개 이상(조합) 상품은 기존 2열 화면 유지.
- 모달 JS 를 app/static/cafe24-options.js 로 분리. 유닛테스트 90 통과,
  통합 하네스 통과, 헤드리스 크롬 렌더 확인.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 23:25:52 +09:00
parent be0d44ded6
commit 8125a59366
8 changed files with 1002 additions and 360 deletions
+18 -3
View File
@@ -649,10 +649,16 @@ ADDITIONAL_AMOUNT_MAX = 2_147_483_647
def parse_option_values(raw: object) -> list[str]:
"""'빨강, 파랑\\n노랑' → ['빨강','파랑','노랑'] (중복·빈 값 제거, 순서 유지)."""
text = str(raw or "")
"""'빨강, 파랑\\n노랑' 또는 ['빨강','파랑'] → ['빨강','파랑','노랑'] (중복·빈 값 제거, 순서 유지).
목록으로 오면 항목 안의 쉼표는 이름의 일부로 본다(화면이 행 단위로 보낼 때).
"""
if isinstance(raw, (list, tuple)):
parts = [str(p) for p in raw]
else:
parts = re.split(r"[,\n]", str(raw or ""))
seen: list[str] = []
for part in re.split(r"[,\n]", text):
for part in parts:
value = part.strip()
if value and value not in seen:
seen.append(value)
@@ -785,6 +791,15 @@ def build_variant_updates(rows: list[dict]) -> list[dict]:
for flag in ("display", "selling"):
if row.get(flag) is not None:
item[flag] = "T" if parse_tristate(row[flag]) else "F"
# 진열 순서(1~300) — 카페24 문서: 조합형 옵션 품목에만. 화면의 드래그 정렬이 보낸다.
if row.get("display_order") is not None:
try:
order = int(row["display_order"])
except (TypeError, ValueError):
raise ValueError("진열 순서는 숫자여야 합니다.") from None
if not 1 <= order <= 300:
raise ValueError("진열 순서는 1~300 사이여야 합니다.")
item["display_order"] = order
if len(item) > 1:
out.append(item)
return out