diff --git a/app/modules/cupang/db.py b/app/modules/cupang/db.py index 33ea977..39da6f7 100644 --- a/app/modules/cupang/db.py +++ b/app/modules/cupang/db.py @@ -338,6 +338,73 @@ class CupangDBStore: if cur.rowcount == 0: raise KeyError(product_id) + def update_product( + self, + *, + product_id: int, + product_name: str, + product_code: str, + coupang_item_code: str = "", + ) -> dict[str, Any]: + """등록된 제품 수정(제품명/제품코드/쿠팡상품코드). + + 제품코드가 바뀌면 같은 코드로 연결돼 있던 박스 규칙(cupang_box_rules)의 + product_code 도 함께 옮긴다(출고 라인은 과거 기록이라 스냅샷 유지). + """ + name = (product_name or "").strip() + code = (product_code or "").strip() + cic = (coupang_item_code or "").strip() + if not name or not code: + raise ValueError("제품코드와 제품명 모두 필요합니다.") + with self._pool.connection() as conn: + with conn.transaction(): + old = conn.execute( + "SELECT * FROM cupang_products WHERE id = %s FOR UPDATE", + (product_id,), + ).fetchone() + if old is None: + raise KeyError(product_id) + old_code = (old["product_code"] or "").strip() + if code != old_code: + dup = conn.execute( + "SELECT 1 FROM cupang_products " + "WHERE product_code = %s AND id <> %s", + (code, product_id), + ).fetchone() + if dup: + raise ValueError(f"제품코드 {code} 는 이미 등록돼 있습니다.") + row = conn.execute( + """ + UPDATE cupang_products + SET product_name = %s, + product_code = %s, + coupang_item_code = %s + WHERE id = %s + RETURNING * + """, + (name, code, cic, product_id), + ).fetchone() + if code != old_code: + # 새 코드로 된 규칙이 이미 있으면 옮기지 않는다(중복 방지). + exists_new = conn.execute( + "SELECT 1 FROM cupang_box_rules WHERE product_code = %s", + (code,), + ).fetchone() + if not exists_new: + conn.execute( + "UPDATE cupang_box_rules " + "SET product_code = %s, product_name_snapshot = %s " + "WHERE product_code = %s", + (code, name, old_code), + ) + else: + conn.execute( + "UPDATE cupang_box_rules SET product_name_snapshot = %s " + "WHERE product_code = %s", + (name, code), + ) + return self._product_serialize(row) + def toggle_product_active(self, *, product_id: int) -> dict[str, Any]: """활성 ↔ 비활성 뒤집기. 갱신된 행을 반환.""" with self._pool.connection() as conn: diff --git a/app/modules/cupang/router.py b/app/modules/cupang/router.py index 84c6dae..a340a6b 100644 --- a/app/modules/cupang/router.py +++ b/app/modules/cupang/router.py @@ -917,6 +917,31 @@ async def product_set_active( return RedirectResponse(url="/cupang/products", status_code=303) +@router.post("/api/products/{product_id:int}/edit") +async def product_edit( + request: Request, + product_id: int, + payload: dict[str, Any] = Body(...), + user: dict[str, Any] = Depends(_require_user), +) -> JSONResponse: + """등록된 제품 수정. body: {product_name, product_code, coupang_item_code}.""" + store = _store(request) + if store is None: + raise HTTPException(status_code=503, detail="cupang_db 미설정") + try: + product = store.update_product( + product_id=product_id, + product_name=str(payload.get("product_name") or ""), + product_code=str(payload.get("product_code") or ""), + coupang_item_code=str(payload.get("coupang_item_code") or ""), + ) + except KeyError: + raise HTTPException(status_code=404, detail="제품을 찾을 수 없습니다.") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + return JSONResponse({"ok": True, "product": product}) + + @router.post("/api/products/{product_id:int}/toggle") async def product_toggle_active( request: Request, diff --git a/app/modules/cupang/templates/cupang/products.html b/app/modules/cupang/templates/cupang/products.html index 259caa2..1ae9c19 100644 --- a/app/modules/cupang/templates/cupang/products.html +++ b/app/modules/cupang/templates/cupang/products.html @@ -81,6 +81,8 @@
+
@@ -137,6 +139,90 @@ })(); + + + + +