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 %} +