diff --git a/app/integrations/cafe24/products.py b/app/integrations/cafe24/products.py
index 845d9ce..c2ba18c 100644
--- a/app/integrations/cafe24/products.py
+++ b/app/integrations/cafe24/products.py
@@ -402,6 +402,33 @@ def list_variants(client: Cafe24Client, product_no: int) -> list[dict[str, Any]]
return variants if isinstance(variants, list) else []
+def wait_for_variants(
+ client: Cafe24Client,
+ product_no: int,
+ expected: int,
+ *,
+ attempts: int = 4,
+ delay: float = 0.8,
+) -> list[dict[str, Any]]:
+ """옵션 생성 직후 카페24가 자동 생성한 품목이 조회될 때까지 짧게 재시도한다.
+
+ 상세설명과 같은 읽기 지연이 품목 조회에도 있다 — POST options 직후 GET variants 가
+ 비어 있거나 일부만 올 수 있다. 기대 개수(옵션값 수)만큼 오면 바로 돌려준다.
+ 끝까지 못 채워도 마지막 결과를 돌려준다(호출부가 안내).
+ """
+ latest: list[dict[str, Any]] = []
+ for attempt in range(max(1, attempts)):
+ if attempt:
+ time.sleep(delay)
+ try:
+ latest = list_variants(client, product_no)
+ except Exception: # noqa: BLE001 — 조회 실패는 다음 시도로
+ latest = []
+ if len(latest) >= max(1, expected):
+ return latest
+ return latest
+
+
def update_variants(
client: Cafe24Client, product_no: int, requests: list[dict[str, Any]]
) -> list[dict[str, Any]]:
diff --git a/app/modules/cafe24/routes_product_info.py b/app/modules/cafe24/routes_product_info.py
index fe10e9e..3026b28 100644
--- a/app/modules/cafe24/routes_product_info.py
+++ b/app/modules/cafe24/routes_product_info.py
@@ -327,11 +327,11 @@ def options_create(
st.log_audit(actor=actor, action="create_options", product_no=product_no, result="SUCCESS",
detail=f"옵션 '{body['options'][0]['option_name']}' 생성: {', '.join(values)}")
logger.info("카페24 상품 %s 옵션 생성 (%s)", product_no, actor)
- try:
- variants = products.list_variants(api.client, product_no)
- except Cafe24Error as exc: # 옵션은 만들어졌다 — 품목 목록만 비워서 돌려준다.
- logger.warning("카페24 상품 %s 옵션 생성 후 품목 조회 실패: %s", product_no, exc)
- variants = []
+ # 카페24가 자동 생성한 품목(코드 부여)을 바로 돌려준다 — 읽기 지연이 있어 짧게 재시도.
+ # 화면은 이 코드로 자체코드·추가금액을 이어서 PUT 한다.
+ variants = products.wait_for_variants(api.client, product_no, len(values))
+ if len(variants) < len(values):
+ logger.warning("카페24 상품 %s 옵션 생성 후 품목 %s/%s 건만 조회됨", product_no, len(variants), len(values))
return {"ok": True, "option": _options_view(created), "variants": [_variant_view(v) for v in variants]}
@@ -484,7 +484,7 @@ def variants_update(
for r in results:
code = str(r.get("variant_code") or "")
if code in by_code:
- for key in ("custom_variant_code", "additional_amount", "display", "selling"):
+ for key in ("custom_variant_code", "additional_amount", "display", "selling", "display_order"):
if key in r and r[key] is not None:
by_code[code][key] = r[key]
st.save_write_snapshot(product_no, "variants", by_code)
diff --git a/app/modules/cafe24/store.py b/app/modules/cafe24/store.py
index 59f711c..9874cdf 100644
--- a/app/modules/cafe24/store.py
+++ b/app/modules/cafe24/store.py
@@ -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
diff --git a/app/modules/cafe24/templates/cafe24/products.html b/app/modules/cafe24/templates/cafe24/products.html
index 00e5216..a56b251 100644
--- a/app/modules/cafe24/templates/cafe24/products.html
+++ b/app/modules/cafe24/templates/cafe24/products.html
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
-
+
{% endblock %}
{% block content %}
@@ -125,6 +125,7 @@
{% endblock %}
{% block scripts %}
+