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
+27
View File
@@ -1094,6 +1094,33 @@ def test_build_variant_updates():
pass
else:
raise AssertionError("bad code accepted")
# 드래그 정렬 → display_order (1~300)
assert store.build_variant_updates([{"variant_code": "P000000R000A", "display_order": "3"}]) == [
{"variant_code": "P000000R000A", "display_order": 3}
]
for bad in ("0", "301", "x"):
try:
store.build_variant_updates([{"variant_code": "P000000R000A", "display_order": bad}])
except ValueError:
pass
else:
raise AssertionError(bad)
def test_parse_option_values_accepts_list():
assert store.parse_option_values(["빨강, 큰 것", " 파랑 ", "", "빨강, 큰 것"]) == ["빨강, 큰 것", "파랑"]
def test_wait_for_variants_retries_until_expected():
calls = {"n": 0}
class _Client:
def get(self, path, **kw):
calls["n"] += 1
return {"variants": [{"variant_code": "A"}] * (2 if calls["n"] >= 3 else 1)}
out = products.wait_for_variants(_Client(), 7, 2, attempts=5)
assert len(out) == 2 and calls["n"] == 3
class _RouteClient: