fix: cart_items DB 저장 + 입고번호 확정시점 채번 + 장바구니 조회
- cart_items: id를 crypto.randomUUID()로 생성 (NOT NULL 대응) - cart_items: row_data에 품목 정보 JSON 저장 - 입고번호: 장바구니에서 미리 표시 안 함 → 확정 시 생성 - 장바구니 조회: 올바른 API 파라미터로 수정 - 담기 취소 시 dbId로 정확한 cart_items DELETE
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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}
|
||||
</span>
|
||||
)}
|
||||
{inboundNumber && (
|
||||
<span className="ml-auto text-[11px] font-mono text-gray-500">
|
||||
{inboundNumber}
|
||||
</span>
|
||||
)}
|
||||
<span className="ml-auto text-[11px] font-mono text-gray-400">
|
||||
{inboundNumber || "확정 시 자동생성"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Info fields: 3 columns */}
|
||||
@@ -609,13 +594,17 @@ export function InboundCartPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Inbound number (readonly) */}
|
||||
{/* Inbound number (readonly — 확정 시점에 채번) */}
|
||||
<div>
|
||||
<label className="text-[11px] font-semibold text-gray-400 mb-1 block">
|
||||
입고번호
|
||||
</label>
|
||||
<div className="w-full px-3 py-2.5 border border-gray-200 rounded-lg text-sm bg-gray-50 text-gray-600 font-mono">
|
||||
{inboundNumber || "자동 채번"}
|
||||
<div className="w-full px-3 py-2.5 border border-gray-200 rounded-lg text-sm bg-gray-50 font-mono">
|
||||
{inboundNumber ? (
|
||||
<span className="text-gray-700">{inboundNumber}</span>
|
||||
) : (
|
||||
<span className="text-gray-400">확정 시 자동생성</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user