diff --git a/frontend/app/(main)/COMPANY_10/logistics/inventory/page.tsx b/frontend/app/(main)/COMPANY_10/logistics/inventory/page.tsx index 7d2a96c6..87909e6f 100644 --- a/frontend/app/(main)/COMPANY_10/logistics/inventory/page.tsx +++ b/frontend/app/(main)/COMPANY_10/logistics/inventory/page.tsx @@ -140,31 +140,47 @@ export default function InventoryStatusPage() { Record >({}); - // 카테고리 로드 + // 사용자 맵 (writer → 이름) + const [userMap, setUserMap] = useState>({}); + + // 카테고리 + 사용자 로드 useEffect(() => { const load = async () => { const optMap: Record = {}; const flatten = (vals: any[]): { code: string; label: string }[] => { const result: { code: string; label: string }[] = []; for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); if (v.children?.length) result.push(...flatten(v.children)); } return result; }; - for (const col of ["status", "unit"]) { + // inventory_stock 카테고리 + for (const col of ["status"]) { try { - const res = await apiClient.get( - `/table-categories/${STOCK_TABLE}/${col}/values` - ); + const res = await apiClient.get(`/table-categories/${STOCK_TABLE}/${col}/values`); if (res.data?.success) optMap[col] = flatten(res.data.data || []); - } catch { - /* skip */ - } + } catch { /* skip */ } } + // item_info 단위 카테고리 + try { + const res = await apiClient.get("/table-categories/item_info/unit/values"); + if (res.data?.success) optMap["item_unit"] = flatten(res.data.data || []); + } catch { /* skip */ } setCategoryOptions(optMap); }; load(); + // 사용자 목록 로드 + apiClient.get("/admin/users").then((res) => { + const users = res.data?.data || res.data || []; + const map: Record = {}; + for (const u of users) { + const id = u.user_id || u.id; + const name = u.user_name || u.name || id; + if (id) map[id] = name; + } + setUserMap(map); + }).catch(() => {}); }, []); // 재고 목록 조회 @@ -193,10 +209,12 @@ export default function InventoryStatusPage() { }; const data = raw.map((r: any) => { const itemInfo = itemMap.get(r.item_code) as any; + const rawUnit = itemInfo?.unit || r.unit || ""; return { ...r, item_name: itemInfo?.name || "", - unit: itemInfo?.unit || resolve("unit", r.unit), + unit: resolve("item_unit", rawUnit) || rawUnit, + warehouse_code: whMap.get(r.warehouse_code) || r.warehouse_code || "", warehouse_name: whMap.get(r.warehouse_code) || r.warehouse_code || "", status: resolve("status", r.status), _isLow: r.safety_qty && Number(r.current_qty) < Number(r.safety_qty), @@ -613,7 +631,7 @@ export default function InventoryStatusPage() { {h.remark || h.reason || ""} - {h.writer || h.created_by || ""} + {userMap[h.writer] || userMap[h.created_by] || h.writer || h.created_by || ""} ))} diff --git a/frontend/app/(main)/COMPANY_10/logistics/outbound/page.tsx b/frontend/app/(main)/COMPANY_10/logistics/outbound/page.tsx index c1ffbd40..f3bafd20 100644 --- a/frontend/app/(main)/COMPANY_10/logistics/outbound/page.tsx +++ b/frontend/app/(main)/COMPANY_10/logistics/outbound/page.tsx @@ -311,6 +311,34 @@ export default function OutboundPage() { const [editMode, setEditMode] = useState(false); const [editItemIds, setEditItemIds] = useState([]); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); + useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); + }, []); + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 소스 데이터 const [sourceKeyword, setSourceKeyword] = useState(""); const [sourceLoading, setSourceLoading] = useState(false); @@ -1337,6 +1365,7 @@ export default function OutboundPage() { data={pagedItems} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1772,10 +1801,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1826,8 +1857,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx index 5b730674..9351d06f 100644 --- a/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx @@ -353,17 +353,45 @@ export default function ReceivingPage() { // 구매관리 division 코드 (라벨 기준 조회) const [purchaseDivisionCode, setPurchaseDivisionCode] = useState(""); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); - // 구매관리 division 코드 로드 + // 구매관리 division 코드 + 재질/단위 카테고리 로드 useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; // division 카테고리에서 "구매관리" 라벨의 코드 조회 apiClient.get("/table-categories/item_info/division/values").then((res) => { const vals = res.data?.data || []; - const found = vals.find((v: any) => (v.value_label || v.label) === "구매관리"); + const found = vals.find((v: any) => (v.valueLabel || v.value_label || v.label) === "구매관리"); if (found) setPurchaseDivisionCode(found.value_code || found.code); }).catch(() => {}); + // 재질, 단위 카테고리 + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); }, []); + // 카테고리 코드→라벨 변환 + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 목록 조회 const fetchList = useCallback(async () => { setLoading(true); @@ -1287,6 +1315,7 @@ export default function ReceivingPage() { data={items} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1742,10 +1771,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1798,8 +1829,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_16/logistics/inventory/page.tsx b/frontend/app/(main)/COMPANY_16/logistics/inventory/page.tsx index 7d2a96c6..87909e6f 100644 --- a/frontend/app/(main)/COMPANY_16/logistics/inventory/page.tsx +++ b/frontend/app/(main)/COMPANY_16/logistics/inventory/page.tsx @@ -140,31 +140,47 @@ export default function InventoryStatusPage() { Record >({}); - // 카테고리 로드 + // 사용자 맵 (writer → 이름) + const [userMap, setUserMap] = useState>({}); + + // 카테고리 + 사용자 로드 useEffect(() => { const load = async () => { const optMap: Record = {}; const flatten = (vals: any[]): { code: string; label: string }[] => { const result: { code: string; label: string }[] = []; for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); if (v.children?.length) result.push(...flatten(v.children)); } return result; }; - for (const col of ["status", "unit"]) { + // inventory_stock 카테고리 + for (const col of ["status"]) { try { - const res = await apiClient.get( - `/table-categories/${STOCK_TABLE}/${col}/values` - ); + const res = await apiClient.get(`/table-categories/${STOCK_TABLE}/${col}/values`); if (res.data?.success) optMap[col] = flatten(res.data.data || []); - } catch { - /* skip */ - } + } catch { /* skip */ } } + // item_info 단위 카테고리 + try { + const res = await apiClient.get("/table-categories/item_info/unit/values"); + if (res.data?.success) optMap["item_unit"] = flatten(res.data.data || []); + } catch { /* skip */ } setCategoryOptions(optMap); }; load(); + // 사용자 목록 로드 + apiClient.get("/admin/users").then((res) => { + const users = res.data?.data || res.data || []; + const map: Record = {}; + for (const u of users) { + const id = u.user_id || u.id; + const name = u.user_name || u.name || id; + if (id) map[id] = name; + } + setUserMap(map); + }).catch(() => {}); }, []); // 재고 목록 조회 @@ -193,10 +209,12 @@ export default function InventoryStatusPage() { }; const data = raw.map((r: any) => { const itemInfo = itemMap.get(r.item_code) as any; + const rawUnit = itemInfo?.unit || r.unit || ""; return { ...r, item_name: itemInfo?.name || "", - unit: itemInfo?.unit || resolve("unit", r.unit), + unit: resolve("item_unit", rawUnit) || rawUnit, + warehouse_code: whMap.get(r.warehouse_code) || r.warehouse_code || "", warehouse_name: whMap.get(r.warehouse_code) || r.warehouse_code || "", status: resolve("status", r.status), _isLow: r.safety_qty && Number(r.current_qty) < Number(r.safety_qty), @@ -613,7 +631,7 @@ export default function InventoryStatusPage() { {h.remark || h.reason || ""} - {h.writer || h.created_by || ""} + {userMap[h.writer] || userMap[h.created_by] || h.writer || h.created_by || ""} ))} diff --git a/frontend/app/(main)/COMPANY_16/logistics/outbound/page.tsx b/frontend/app/(main)/COMPANY_16/logistics/outbound/page.tsx index c1ffbd40..f3bafd20 100644 --- a/frontend/app/(main)/COMPANY_16/logistics/outbound/page.tsx +++ b/frontend/app/(main)/COMPANY_16/logistics/outbound/page.tsx @@ -311,6 +311,34 @@ export default function OutboundPage() { const [editMode, setEditMode] = useState(false); const [editItemIds, setEditItemIds] = useState([]); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); + useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); + }, []); + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 소스 데이터 const [sourceKeyword, setSourceKeyword] = useState(""); const [sourceLoading, setSourceLoading] = useState(false); @@ -1337,6 +1365,7 @@ export default function OutboundPage() { data={pagedItems} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1772,10 +1801,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1826,8 +1857,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx index 5b730674..9351d06f 100644 --- a/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx @@ -353,17 +353,45 @@ export default function ReceivingPage() { // 구매관리 division 코드 (라벨 기준 조회) const [purchaseDivisionCode, setPurchaseDivisionCode] = useState(""); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); - // 구매관리 division 코드 로드 + // 구매관리 division 코드 + 재질/단위 카테고리 로드 useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; // division 카테고리에서 "구매관리" 라벨의 코드 조회 apiClient.get("/table-categories/item_info/division/values").then((res) => { const vals = res.data?.data || []; - const found = vals.find((v: any) => (v.value_label || v.label) === "구매관리"); + const found = vals.find((v: any) => (v.valueLabel || v.value_label || v.label) === "구매관리"); if (found) setPurchaseDivisionCode(found.value_code || found.code); }).catch(() => {}); + // 재질, 단위 카테고리 + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); }, []); + // 카테고리 코드→라벨 변환 + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 목록 조회 const fetchList = useCallback(async () => { setLoading(true); @@ -1287,6 +1315,7 @@ export default function ReceivingPage() { data={items} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1742,10 +1771,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1798,8 +1829,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_29/logistics/inventory/page.tsx b/frontend/app/(main)/COMPANY_29/logistics/inventory/page.tsx index 7d2a96c6..87909e6f 100644 --- a/frontend/app/(main)/COMPANY_29/logistics/inventory/page.tsx +++ b/frontend/app/(main)/COMPANY_29/logistics/inventory/page.tsx @@ -140,31 +140,47 @@ export default function InventoryStatusPage() { Record >({}); - // 카테고리 로드 + // 사용자 맵 (writer → 이름) + const [userMap, setUserMap] = useState>({}); + + // 카테고리 + 사용자 로드 useEffect(() => { const load = async () => { const optMap: Record = {}; const flatten = (vals: any[]): { code: string; label: string }[] => { const result: { code: string; label: string }[] = []; for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); if (v.children?.length) result.push(...flatten(v.children)); } return result; }; - for (const col of ["status", "unit"]) { + // inventory_stock 카테고리 + for (const col of ["status"]) { try { - const res = await apiClient.get( - `/table-categories/${STOCK_TABLE}/${col}/values` - ); + const res = await apiClient.get(`/table-categories/${STOCK_TABLE}/${col}/values`); if (res.data?.success) optMap[col] = flatten(res.data.data || []); - } catch { - /* skip */ - } + } catch { /* skip */ } } + // item_info 단위 카테고리 + try { + const res = await apiClient.get("/table-categories/item_info/unit/values"); + if (res.data?.success) optMap["item_unit"] = flatten(res.data.data || []); + } catch { /* skip */ } setCategoryOptions(optMap); }; load(); + // 사용자 목록 로드 + apiClient.get("/admin/users").then((res) => { + const users = res.data?.data || res.data || []; + const map: Record = {}; + for (const u of users) { + const id = u.user_id || u.id; + const name = u.user_name || u.name || id; + if (id) map[id] = name; + } + setUserMap(map); + }).catch(() => {}); }, []); // 재고 목록 조회 @@ -193,10 +209,12 @@ export default function InventoryStatusPage() { }; const data = raw.map((r: any) => { const itemInfo = itemMap.get(r.item_code) as any; + const rawUnit = itemInfo?.unit || r.unit || ""; return { ...r, item_name: itemInfo?.name || "", - unit: itemInfo?.unit || resolve("unit", r.unit), + unit: resolve("item_unit", rawUnit) || rawUnit, + warehouse_code: whMap.get(r.warehouse_code) || r.warehouse_code || "", warehouse_name: whMap.get(r.warehouse_code) || r.warehouse_code || "", status: resolve("status", r.status), _isLow: r.safety_qty && Number(r.current_qty) < Number(r.safety_qty), @@ -613,7 +631,7 @@ export default function InventoryStatusPage() { {h.remark || h.reason || ""} - {h.writer || h.created_by || ""} + {userMap[h.writer] || userMap[h.created_by] || h.writer || h.created_by || ""} ))} diff --git a/frontend/app/(main)/COMPANY_29/logistics/outbound/page.tsx b/frontend/app/(main)/COMPANY_29/logistics/outbound/page.tsx index c1ffbd40..f3bafd20 100644 --- a/frontend/app/(main)/COMPANY_29/logistics/outbound/page.tsx +++ b/frontend/app/(main)/COMPANY_29/logistics/outbound/page.tsx @@ -311,6 +311,34 @@ export default function OutboundPage() { const [editMode, setEditMode] = useState(false); const [editItemIds, setEditItemIds] = useState([]); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); + useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); + }, []); + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 소스 데이터 const [sourceKeyword, setSourceKeyword] = useState(""); const [sourceLoading, setSourceLoading] = useState(false); @@ -1337,6 +1365,7 @@ export default function OutboundPage() { data={pagedItems} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1772,10 +1801,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1826,8 +1857,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx index 5b730674..9351d06f 100644 --- a/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx @@ -353,17 +353,45 @@ export default function ReceivingPage() { // 구매관리 division 코드 (라벨 기준 조회) const [purchaseDivisionCode, setPurchaseDivisionCode] = useState(""); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); - // 구매관리 division 코드 로드 + // 구매관리 division 코드 + 재질/단위 카테고리 로드 useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; // division 카테고리에서 "구매관리" 라벨의 코드 조회 apiClient.get("/table-categories/item_info/division/values").then((res) => { const vals = res.data?.data || []; - const found = vals.find((v: any) => (v.value_label || v.label) === "구매관리"); + const found = vals.find((v: any) => (v.valueLabel || v.value_label || v.label) === "구매관리"); if (found) setPurchaseDivisionCode(found.value_code || found.code); }).catch(() => {}); + // 재질, 단위 카테고리 + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); }, []); + // 카테고리 코드→라벨 변환 + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 목록 조회 const fetchList = useCallback(async () => { setLoading(true); @@ -1287,6 +1315,7 @@ export default function ReceivingPage() { data={items} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1742,10 +1771,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1798,8 +1829,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_30/logistics/inventory/page.tsx b/frontend/app/(main)/COMPANY_30/logistics/inventory/page.tsx index 7d2a96c6..87909e6f 100644 --- a/frontend/app/(main)/COMPANY_30/logistics/inventory/page.tsx +++ b/frontend/app/(main)/COMPANY_30/logistics/inventory/page.tsx @@ -140,31 +140,47 @@ export default function InventoryStatusPage() { Record >({}); - // 카테고리 로드 + // 사용자 맵 (writer → 이름) + const [userMap, setUserMap] = useState>({}); + + // 카테고리 + 사용자 로드 useEffect(() => { const load = async () => { const optMap: Record = {}; const flatten = (vals: any[]): { code: string; label: string }[] => { const result: { code: string; label: string }[] = []; for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); if (v.children?.length) result.push(...flatten(v.children)); } return result; }; - for (const col of ["status", "unit"]) { + // inventory_stock 카테고리 + for (const col of ["status"]) { try { - const res = await apiClient.get( - `/table-categories/${STOCK_TABLE}/${col}/values` - ); + const res = await apiClient.get(`/table-categories/${STOCK_TABLE}/${col}/values`); if (res.data?.success) optMap[col] = flatten(res.data.data || []); - } catch { - /* skip */ - } + } catch { /* skip */ } } + // item_info 단위 카테고리 + try { + const res = await apiClient.get("/table-categories/item_info/unit/values"); + if (res.data?.success) optMap["item_unit"] = flatten(res.data.data || []); + } catch { /* skip */ } setCategoryOptions(optMap); }; load(); + // 사용자 목록 로드 + apiClient.get("/admin/users").then((res) => { + const users = res.data?.data || res.data || []; + const map: Record = {}; + for (const u of users) { + const id = u.user_id || u.id; + const name = u.user_name || u.name || id; + if (id) map[id] = name; + } + setUserMap(map); + }).catch(() => {}); }, []); // 재고 목록 조회 @@ -193,10 +209,12 @@ export default function InventoryStatusPage() { }; const data = raw.map((r: any) => { const itemInfo = itemMap.get(r.item_code) as any; + const rawUnit = itemInfo?.unit || r.unit || ""; return { ...r, item_name: itemInfo?.name || "", - unit: itemInfo?.unit || resolve("unit", r.unit), + unit: resolve("item_unit", rawUnit) || rawUnit, + warehouse_code: whMap.get(r.warehouse_code) || r.warehouse_code || "", warehouse_name: whMap.get(r.warehouse_code) || r.warehouse_code || "", status: resolve("status", r.status), _isLow: r.safety_qty && Number(r.current_qty) < Number(r.safety_qty), @@ -613,7 +631,7 @@ export default function InventoryStatusPage() { {h.remark || h.reason || ""} - {h.writer || h.created_by || ""} + {userMap[h.writer] || userMap[h.created_by] || h.writer || h.created_by || ""} ))} diff --git a/frontend/app/(main)/COMPANY_30/logistics/outbound/page.tsx b/frontend/app/(main)/COMPANY_30/logistics/outbound/page.tsx index c1ffbd40..f3bafd20 100644 --- a/frontend/app/(main)/COMPANY_30/logistics/outbound/page.tsx +++ b/frontend/app/(main)/COMPANY_30/logistics/outbound/page.tsx @@ -311,6 +311,34 @@ export default function OutboundPage() { const [editMode, setEditMode] = useState(false); const [editItemIds, setEditItemIds] = useState([]); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); + useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); + }, []); + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 소스 데이터 const [sourceKeyword, setSourceKeyword] = useState(""); const [sourceLoading, setSourceLoading] = useState(false); @@ -1337,6 +1365,7 @@ export default function OutboundPage() { data={pagedItems} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1772,10 +1801,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1826,8 +1857,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx index 5b730674..9351d06f 100644 --- a/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx @@ -353,17 +353,45 @@ export default function ReceivingPage() { // 구매관리 division 코드 (라벨 기준 조회) const [purchaseDivisionCode, setPurchaseDivisionCode] = useState(""); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); - // 구매관리 division 코드 로드 + // 구매관리 division 코드 + 재질/단위 카테고리 로드 useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; // division 카테고리에서 "구매관리" 라벨의 코드 조회 apiClient.get("/table-categories/item_info/division/values").then((res) => { const vals = res.data?.data || []; - const found = vals.find((v: any) => (v.value_label || v.label) === "구매관리"); + const found = vals.find((v: any) => (v.valueLabel || v.value_label || v.label) === "구매관리"); if (found) setPurchaseDivisionCode(found.value_code || found.code); }).catch(() => {}); + // 재질, 단위 카테고리 + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); }, []); + // 카테고리 코드→라벨 변환 + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 목록 조회 const fetchList = useCallback(async () => { setLoading(true); @@ -1287,6 +1315,7 @@ export default function ReceivingPage() { data={items} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1742,10 +1771,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1798,8 +1829,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_7/logistics/inventory/page.tsx b/frontend/app/(main)/COMPANY_7/logistics/inventory/page.tsx index 7d2a96c6..87909e6f 100644 --- a/frontend/app/(main)/COMPANY_7/logistics/inventory/page.tsx +++ b/frontend/app/(main)/COMPANY_7/logistics/inventory/page.tsx @@ -140,31 +140,47 @@ export default function InventoryStatusPage() { Record >({}); - // 카테고리 로드 + // 사용자 맵 (writer → 이름) + const [userMap, setUserMap] = useState>({}); + + // 카테고리 + 사용자 로드 useEffect(() => { const load = async () => { const optMap: Record = {}; const flatten = (vals: any[]): { code: string; label: string }[] => { const result: { code: string; label: string }[] = []; for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); if (v.children?.length) result.push(...flatten(v.children)); } return result; }; - for (const col of ["status", "unit"]) { + // inventory_stock 카테고리 + for (const col of ["status"]) { try { - const res = await apiClient.get( - `/table-categories/${STOCK_TABLE}/${col}/values` - ); + const res = await apiClient.get(`/table-categories/${STOCK_TABLE}/${col}/values`); if (res.data?.success) optMap[col] = flatten(res.data.data || []); - } catch { - /* skip */ - } + } catch { /* skip */ } } + // item_info 단위 카테고리 + try { + const res = await apiClient.get("/table-categories/item_info/unit/values"); + if (res.data?.success) optMap["item_unit"] = flatten(res.data.data || []); + } catch { /* skip */ } setCategoryOptions(optMap); }; load(); + // 사용자 목록 로드 + apiClient.get("/admin/users").then((res) => { + const users = res.data?.data || res.data || []; + const map: Record = {}; + for (const u of users) { + const id = u.user_id || u.id; + const name = u.user_name || u.name || id; + if (id) map[id] = name; + } + setUserMap(map); + }).catch(() => {}); }, []); // 재고 목록 조회 @@ -193,10 +209,12 @@ export default function InventoryStatusPage() { }; const data = raw.map((r: any) => { const itemInfo = itemMap.get(r.item_code) as any; + const rawUnit = itemInfo?.unit || r.unit || ""; return { ...r, item_name: itemInfo?.name || "", - unit: itemInfo?.unit || resolve("unit", r.unit), + unit: resolve("item_unit", rawUnit) || rawUnit, + warehouse_code: whMap.get(r.warehouse_code) || r.warehouse_code || "", warehouse_name: whMap.get(r.warehouse_code) || r.warehouse_code || "", status: resolve("status", r.status), _isLow: r.safety_qty && Number(r.current_qty) < Number(r.safety_qty), @@ -613,7 +631,7 @@ export default function InventoryStatusPage() { {h.remark || h.reason || ""} - {h.writer || h.created_by || ""} + {userMap[h.writer] || userMap[h.created_by] || h.writer || h.created_by || ""} ))} diff --git a/frontend/app/(main)/COMPANY_7/logistics/outbound/page.tsx b/frontend/app/(main)/COMPANY_7/logistics/outbound/page.tsx index c1ffbd40..f3bafd20 100644 --- a/frontend/app/(main)/COMPANY_7/logistics/outbound/page.tsx +++ b/frontend/app/(main)/COMPANY_7/logistics/outbound/page.tsx @@ -311,6 +311,34 @@ export default function OutboundPage() { const [editMode, setEditMode] = useState(false); const [editItemIds, setEditItemIds] = useState([]); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); + useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); + }, []); + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 소스 데이터 const [sourceKeyword, setSourceKeyword] = useState(""); const [sourceLoading, setSourceLoading] = useState(false); @@ -1337,6 +1365,7 @@ export default function OutboundPage() { data={pagedItems} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1772,10 +1801,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1826,8 +1857,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx index 5b730674..9351d06f 100644 --- a/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx @@ -353,17 +353,45 @@ export default function ReceivingPage() { // 구매관리 division 코드 (라벨 기준 조회) const [purchaseDivisionCode, setPurchaseDivisionCode] = useState(""); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); - // 구매관리 division 코드 로드 + // 구매관리 division 코드 + 재질/단위 카테고리 로드 useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; // division 카테고리에서 "구매관리" 라벨의 코드 조회 apiClient.get("/table-categories/item_info/division/values").then((res) => { const vals = res.data?.data || []; - const found = vals.find((v: any) => (v.value_label || v.label) === "구매관리"); + const found = vals.find((v: any) => (v.valueLabel || v.value_label || v.label) === "구매관리"); if (found) setPurchaseDivisionCode(found.value_code || found.code); }).catch(() => {}); + // 재질, 단위 카테고리 + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); }, []); + // 카테고리 코드→라벨 변환 + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 목록 조회 const fetchList = useCallback(async () => { setLoading(true); @@ -1287,6 +1315,7 @@ export default function ReceivingPage() { data={items} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1742,10 +1771,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1798,8 +1829,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_8/logistics/inventory/page.tsx b/frontend/app/(main)/COMPANY_8/logistics/inventory/page.tsx index 7d2a96c6..87909e6f 100644 --- a/frontend/app/(main)/COMPANY_8/logistics/inventory/page.tsx +++ b/frontend/app/(main)/COMPANY_8/logistics/inventory/page.tsx @@ -140,31 +140,47 @@ export default function InventoryStatusPage() { Record >({}); - // 카테고리 로드 + // 사용자 맵 (writer → 이름) + const [userMap, setUserMap] = useState>({}); + + // 카테고리 + 사용자 로드 useEffect(() => { const load = async () => { const optMap: Record = {}; const flatten = (vals: any[]): { code: string; label: string }[] => { const result: { code: string; label: string }[] = []; for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); if (v.children?.length) result.push(...flatten(v.children)); } return result; }; - for (const col of ["status", "unit"]) { + // inventory_stock 카테고리 + for (const col of ["status"]) { try { - const res = await apiClient.get( - `/table-categories/${STOCK_TABLE}/${col}/values` - ); + const res = await apiClient.get(`/table-categories/${STOCK_TABLE}/${col}/values`); if (res.data?.success) optMap[col] = flatten(res.data.data || []); - } catch { - /* skip */ - } + } catch { /* skip */ } } + // item_info 단위 카테고리 + try { + const res = await apiClient.get("/table-categories/item_info/unit/values"); + if (res.data?.success) optMap["item_unit"] = flatten(res.data.data || []); + } catch { /* skip */ } setCategoryOptions(optMap); }; load(); + // 사용자 목록 로드 + apiClient.get("/admin/users").then((res) => { + const users = res.data?.data || res.data || []; + const map: Record = {}; + for (const u of users) { + const id = u.user_id || u.id; + const name = u.user_name || u.name || id; + if (id) map[id] = name; + } + setUserMap(map); + }).catch(() => {}); }, []); // 재고 목록 조회 @@ -193,10 +209,12 @@ export default function InventoryStatusPage() { }; const data = raw.map((r: any) => { const itemInfo = itemMap.get(r.item_code) as any; + const rawUnit = itemInfo?.unit || r.unit || ""; return { ...r, item_name: itemInfo?.name || "", - unit: itemInfo?.unit || resolve("unit", r.unit), + unit: resolve("item_unit", rawUnit) || rawUnit, + warehouse_code: whMap.get(r.warehouse_code) || r.warehouse_code || "", warehouse_name: whMap.get(r.warehouse_code) || r.warehouse_code || "", status: resolve("status", r.status), _isLow: r.safety_qty && Number(r.current_qty) < Number(r.safety_qty), @@ -613,7 +631,7 @@ export default function InventoryStatusPage() { {h.remark || h.reason || ""} - {h.writer || h.created_by || ""} + {userMap[h.writer] || userMap[h.created_by] || h.writer || h.created_by || ""} ))} diff --git a/frontend/app/(main)/COMPANY_8/logistics/outbound/page.tsx b/frontend/app/(main)/COMPANY_8/logistics/outbound/page.tsx index c1ffbd40..f3bafd20 100644 --- a/frontend/app/(main)/COMPANY_8/logistics/outbound/page.tsx +++ b/frontend/app/(main)/COMPANY_8/logistics/outbound/page.tsx @@ -311,6 +311,34 @@ export default function OutboundPage() { const [editMode, setEditMode] = useState(false); const [editItemIds, setEditItemIds] = useState([]); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); + useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); + }, []); + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 소스 데이터 const [sourceKeyword, setSourceKeyword] = useState(""); const [sourceLoading, setSourceLoading] = useState(false); @@ -1337,6 +1365,7 @@ export default function OutboundPage() { data={pagedItems} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1772,10 +1801,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1826,8 +1857,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx index 5b730674..9351d06f 100644 --- a/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx @@ -353,17 +353,45 @@ export default function ReceivingPage() { // 구매관리 division 코드 (라벨 기준 조회) const [purchaseDivisionCode, setPurchaseDivisionCode] = useState(""); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); - // 구매관리 division 코드 로드 + // 구매관리 division 코드 + 재질/단위 카테고리 로드 useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; // division 카테고리에서 "구매관리" 라벨의 코드 조회 apiClient.get("/table-categories/item_info/division/values").then((res) => { const vals = res.data?.data || []; - const found = vals.find((v: any) => (v.value_label || v.label) === "구매관리"); + const found = vals.find((v: any) => (v.valueLabel || v.value_label || v.label) === "구매관리"); if (found) setPurchaseDivisionCode(found.value_code || found.code); }).catch(() => {}); + // 재질, 단위 카테고리 + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); }, []); + // 카테고리 코드→라벨 변환 + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 목록 조회 const fetchList = useCallback(async () => { setLoading(true); @@ -1287,6 +1315,7 @@ export default function ReceivingPage() { data={items} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1742,10 +1771,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1798,8 +1829,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_9/logistics/inventory/page.tsx b/frontend/app/(main)/COMPANY_9/logistics/inventory/page.tsx index 7d2a96c6..87909e6f 100644 --- a/frontend/app/(main)/COMPANY_9/logistics/inventory/page.tsx +++ b/frontend/app/(main)/COMPANY_9/logistics/inventory/page.tsx @@ -140,31 +140,47 @@ export default function InventoryStatusPage() { Record >({}); - // 카테고리 로드 + // 사용자 맵 (writer → 이름) + const [userMap, setUserMap] = useState>({}); + + // 카테고리 + 사용자 로드 useEffect(() => { const load = async () => { const optMap: Record = {}; const flatten = (vals: any[]): { code: string; label: string }[] => { const result: { code: string; label: string }[] = []; for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); if (v.children?.length) result.push(...flatten(v.children)); } return result; }; - for (const col of ["status", "unit"]) { + // inventory_stock 카테고리 + for (const col of ["status"]) { try { - const res = await apiClient.get( - `/table-categories/${STOCK_TABLE}/${col}/values` - ); + const res = await apiClient.get(`/table-categories/${STOCK_TABLE}/${col}/values`); if (res.data?.success) optMap[col] = flatten(res.data.data || []); - } catch { - /* skip */ - } + } catch { /* skip */ } } + // item_info 단위 카테고리 + try { + const res = await apiClient.get("/table-categories/item_info/unit/values"); + if (res.data?.success) optMap["item_unit"] = flatten(res.data.data || []); + } catch { /* skip */ } setCategoryOptions(optMap); }; load(); + // 사용자 목록 로드 + apiClient.get("/admin/users").then((res) => { + const users = res.data?.data || res.data || []; + const map: Record = {}; + for (const u of users) { + const id = u.user_id || u.id; + const name = u.user_name || u.name || id; + if (id) map[id] = name; + } + setUserMap(map); + }).catch(() => {}); }, []); // 재고 목록 조회 @@ -193,10 +209,12 @@ export default function InventoryStatusPage() { }; const data = raw.map((r: any) => { const itemInfo = itemMap.get(r.item_code) as any; + const rawUnit = itemInfo?.unit || r.unit || ""; return { ...r, item_name: itemInfo?.name || "", - unit: itemInfo?.unit || resolve("unit", r.unit), + unit: resolve("item_unit", rawUnit) || rawUnit, + warehouse_code: whMap.get(r.warehouse_code) || r.warehouse_code || "", warehouse_name: whMap.get(r.warehouse_code) || r.warehouse_code || "", status: resolve("status", r.status), _isLow: r.safety_qty && Number(r.current_qty) < Number(r.safety_qty), @@ -613,7 +631,7 @@ export default function InventoryStatusPage() { {h.remark || h.reason || ""} - {h.writer || h.created_by || ""} + {userMap[h.writer] || userMap[h.created_by] || h.writer || h.created_by || ""} ))} diff --git a/frontend/app/(main)/COMPANY_9/logistics/outbound/page.tsx b/frontend/app/(main)/COMPANY_9/logistics/outbound/page.tsx index c1ffbd40..f3bafd20 100644 --- a/frontend/app/(main)/COMPANY_9/logistics/outbound/page.tsx +++ b/frontend/app/(main)/COMPANY_9/logistics/outbound/page.tsx @@ -311,6 +311,34 @@ export default function OutboundPage() { const [editMode, setEditMode] = useState(false); const [editItemIds, setEditItemIds] = useState([]); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); + useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); + }, []); + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 소스 데이터 const [sourceKeyword, setSourceKeyword] = useState(""); const [sourceLoading, setSourceLoading] = useState(false); @@ -1337,6 +1365,7 @@ export default function OutboundPage() { data={pagedItems} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1772,10 +1801,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1826,8 +1857,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()} diff --git a/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx index 5b730674..9351d06f 100644 --- a/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx @@ -353,17 +353,45 @@ export default function ReceivingPage() { // 구매관리 division 코드 (라벨 기준 조회) const [purchaseDivisionCode, setPurchaseDivisionCode] = useState(""); + // 카테고리 코드→라벨 매핑 (재질, 단위) + const [catMap, setCatMap] = useState>>({}); - // 구매관리 division 코드 로드 + // 구매관리 division 코드 + 재질/단위 카테고리 로드 useEffect(() => { + const flatten = (arr: any[]): { code: string; label: string }[] => { + const result: { code: string; label: string }[] = []; + for (const v of arr) { + result.push({ code: v.valueCode || v.value_code || v.code, label: v.valueLabel || v.value_label || v.label }); + if (v.children?.length) result.push(...flatten(v.children)); + } + return result; + }; // division 카테고리에서 "구매관리" 라벨의 코드 조회 apiClient.get("/table-categories/item_info/division/values").then((res) => { const vals = res.data?.data || []; - const found = vals.find((v: any) => (v.value_label || v.label) === "구매관리"); + const found = vals.find((v: any) => (v.valueLabel || v.value_label || v.label) === "구매관리"); if (found) setPurchaseDivisionCode(found.value_code || found.code); }).catch(() => {}); + // 재질, 단위 카테고리 + const map: Record> = {}; + Promise.all( + ["material", "unit"].map(async (col) => { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + const items = flatten(res.data?.data || []); + map[col] = {}; + for (const item of items) map[col][item.code] = item.label; + } catch { /* skip */ } + }) + ).then(() => setCatMap(map)); }, []); + // 카테고리 코드→라벨 변환 + const resolveCat = useCallback((col: string, code: string) => { + if (!code) return ""; + return catMap[col]?.[code] || code; + }, [catMap]); + // 목록 조회 const fetchList = useCallback(async () => { setLoading(true); @@ -1287,6 +1315,7 @@ export default function ReceivingPage() { data={items} onAdd={addItem} selectedKeys={selectedItems.map((s) => s.key)} + resolveCat={resolveCat} /> )} @@ -1742,10 +1771,12 @@ function SourceItemTable({ data, onAdd, selectedKeys, + resolveCat, }: { data: ItemSource[]; onAdd: (item: ItemSource) => void; selectedKeys: string[]; + resolveCat: (col: string, code: string) => string; }) { if (data.length === 0) { return ( @@ -1798,8 +1829,8 @@ function SourceItemTable({ {item.spec || "-"} - {item.material || "-"} - {item.unit || "-"} + {resolveCat("material", item.material) || "-"} + {resolveCat("unit", item.unit) || "-"} {Number(item.standard_price).toLocaleString()}