From 4b5bccb86a6c60a491cc0a01efa5ca75fb781bd7 Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Wed, 1 Apr 2026 22:03:37 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20cart=5Fitems=20DB=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20+=20=EC=9E=85=EA=B3=A0=EB=B2=88=ED=98=B8=20=ED=99=95?= =?UTF-8?q?=EC=A0=95=EC=8B=9C=EC=A0=90=20=EC=B1=84=EB=B2=88=20+=20?= =?UTF-8?q?=EC=9E=A5=EB=B0=94=EA=B5=AC=EB=8B=88=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cart_items: id를 crypto.randomUUID()로 생성 (NOT NULL 대응) - cart_items: row_data에 품목 정보 JSON 저장 - 입고번호: 장바구니에서 미리 표시 안 함 → 확정 시 생성 - 장바구니 조회: 올바른 API 파라미터로 수정 - 담기 취소 시 dbId로 정확한 cart_items DELETE --- .../pop/hardcoded/inbound/InboundCart.tsx | 2 + .../pop/hardcoded/inbound/InboundCartPage.tsx | 55 ++++++++----------- .../pop/hardcoded/inbound/PurchaseInbound.tsx | 25 ++++++--- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/frontend/components/pop/hardcoded/inbound/InboundCart.tsx b/frontend/components/pop/hardcoded/inbound/InboundCart.tsx index 47de2029..36efb141 100644 --- a/frontend/components/pop/hardcoded/inbound/InboundCart.tsx +++ b/frontend/components/pop/hardcoded/inbound/InboundCart.tsx @@ -22,6 +22,8 @@ interface Warehouse { export interface CartItem { id: string; + /** cart_items 테이블의 PK (UUID) — DB 삭제용 */ + dbId?: string; /** purchase_detail or purchase_order_mng */ source_table: string; /** PK of the source row */ diff --git a/frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx b/frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx index 66ab2737..669f15c0 100644 --- a/frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx +++ b/frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx @@ -108,8 +108,7 @@ export function InboundCartPage() { screen_id: "pop-purchase-inbound", status: "pending", pageSize: 100, - sortColumn: "created_date", - sortDirection: "desc", + orderBy: "created_date DESC", }, }); @@ -190,18 +189,8 @@ export function InboundCartPage() { }, []); /* ------------------------------------------------------------------ */ - /* Generate inbound number */ + /* Generate inbound number (확정 시점에만 호출 — 동시접속 충돌 방지) */ /* ------------------------------------------------------------------ */ - const fetchInboundNumber = useCallback(async () => { - try { - const res = await apiClient.get("/receiving/generate-number"); - if (res.data?.success && res.data?.data) { - setInboundNumber(res.data.data); - } - } catch { - /* generate on confirm as fallback */ - } - }, []); /* ------------------------------------------------------------------ */ /* Initial load */ @@ -211,8 +200,7 @@ export function InboundCartPage() { fetchedRef.current = true; fetchCartItems(); fetchWarehouses(); - fetchInboundNumber(); - }, [fetchCartItems, fetchWarehouses, fetchInboundNumber]); + }, [fetchCartItems, fetchWarehouses]); /* ------------------------------------------------------------------ */ /* Selection */ @@ -376,17 +364,16 @@ export function InboundCartPage() { setResultMsg(null); try { - // Use existing inbound number or generate new one - let finalNumber = inboundNumber; - if (!finalNumber) { - try { - const numRes = await apiClient.get("/receiving/generate-number"); - if (numRes.data?.success && numRes.data?.data) { - finalNumber = numRes.data.data; - } - } catch { - /* backend will handle */ + // 확정 시점에 채번 (동시접속 충돌 방지) + let finalNumber = ""; + try { + const numRes = await apiClient.get("/receiving/generate-number"); + if (numRes.data?.success && numRes.data?.data) { + finalNumber = numRes.data.data; + setInboundNumber(finalNumber); } + } catch { + /* backend will handle */ } // POST /api/receiving -- same payload structure as PC @@ -559,11 +546,9 @@ export function InboundCartPage() { | {selectedWarehouseName} )} - {inboundNumber && ( - - {inboundNumber} - - )} + + {inboundNumber || "확정 시 자동생성"} + {/* Info fields: 3 columns */} @@ -609,13 +594,17 @@ export function InboundCartPage() { - {/* Inbound number (readonly) */} + {/* Inbound number (readonly — 확정 시점에 채번) */}
-
- {inboundNumber || "자동 채번"} +
+ {inboundNumber ? ( + {inboundNumber} + ) : ( + 확정 시 자동생성 + )}
diff --git a/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx b/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx index ec39f9cd..1d4f1dc0 100644 --- a/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx +++ b/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx @@ -238,8 +238,10 @@ export function PurchaseInbound({ onCartCountChange }: PurchaseInboundProps = {} const order = numpadTarget; if (cartItems.find((c) => c.id === order.id)) return; + const cartDbId = crypto.randomUUID(); const newItem: CartItem = { id: order.id, + dbId: cartDbId, source_table: order.source_table, source_id: order.id, purchase_no: order.purchase_no, @@ -262,11 +264,12 @@ export function PurchaseInbound({ onCartCountChange }: PurchaseInboundProps = {} setNumpadTarget(null); // Save to cart_items table (background, non-blocking) - // cart_items 실제 컬럼: screen_id, source_table, row_key, row_data, quantity, unit + // cart_items.id는 NOT NULL + 자동생성 없음 → 프론트에서 UUID 생성 필수 apiClient.post("/pop/execute-action", { tasks: [{ type: "cart-save" }], cartChanges: { toCreate: [{ + id: cartDbId, screen_id: "pop-purchase-inbound", source_table: order.source_table, row_key: order.id, @@ -281,9 +284,12 @@ export function PurchaseInbound({ onCartCountChange }: PurchaseInboundProps = {} material: order.material || "", order_qty: order.order_qty, remain_qty: order.remain_qty, + order_date: order.order_date || "", + inspection_type: order.inspection_type, }), quantity: String(Math.min(qty, order.remain_qty)), unit: "EA", + package_entries: packages.length > 0 ? JSON.stringify(packages) : null, status: "pending", }], }, @@ -294,14 +300,17 @@ export function PurchaseInbound({ onCartCountChange }: PurchaseInboundProps = {} /* Remove from cart (cancel) */ const handleRemoveFromCart = (id: string) => { + const item = cartItems.find((c) => c.id === id); setCartItems((prev) => prev.filter((c) => c.id !== id)); - // Delete from cart_items DB (background) - apiClient.post("/pop/execute-action", { - tasks: [{ type: "cart-save" }], - cartChanges: { - toDelete: [id], - }, - }).catch(() => {}); + // Delete from cart_items DB (background) — dbId(UUID)로 삭제 + if (item?.dbId) { + apiClient.post("/pop/execute-action", { + tasks: [{ type: "cart-save" }], + cartChanges: { + toDelete: [item.dbId], + }, + }).catch(() => {}); + } }; /* Search */