diff --git a/backend-node/src/controllers/fileController.ts b/backend-node/src/controllers/fileController.ts index ea9bd7a9..03048d6d 100644 --- a/backend-node/src/controllers/fileController.ts +++ b/backend-node/src/controllers/fileController.ts @@ -924,12 +924,26 @@ export const previewFile = async ( ); res.setHeader("Access-Control-Allow-Credentials", "true"); - // 캐시 헤더 설정 + // Cross-Origin-Resource-Policy: cross-origin 설정 + // helmet 기본값(same-origin)을 오버라이드하여 v1.vexplor.com에서 api.vexplor.com 이미지 로드 허용 + res.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); + + // 파일 크기 및 캐시 헤더 설정 + const stat = fs.statSync(finalPath); + res.setHeader("Content-Length", stat.size); res.setHeader("Cache-Control", "public, max-age=3600"); res.setHeader("Content-Type", mimeType); // 파일 스트림으로 전송 const fileStream = fs.createReadStream(finalPath); + fileStream.on("error", (err) => { + console.error("파일 스트림 오류:", err); + if (!res.headersSent) { + res.status(500).json({ success: false, message: "파일 읽기 오류" }); + } else { + res.end(); + } + }); fileStream.pipe(res); } catch (error) { console.error("파일 미리보기 오류:", error); @@ -1031,9 +1045,20 @@ export const downloadFile = async ( `attachment; filename="${encodeURIComponent(fileRecord.real_file_name!)}"` ); res.setHeader("Content-Type", "application/octet-stream"); + res.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); + const stat = fs.statSync(filePath); + res.setHeader("Content-Length", stat.size); // 파일 스트림 전송 const fileStream = fs.createReadStream(filePath); + fileStream.on("error", (err) => { + console.error("파일 스트림 오류:", err); + if (!res.headersSent) { + res.status(500).json({ success: false, message: "파일 읽기 오류" }); + } else { + res.end(); + } + }); fileStream.pipe(res); } catch (error) { console.error("파일 다운로드 오류:", error); @@ -1218,10 +1243,21 @@ export const getFileByToken = async (req: Request, res: Response) => { "Content-Disposition", `inline; filename="${encodeURIComponent(fileRecord.real_file_name!)}"` ); + res.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); + const stat = fs.statSync(filePath); + res.setHeader("Content-Length", stat.size); res.setHeader("Cache-Control", "public, max-age=300"); // 5분 캐시 // 파일 스트림 전송 const fileStream = fs.createReadStream(filePath); + fileStream.on("error", (err) => { + console.error("파일 스트림 오류:", err); + if (!res.headersSent) { + res.status(500).json({ success: false, message: "파일 읽기 오류" }); + } else { + res.end(); + } + }); fileStream.pipe(res); } catch (error) { console.error("❌ 토큰 파일 접근 오류:", error); diff --git a/backend-node/src/controllers/materialStatusController.ts b/backend-node/src/controllers/materialStatusController.ts index 1c76246a..b5427659 100644 --- a/backend-node/src/controllers/materialStatusController.ts +++ b/backend-node/src/controllers/materialStatusController.ts @@ -226,11 +226,12 @@ export async function getMaterialStatus( return res.json({ success: true, data: [] }); } - // 4) 재고 조회 (창고/위치별) - const stockPlaceholders = materialIds + // 4) 재고 조회 (창고/위치별) — inventory_stock.item_code는 item_number 기준 + const materialCodes = materialIds.map((id) => materialMap[id].materialCode); + const stockPlaceholders = materialCodes .map((_, i) => `$${i + 1}`) .join(","); - const stockParams: any[] = [...materialIds]; + const stockParams: any[] = [...materialCodes]; let stockParamIdx = materialIds.length + 1; const stockConditions: string[] = [ diff --git a/backend-node/src/controllers/moldController.ts b/backend-node/src/controllers/moldController.ts index cf01f362..25e49186 100644 --- a/backend-node/src/controllers/moldController.ts +++ b/backend-node/src/controllers/moldController.ts @@ -94,7 +94,7 @@ export async function createMold(req: AuthenticatedRequest, res: Response): Prom mold_code, mold_name, mold_type, category, manufacturer, manufacturing_number, manufacturing_date, cavity_count, shot_count, mold_quantity, base_input_qty, operation_status, - remarks, image_path, memo, + remarks, image_path, memo, warranty_shot_count, } = req.body; if (!mold_code || !mold_name) { @@ -107,15 +107,16 @@ export async function createMold(req: AuthenticatedRequest, res: Response): Prom id, company_code, mold_code, mold_name, mold_type, category, manufacturer, manufacturing_number, manufacturing_date, cavity_count, shot_count, mold_quantity, base_input_qty, - operation_status, remarks, image_path, memo, writer, created_date - ) VALUES (gen_random_uuid()::text,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,NOW()) + operation_status, remarks, image_path, memo, warranty_shot_count, writer, created_date + ) VALUES (gen_random_uuid()::text,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,NOW()) RETURNING * `; const params = [ companyCode, mold_code, mold_name, mold_type || null, category || null, manufacturer || null, manufacturing_number || null, manufacturing_date || null, cavity_count || 0, shot_count || 0, mold_quantity || 1, base_input_qty || 0, - operation_status || "ACTIVE", remarks || null, image_path || null, memo || null, userId, + operation_status || "ACTIVE", remarks || null, image_path || null, memo || null, + warranty_shot_count || 0, userId, ]; const result = await query(sql, params); @@ -139,7 +140,7 @@ export async function updateMold(req: AuthenticatedRequest, res: Response): Prom mold_name, mold_type, category, manufacturer, manufacturing_number, manufacturing_date, cavity_count, shot_count, mold_quantity, base_input_qty, operation_status, - remarks, image_path, memo, + remarks, image_path, memo, warranty_shot_count, } = req.body; const sql = ` @@ -153,8 +154,9 @@ export async function updateMold(req: AuthenticatedRequest, res: Response): Prom base_input_qty = COALESCE($10, base_input_qty), operation_status = COALESCE($11, operation_status), remarks = $12, image_path = $13, memo = $14, + warranty_shot_count = $15, updated_date = NOW() - WHERE mold_code = $15 AND company_code = $16 + WHERE mold_code = $16 AND company_code = $17 RETURNING * `; const params = [ @@ -162,7 +164,7 @@ export async function updateMold(req: AuthenticatedRequest, res: Response): Prom manufacturing_number, manufacturing_date, cavity_count, shot_count, mold_quantity, base_input_qty, operation_status, remarks, image_path, memo, - moldCode, companyCode, + warranty_shot_count || 0, moldCode, companyCode, ]; const result = await query(sql, params); diff --git a/frontend/app/(main)/COMPANY_10/logistics/info/page.tsx b/frontend/app/(main)/COMPANY_10/logistics/info/page.tsx index 543dd3dd..35764744 100644 --- a/frontend/app/(main)/COMPANY_10/logistics/info/page.tsx +++ b/frontend/app/(main)/COMPANY_10/logistics/info/page.tsx @@ -230,11 +230,10 @@ function flattenCategories(items: any[]): { value: string; label: string }[] { const result: { value: string; label: string }[] = []; function walk(arr: any[]) { for (const item of arr) { - if (item.value || item.name) { - result.push({ - value: item.value || item.name, - label: item.label || item.name || item.value, - }); + const val = item.valueCode || item.value || item.name; + const lbl = item.valueLabel || item.label || item.name || val; + if (val) { + result.push({ value: val, label: lbl }); } if (item.children?.length) walk(item.children); } diff --git a/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx index 85cdc23c..5b730674 100644 --- a/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_10/logistics/receiving/page.tsx @@ -777,12 +777,16 @@ export default function ReceivingPage() { return; } + if (!modalWarehouse) { + toast.error("창고를 선택해주세요."); + return; + } setSaving(true); try { const res = await createReceiving({ inbound_number: modalInboundNo, inbound_date: modalInboundDate, - warehouse_code: modalWarehouse || undefined, + warehouse_code: modalWarehouse, location_code: modalLocation || undefined, inspector: modalInspector || undefined, manager: modalManager || undefined, diff --git a/frontend/app/(main)/COMPANY_10/quality/item-inspection/page.tsx b/frontend/app/(main)/COMPANY_10/quality/item-inspection/page.tsx index 2c0e1338..1059223c 100644 --- a/frontend/app/(main)/COMPANY_10/quality/item-inspection/page.tsx +++ b/frontend/app/(main)/COMPANY_10/quality/item-inspection/page.tsx @@ -72,6 +72,7 @@ export default function ItemInspectionInfoPage() { const [itemOptions, setItemOptions] = useState<{ code: string; name: string; item_type: string; unit: string }[]>([]); const [inspOptions, setInspOptions] = useState<{ code: string; label: string; detail: string; method: string; types: string[] }[]>([]); const [inspTypeCatOptions, setInspTypeCatOptions] = useState<{ code: string; label: string }[]>([]); + const [inspMethodCatOptions, setInspMethodCatOptions] = useState<{ code: string; label: string }[]>([]); const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); /* 검사유형별 검사항목 rows */ @@ -118,6 +119,15 @@ export default function ItemInspectionInfoPage() { setInspTypeCatOptions(flatCats); } catch { /* skip */ } + // 검사방법 카테고리 값 로드 (코드→라벨 매핑용) + try { + const methodRes = await apiClient.get(`/table-categories/${INSPECTION_TABLE}/inspection_method/values`); + const flatMethods: { code: string; label: string }[] = []; + const flattenM = (arr: any[]) => { for (const v of arr) { flatMethods.push({ code: v.valueCode, label: v.valueLabel }); if (v.children?.length) flattenM(v.children); } }; + if (methodRes.data?.data?.length) flattenM(methodRes.data.data); + setInspMethodCatOptions(flatMethods); + } catch { /* skip */ } + const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; setUserOptions(users.map((u: any) => ({ code: u.user_id || u.id, @@ -221,11 +231,13 @@ export default function ItemInspectionInfoPage() { if (!typeKey) continue; typeFlags[typeKey] = true; if (!rowMap[typeKey]) rowMap[typeKey] = []; + const mCode = r.inspection_method || ""; + const mLabel = inspMethodCatOptions.find(o => o.code === mCode)?.label || mCode; rowMap[typeKey].push({ id: r.id, inspection_standard_id: r.inspection_standard_id || "", inspection_detail: r.inspection_item_name || r.inspection_standard || "", - inspection_method: r.inspection_method || "", + inspection_method: mLabel, apply_process: "", acceptance_criteria: r.pass_criteria || "", is_required: r.is_required === "true" || r.is_required === true, @@ -270,7 +282,9 @@ export default function ItemInspectionInfoPage() { if (r.id !== rowId) return r; if (field === "inspection_standard_id") { const opt = inspOptions.find(o => o.code === value); - return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: opt?.method || "" }; + const methodCode = opt?.method || ""; + const methodLabel = inspMethodCatOptions.find(o => o.code === methodCode)?.label || methodCode; + return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: methodLabel }; } return { ...r, [field]: value }; }), @@ -471,17 +485,36 @@ export default function ItemInspectionInfoPage() { {ts.visibleColumns.map((col) => renderCell(col.key))} - {isExpanded && group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( - + {isExpanded && ( + - {row.inspection_type} - {resolveInspLabel(row.inspection_standard_id)} - {row.inspection_item_name || "-"} - {row.inspection_method || "-"} - {row.pass_criteria || "-"} + + + + + 검사유형 + 검사기준 + 검사항목 + 검사방법 + 합격기준 + + + + {group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( + + {row.inspection_type} + {resolveInspLabel(row.inspection_standard_id)} + {row.inspection_item_name || "-"} + {row.inspection_method || "-"} + {row.pass_criteria || "-"} + + ))} + +
+
- ))} + )} ); })} diff --git a/frontend/app/(main)/COMPANY_16/logistics/info/page.tsx b/frontend/app/(main)/COMPANY_16/logistics/info/page.tsx index 543dd3dd..35764744 100644 --- a/frontend/app/(main)/COMPANY_16/logistics/info/page.tsx +++ b/frontend/app/(main)/COMPANY_16/logistics/info/page.tsx @@ -230,11 +230,10 @@ function flattenCategories(items: any[]): { value: string; label: string }[] { const result: { value: string; label: string }[] = []; function walk(arr: any[]) { for (const item of arr) { - if (item.value || item.name) { - result.push({ - value: item.value || item.name, - label: item.label || item.name || item.value, - }); + const val = item.valueCode || item.value || item.name; + const lbl = item.valueLabel || item.label || item.name || val; + if (val) { + result.push({ value: val, label: lbl }); } if (item.children?.length) walk(item.children); } diff --git a/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx index 85cdc23c..5b730674 100644 --- a/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_16/logistics/receiving/page.tsx @@ -777,12 +777,16 @@ export default function ReceivingPage() { return; } + if (!modalWarehouse) { + toast.error("창고를 선택해주세요."); + return; + } setSaving(true); try { const res = await createReceiving({ inbound_number: modalInboundNo, inbound_date: modalInboundDate, - warehouse_code: modalWarehouse || undefined, + warehouse_code: modalWarehouse, location_code: modalLocation || undefined, inspector: modalInspector || undefined, manager: modalManager || undefined, diff --git a/frontend/app/(main)/COMPANY_16/quality/item-inspection/page.tsx b/frontend/app/(main)/COMPANY_16/quality/item-inspection/page.tsx index 2c0e1338..1059223c 100644 --- a/frontend/app/(main)/COMPANY_16/quality/item-inspection/page.tsx +++ b/frontend/app/(main)/COMPANY_16/quality/item-inspection/page.tsx @@ -72,6 +72,7 @@ export default function ItemInspectionInfoPage() { const [itemOptions, setItemOptions] = useState<{ code: string; name: string; item_type: string; unit: string }[]>([]); const [inspOptions, setInspOptions] = useState<{ code: string; label: string; detail: string; method: string; types: string[] }[]>([]); const [inspTypeCatOptions, setInspTypeCatOptions] = useState<{ code: string; label: string }[]>([]); + const [inspMethodCatOptions, setInspMethodCatOptions] = useState<{ code: string; label: string }[]>([]); const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); /* 검사유형별 검사항목 rows */ @@ -118,6 +119,15 @@ export default function ItemInspectionInfoPage() { setInspTypeCatOptions(flatCats); } catch { /* skip */ } + // 검사방법 카테고리 값 로드 (코드→라벨 매핑용) + try { + const methodRes = await apiClient.get(`/table-categories/${INSPECTION_TABLE}/inspection_method/values`); + const flatMethods: { code: string; label: string }[] = []; + const flattenM = (arr: any[]) => { for (const v of arr) { flatMethods.push({ code: v.valueCode, label: v.valueLabel }); if (v.children?.length) flattenM(v.children); } }; + if (methodRes.data?.data?.length) flattenM(methodRes.data.data); + setInspMethodCatOptions(flatMethods); + } catch { /* skip */ } + const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; setUserOptions(users.map((u: any) => ({ code: u.user_id || u.id, @@ -221,11 +231,13 @@ export default function ItemInspectionInfoPage() { if (!typeKey) continue; typeFlags[typeKey] = true; if (!rowMap[typeKey]) rowMap[typeKey] = []; + const mCode = r.inspection_method || ""; + const mLabel = inspMethodCatOptions.find(o => o.code === mCode)?.label || mCode; rowMap[typeKey].push({ id: r.id, inspection_standard_id: r.inspection_standard_id || "", inspection_detail: r.inspection_item_name || r.inspection_standard || "", - inspection_method: r.inspection_method || "", + inspection_method: mLabel, apply_process: "", acceptance_criteria: r.pass_criteria || "", is_required: r.is_required === "true" || r.is_required === true, @@ -270,7 +282,9 @@ export default function ItemInspectionInfoPage() { if (r.id !== rowId) return r; if (field === "inspection_standard_id") { const opt = inspOptions.find(o => o.code === value); - return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: opt?.method || "" }; + const methodCode = opt?.method || ""; + const methodLabel = inspMethodCatOptions.find(o => o.code === methodCode)?.label || methodCode; + return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: methodLabel }; } return { ...r, [field]: value }; }), @@ -471,17 +485,36 @@ export default function ItemInspectionInfoPage() { {ts.visibleColumns.map((col) => renderCell(col.key))}
- {isExpanded && group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( - + {isExpanded && ( + - {row.inspection_type} - {resolveInspLabel(row.inspection_standard_id)} - {row.inspection_item_name || "-"} - {row.inspection_method || "-"} - {row.pass_criteria || "-"} + + + + + 검사유형 + 검사기준 + 검사항목 + 검사방법 + 합격기준 + + + + {group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( + + {row.inspection_type} + {resolveInspLabel(row.inspection_standard_id)} + {row.inspection_item_name || "-"} + {row.inspection_method || "-"} + {row.pass_criteria || "-"} + + ))} + +
+
- ))} + )} ); })} diff --git a/frontend/app/(main)/COMPANY_29/logistics/info/page.tsx b/frontend/app/(main)/COMPANY_29/logistics/info/page.tsx index 543dd3dd..35764744 100644 --- a/frontend/app/(main)/COMPANY_29/logistics/info/page.tsx +++ b/frontend/app/(main)/COMPANY_29/logistics/info/page.tsx @@ -230,11 +230,10 @@ function flattenCategories(items: any[]): { value: string; label: string }[] { const result: { value: string; label: string }[] = []; function walk(arr: any[]) { for (const item of arr) { - if (item.value || item.name) { - result.push({ - value: item.value || item.name, - label: item.label || item.name || item.value, - }); + const val = item.valueCode || item.value || item.name; + const lbl = item.valueLabel || item.label || item.name || val; + if (val) { + result.push({ value: val, label: lbl }); } if (item.children?.length) walk(item.children); } diff --git a/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx index 85cdc23c..5b730674 100644 --- a/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_29/logistics/receiving/page.tsx @@ -777,12 +777,16 @@ export default function ReceivingPage() { return; } + if (!modalWarehouse) { + toast.error("창고를 선택해주세요."); + return; + } setSaving(true); try { const res = await createReceiving({ inbound_number: modalInboundNo, inbound_date: modalInboundDate, - warehouse_code: modalWarehouse || undefined, + warehouse_code: modalWarehouse, location_code: modalLocation || undefined, inspector: modalInspector || undefined, manager: modalManager || undefined, diff --git a/frontend/app/(main)/COMPANY_29/quality/item-inspection/page.tsx b/frontend/app/(main)/COMPANY_29/quality/item-inspection/page.tsx index 2c0e1338..1059223c 100644 --- a/frontend/app/(main)/COMPANY_29/quality/item-inspection/page.tsx +++ b/frontend/app/(main)/COMPANY_29/quality/item-inspection/page.tsx @@ -72,6 +72,7 @@ export default function ItemInspectionInfoPage() { const [itemOptions, setItemOptions] = useState<{ code: string; name: string; item_type: string; unit: string }[]>([]); const [inspOptions, setInspOptions] = useState<{ code: string; label: string; detail: string; method: string; types: string[] }[]>([]); const [inspTypeCatOptions, setInspTypeCatOptions] = useState<{ code: string; label: string }[]>([]); + const [inspMethodCatOptions, setInspMethodCatOptions] = useState<{ code: string; label: string }[]>([]); const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); /* 검사유형별 검사항목 rows */ @@ -118,6 +119,15 @@ export default function ItemInspectionInfoPage() { setInspTypeCatOptions(flatCats); } catch { /* skip */ } + // 검사방법 카테고리 값 로드 (코드→라벨 매핑용) + try { + const methodRes = await apiClient.get(`/table-categories/${INSPECTION_TABLE}/inspection_method/values`); + const flatMethods: { code: string; label: string }[] = []; + const flattenM = (arr: any[]) => { for (const v of arr) { flatMethods.push({ code: v.valueCode, label: v.valueLabel }); if (v.children?.length) flattenM(v.children); } }; + if (methodRes.data?.data?.length) flattenM(methodRes.data.data); + setInspMethodCatOptions(flatMethods); + } catch { /* skip */ } + const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; setUserOptions(users.map((u: any) => ({ code: u.user_id || u.id, @@ -221,11 +231,13 @@ export default function ItemInspectionInfoPage() { if (!typeKey) continue; typeFlags[typeKey] = true; if (!rowMap[typeKey]) rowMap[typeKey] = []; + const mCode = r.inspection_method || ""; + const mLabel = inspMethodCatOptions.find(o => o.code === mCode)?.label || mCode; rowMap[typeKey].push({ id: r.id, inspection_standard_id: r.inspection_standard_id || "", inspection_detail: r.inspection_item_name || r.inspection_standard || "", - inspection_method: r.inspection_method || "", + inspection_method: mLabel, apply_process: "", acceptance_criteria: r.pass_criteria || "", is_required: r.is_required === "true" || r.is_required === true, @@ -270,7 +282,9 @@ export default function ItemInspectionInfoPage() { if (r.id !== rowId) return r; if (field === "inspection_standard_id") { const opt = inspOptions.find(o => o.code === value); - return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: opt?.method || "" }; + const methodCode = opt?.method || ""; + const methodLabel = inspMethodCatOptions.find(o => o.code === methodCode)?.label || methodCode; + return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: methodLabel }; } return { ...r, [field]: value }; }), @@ -471,17 +485,36 @@ export default function ItemInspectionInfoPage() { {ts.visibleColumns.map((col) => renderCell(col.key))}
- {isExpanded && group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( - + {isExpanded && ( + - {row.inspection_type} - {resolveInspLabel(row.inspection_standard_id)} - {row.inspection_item_name || "-"} - {row.inspection_method || "-"} - {row.pass_criteria || "-"} + + + + + 검사유형 + 검사기준 + 검사항목 + 검사방법 + 합격기준 + + + + {group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( + + {row.inspection_type} + {resolveInspLabel(row.inspection_standard_id)} + {row.inspection_item_name || "-"} + {row.inspection_method || "-"} + {row.pass_criteria || "-"} + + ))} + +
+
- ))} + )} ); })} diff --git a/frontend/app/(main)/COMPANY_30/logistics/info/page.tsx b/frontend/app/(main)/COMPANY_30/logistics/info/page.tsx index 543dd3dd..35764744 100644 --- a/frontend/app/(main)/COMPANY_30/logistics/info/page.tsx +++ b/frontend/app/(main)/COMPANY_30/logistics/info/page.tsx @@ -230,11 +230,10 @@ function flattenCategories(items: any[]): { value: string; label: string }[] { const result: { value: string; label: string }[] = []; function walk(arr: any[]) { for (const item of arr) { - if (item.value || item.name) { - result.push({ - value: item.value || item.name, - label: item.label || item.name || item.value, - }); + const val = item.valueCode || item.value || item.name; + const lbl = item.valueLabel || item.label || item.name || val; + if (val) { + result.push({ value: val, label: lbl }); } if (item.children?.length) walk(item.children); } diff --git a/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx index 85cdc23c..5b730674 100644 --- a/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_30/logistics/receiving/page.tsx @@ -777,12 +777,16 @@ export default function ReceivingPage() { return; } + if (!modalWarehouse) { + toast.error("창고를 선택해주세요."); + return; + } setSaving(true); try { const res = await createReceiving({ inbound_number: modalInboundNo, inbound_date: modalInboundDate, - warehouse_code: modalWarehouse || undefined, + warehouse_code: modalWarehouse, location_code: modalLocation || undefined, inspector: modalInspector || undefined, manager: modalManager || undefined, diff --git a/frontend/app/(main)/COMPANY_30/quality/item-inspection/page.tsx b/frontend/app/(main)/COMPANY_30/quality/item-inspection/page.tsx index 2c0e1338..1059223c 100644 --- a/frontend/app/(main)/COMPANY_30/quality/item-inspection/page.tsx +++ b/frontend/app/(main)/COMPANY_30/quality/item-inspection/page.tsx @@ -72,6 +72,7 @@ export default function ItemInspectionInfoPage() { const [itemOptions, setItemOptions] = useState<{ code: string; name: string; item_type: string; unit: string }[]>([]); const [inspOptions, setInspOptions] = useState<{ code: string; label: string; detail: string; method: string; types: string[] }[]>([]); const [inspTypeCatOptions, setInspTypeCatOptions] = useState<{ code: string; label: string }[]>([]); + const [inspMethodCatOptions, setInspMethodCatOptions] = useState<{ code: string; label: string }[]>([]); const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); /* 검사유형별 검사항목 rows */ @@ -118,6 +119,15 @@ export default function ItemInspectionInfoPage() { setInspTypeCatOptions(flatCats); } catch { /* skip */ } + // 검사방법 카테고리 값 로드 (코드→라벨 매핑용) + try { + const methodRes = await apiClient.get(`/table-categories/${INSPECTION_TABLE}/inspection_method/values`); + const flatMethods: { code: string; label: string }[] = []; + const flattenM = (arr: any[]) => { for (const v of arr) { flatMethods.push({ code: v.valueCode, label: v.valueLabel }); if (v.children?.length) flattenM(v.children); } }; + if (methodRes.data?.data?.length) flattenM(methodRes.data.data); + setInspMethodCatOptions(flatMethods); + } catch { /* skip */ } + const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; setUserOptions(users.map((u: any) => ({ code: u.user_id || u.id, @@ -221,11 +231,13 @@ export default function ItemInspectionInfoPage() { if (!typeKey) continue; typeFlags[typeKey] = true; if (!rowMap[typeKey]) rowMap[typeKey] = []; + const mCode = r.inspection_method || ""; + const mLabel = inspMethodCatOptions.find(o => o.code === mCode)?.label || mCode; rowMap[typeKey].push({ id: r.id, inspection_standard_id: r.inspection_standard_id || "", inspection_detail: r.inspection_item_name || r.inspection_standard || "", - inspection_method: r.inspection_method || "", + inspection_method: mLabel, apply_process: "", acceptance_criteria: r.pass_criteria || "", is_required: r.is_required === "true" || r.is_required === true, @@ -270,7 +282,9 @@ export default function ItemInspectionInfoPage() { if (r.id !== rowId) return r; if (field === "inspection_standard_id") { const opt = inspOptions.find(o => o.code === value); - return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: opt?.method || "" }; + const methodCode = opt?.method || ""; + const methodLabel = inspMethodCatOptions.find(o => o.code === methodCode)?.label || methodCode; + return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: methodLabel }; } return { ...r, [field]: value }; }), @@ -471,17 +485,36 @@ export default function ItemInspectionInfoPage() { {ts.visibleColumns.map((col) => renderCell(col.key))}
- {isExpanded && group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( - + {isExpanded && ( + - {row.inspection_type} - {resolveInspLabel(row.inspection_standard_id)} - {row.inspection_item_name || "-"} - {row.inspection_method || "-"} - {row.pass_criteria || "-"} + + + + + 검사유형 + 검사기준 + 검사항목 + 검사방법 + 합격기준 + + + + {group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( + + {row.inspection_type} + {resolveInspLabel(row.inspection_standard_id)} + {row.inspection_item_name || "-"} + {row.inspection_method || "-"} + {row.pass_criteria || "-"} + + ))} + +
+
- ))} + )} ); })} diff --git a/frontend/app/(main)/COMPANY_7/logistics/info/page.tsx b/frontend/app/(main)/COMPANY_7/logistics/info/page.tsx index 543dd3dd..35764744 100644 --- a/frontend/app/(main)/COMPANY_7/logistics/info/page.tsx +++ b/frontend/app/(main)/COMPANY_7/logistics/info/page.tsx @@ -230,11 +230,10 @@ function flattenCategories(items: any[]): { value: string; label: string }[] { const result: { value: string; label: string }[] = []; function walk(arr: any[]) { for (const item of arr) { - if (item.value || item.name) { - result.push({ - value: item.value || item.name, - label: item.label || item.name || item.value, - }); + const val = item.valueCode || item.value || item.name; + const lbl = item.valueLabel || item.label || item.name || val; + if (val) { + result.push({ value: val, label: lbl }); } if (item.children?.length) walk(item.children); } diff --git a/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx index 85cdc23c..5b730674 100644 --- a/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_7/logistics/receiving/page.tsx @@ -777,12 +777,16 @@ export default function ReceivingPage() { return; } + if (!modalWarehouse) { + toast.error("창고를 선택해주세요."); + return; + } setSaving(true); try { const res = await createReceiving({ inbound_number: modalInboundNo, inbound_date: modalInboundDate, - warehouse_code: modalWarehouse || undefined, + warehouse_code: modalWarehouse, location_code: modalLocation || undefined, inspector: modalInspector || undefined, manager: modalManager || undefined, diff --git a/frontend/app/(main)/COMPANY_7/quality/item-inspection/page.tsx b/frontend/app/(main)/COMPANY_7/quality/item-inspection/page.tsx index 2c0e1338..1059223c 100644 --- a/frontend/app/(main)/COMPANY_7/quality/item-inspection/page.tsx +++ b/frontend/app/(main)/COMPANY_7/quality/item-inspection/page.tsx @@ -72,6 +72,7 @@ export default function ItemInspectionInfoPage() { const [itemOptions, setItemOptions] = useState<{ code: string; name: string; item_type: string; unit: string }[]>([]); const [inspOptions, setInspOptions] = useState<{ code: string; label: string; detail: string; method: string; types: string[] }[]>([]); const [inspTypeCatOptions, setInspTypeCatOptions] = useState<{ code: string; label: string }[]>([]); + const [inspMethodCatOptions, setInspMethodCatOptions] = useState<{ code: string; label: string }[]>([]); const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); /* 검사유형별 검사항목 rows */ @@ -118,6 +119,15 @@ export default function ItemInspectionInfoPage() { setInspTypeCatOptions(flatCats); } catch { /* skip */ } + // 검사방법 카테고리 값 로드 (코드→라벨 매핑용) + try { + const methodRes = await apiClient.get(`/table-categories/${INSPECTION_TABLE}/inspection_method/values`); + const flatMethods: { code: string; label: string }[] = []; + const flattenM = (arr: any[]) => { for (const v of arr) { flatMethods.push({ code: v.valueCode, label: v.valueLabel }); if (v.children?.length) flattenM(v.children); } }; + if (methodRes.data?.data?.length) flattenM(methodRes.data.data); + setInspMethodCatOptions(flatMethods); + } catch { /* skip */ } + const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; setUserOptions(users.map((u: any) => ({ code: u.user_id || u.id, @@ -221,11 +231,13 @@ export default function ItemInspectionInfoPage() { if (!typeKey) continue; typeFlags[typeKey] = true; if (!rowMap[typeKey]) rowMap[typeKey] = []; + const mCode = r.inspection_method || ""; + const mLabel = inspMethodCatOptions.find(o => o.code === mCode)?.label || mCode; rowMap[typeKey].push({ id: r.id, inspection_standard_id: r.inspection_standard_id || "", inspection_detail: r.inspection_item_name || r.inspection_standard || "", - inspection_method: r.inspection_method || "", + inspection_method: mLabel, apply_process: "", acceptance_criteria: r.pass_criteria || "", is_required: r.is_required === "true" || r.is_required === true, @@ -270,7 +282,9 @@ export default function ItemInspectionInfoPage() { if (r.id !== rowId) return r; if (field === "inspection_standard_id") { const opt = inspOptions.find(o => o.code === value); - return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: opt?.method || "" }; + const methodCode = opt?.method || ""; + const methodLabel = inspMethodCatOptions.find(o => o.code === methodCode)?.label || methodCode; + return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: methodLabel }; } return { ...r, [field]: value }; }), @@ -471,17 +485,36 @@ export default function ItemInspectionInfoPage() { {ts.visibleColumns.map((col) => renderCell(col.key))}
- {isExpanded && group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( - + {isExpanded && ( + - {row.inspection_type} - {resolveInspLabel(row.inspection_standard_id)} - {row.inspection_item_name || "-"} - {row.inspection_method || "-"} - {row.pass_criteria || "-"} + + + + + 검사유형 + 검사기준 + 검사항목 + 검사방법 + 합격기준 + + + + {group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( + + {row.inspection_type} + {resolveInspLabel(row.inspection_standard_id)} + {row.inspection_item_name || "-"} + {row.inspection_method || "-"} + {row.pass_criteria || "-"} + + ))} + +
+
- ))} + )} ); })} diff --git a/frontend/app/(main)/COMPANY_8/logistics/info/page.tsx b/frontend/app/(main)/COMPANY_8/logistics/info/page.tsx index 543dd3dd..35764744 100644 --- a/frontend/app/(main)/COMPANY_8/logistics/info/page.tsx +++ b/frontend/app/(main)/COMPANY_8/logistics/info/page.tsx @@ -230,11 +230,10 @@ function flattenCategories(items: any[]): { value: string; label: string }[] { const result: { value: string; label: string }[] = []; function walk(arr: any[]) { for (const item of arr) { - if (item.value || item.name) { - result.push({ - value: item.value || item.name, - label: item.label || item.name || item.value, - }); + const val = item.valueCode || item.value || item.name; + const lbl = item.valueLabel || item.label || item.name || val; + if (val) { + result.push({ value: val, label: lbl }); } if (item.children?.length) walk(item.children); } diff --git a/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx index 85cdc23c..5b730674 100644 --- a/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_8/logistics/receiving/page.tsx @@ -777,12 +777,16 @@ export default function ReceivingPage() { return; } + if (!modalWarehouse) { + toast.error("창고를 선택해주세요."); + return; + } setSaving(true); try { const res = await createReceiving({ inbound_number: modalInboundNo, inbound_date: modalInboundDate, - warehouse_code: modalWarehouse || undefined, + warehouse_code: modalWarehouse, location_code: modalLocation || undefined, inspector: modalInspector || undefined, manager: modalManager || undefined, diff --git a/frontend/app/(main)/COMPANY_8/quality/item-inspection/page.tsx b/frontend/app/(main)/COMPANY_8/quality/item-inspection/page.tsx index 2c0e1338..1059223c 100644 --- a/frontend/app/(main)/COMPANY_8/quality/item-inspection/page.tsx +++ b/frontend/app/(main)/COMPANY_8/quality/item-inspection/page.tsx @@ -72,6 +72,7 @@ export default function ItemInspectionInfoPage() { const [itemOptions, setItemOptions] = useState<{ code: string; name: string; item_type: string; unit: string }[]>([]); const [inspOptions, setInspOptions] = useState<{ code: string; label: string; detail: string; method: string; types: string[] }[]>([]); const [inspTypeCatOptions, setInspTypeCatOptions] = useState<{ code: string; label: string }[]>([]); + const [inspMethodCatOptions, setInspMethodCatOptions] = useState<{ code: string; label: string }[]>([]); const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); /* 검사유형별 검사항목 rows */ @@ -118,6 +119,15 @@ export default function ItemInspectionInfoPage() { setInspTypeCatOptions(flatCats); } catch { /* skip */ } + // 검사방법 카테고리 값 로드 (코드→라벨 매핑용) + try { + const methodRes = await apiClient.get(`/table-categories/${INSPECTION_TABLE}/inspection_method/values`); + const flatMethods: { code: string; label: string }[] = []; + const flattenM = (arr: any[]) => { for (const v of arr) { flatMethods.push({ code: v.valueCode, label: v.valueLabel }); if (v.children?.length) flattenM(v.children); } }; + if (methodRes.data?.data?.length) flattenM(methodRes.data.data); + setInspMethodCatOptions(flatMethods); + } catch { /* skip */ } + const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; setUserOptions(users.map((u: any) => ({ code: u.user_id || u.id, @@ -221,11 +231,13 @@ export default function ItemInspectionInfoPage() { if (!typeKey) continue; typeFlags[typeKey] = true; if (!rowMap[typeKey]) rowMap[typeKey] = []; + const mCode = r.inspection_method || ""; + const mLabel = inspMethodCatOptions.find(o => o.code === mCode)?.label || mCode; rowMap[typeKey].push({ id: r.id, inspection_standard_id: r.inspection_standard_id || "", inspection_detail: r.inspection_item_name || r.inspection_standard || "", - inspection_method: r.inspection_method || "", + inspection_method: mLabel, apply_process: "", acceptance_criteria: r.pass_criteria || "", is_required: r.is_required === "true" || r.is_required === true, @@ -270,7 +282,9 @@ export default function ItemInspectionInfoPage() { if (r.id !== rowId) return r; if (field === "inspection_standard_id") { const opt = inspOptions.find(o => o.code === value); - return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: opt?.method || "" }; + const methodCode = opt?.method || ""; + const methodLabel = inspMethodCatOptions.find(o => o.code === methodCode)?.label || methodCode; + return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: methodLabel }; } return { ...r, [field]: value }; }), @@ -471,17 +485,36 @@ export default function ItemInspectionInfoPage() { {ts.visibleColumns.map((col) => renderCell(col.key))}
- {isExpanded && group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( - + {isExpanded && ( + - {row.inspection_type} - {resolveInspLabel(row.inspection_standard_id)} - {row.inspection_item_name || "-"} - {row.inspection_method || "-"} - {row.pass_criteria || "-"} + + + + + 검사유형 + 검사기준 + 검사항목 + 검사방법 + 합격기준 + + + + {group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( + + {row.inspection_type} + {resolveInspLabel(row.inspection_standard_id)} + {row.inspection_item_name || "-"} + {row.inspection_method || "-"} + {row.pass_criteria || "-"} + + ))} + +
+
- ))} + )} ); })} diff --git a/frontend/app/(main)/COMPANY_9/logistics/info/page.tsx b/frontend/app/(main)/COMPANY_9/logistics/info/page.tsx index 543dd3dd..35764744 100644 --- a/frontend/app/(main)/COMPANY_9/logistics/info/page.tsx +++ b/frontend/app/(main)/COMPANY_9/logistics/info/page.tsx @@ -230,11 +230,10 @@ function flattenCategories(items: any[]): { value: string; label: string }[] { const result: { value: string; label: string }[] = []; function walk(arr: any[]) { for (const item of arr) { - if (item.value || item.name) { - result.push({ - value: item.value || item.name, - label: item.label || item.name || item.value, - }); + const val = item.valueCode || item.value || item.name; + const lbl = item.valueLabel || item.label || item.name || val; + if (val) { + result.push({ value: val, label: lbl }); } if (item.children?.length) walk(item.children); } diff --git a/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx b/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx index 85cdc23c..5b730674 100644 --- a/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx +++ b/frontend/app/(main)/COMPANY_9/logistics/receiving/page.tsx @@ -777,12 +777,16 @@ export default function ReceivingPage() { return; } + if (!modalWarehouse) { + toast.error("창고를 선택해주세요."); + return; + } setSaving(true); try { const res = await createReceiving({ inbound_number: modalInboundNo, inbound_date: modalInboundDate, - warehouse_code: modalWarehouse || undefined, + warehouse_code: modalWarehouse, location_code: modalLocation || undefined, inspector: modalInspector || undefined, manager: modalManager || undefined, diff --git a/frontend/app/(main)/COMPANY_9/quality/item-inspection/page.tsx b/frontend/app/(main)/COMPANY_9/quality/item-inspection/page.tsx index 2c0e1338..1059223c 100644 --- a/frontend/app/(main)/COMPANY_9/quality/item-inspection/page.tsx +++ b/frontend/app/(main)/COMPANY_9/quality/item-inspection/page.tsx @@ -72,6 +72,7 @@ export default function ItemInspectionInfoPage() { const [itemOptions, setItemOptions] = useState<{ code: string; name: string; item_type: string; unit: string }[]>([]); const [inspOptions, setInspOptions] = useState<{ code: string; label: string; detail: string; method: string; types: string[] }[]>([]); const [inspTypeCatOptions, setInspTypeCatOptions] = useState<{ code: string; label: string }[]>([]); + const [inspMethodCatOptions, setInspMethodCatOptions] = useState<{ code: string; label: string }[]>([]); const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); /* 검사유형별 검사항목 rows */ @@ -118,6 +119,15 @@ export default function ItemInspectionInfoPage() { setInspTypeCatOptions(flatCats); } catch { /* skip */ } + // 검사방법 카테고리 값 로드 (코드→라벨 매핑용) + try { + const methodRes = await apiClient.get(`/table-categories/${INSPECTION_TABLE}/inspection_method/values`); + const flatMethods: { code: string; label: string }[] = []; + const flattenM = (arr: any[]) => { for (const v of arr) { flatMethods.push({ code: v.valueCode, label: v.valueLabel }); if (v.children?.length) flattenM(v.children); } }; + if (methodRes.data?.data?.length) flattenM(methodRes.data.data); + setInspMethodCatOptions(flatMethods); + } catch { /* skip */ } + const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; setUserOptions(users.map((u: any) => ({ code: u.user_id || u.id, @@ -221,11 +231,13 @@ export default function ItemInspectionInfoPage() { if (!typeKey) continue; typeFlags[typeKey] = true; if (!rowMap[typeKey]) rowMap[typeKey] = []; + const mCode = r.inspection_method || ""; + const mLabel = inspMethodCatOptions.find(o => o.code === mCode)?.label || mCode; rowMap[typeKey].push({ id: r.id, inspection_standard_id: r.inspection_standard_id || "", inspection_detail: r.inspection_item_name || r.inspection_standard || "", - inspection_method: r.inspection_method || "", + inspection_method: mLabel, apply_process: "", acceptance_criteria: r.pass_criteria || "", is_required: r.is_required === "true" || r.is_required === true, @@ -270,7 +282,9 @@ export default function ItemInspectionInfoPage() { if (r.id !== rowId) return r; if (field === "inspection_standard_id") { const opt = inspOptions.find(o => o.code === value); - return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: opt?.method || "" }; + const methodCode = opt?.method || ""; + const methodLabel = inspMethodCatOptions.find(o => o.code === methodCode)?.label || methodCode; + return { ...r, inspection_standard_id: value, inspection_detail: opt?.detail || "", inspection_method: methodLabel }; } return { ...r, [field]: value }; }), @@ -471,17 +485,36 @@ export default function ItemInspectionInfoPage() { {ts.visibleColumns.map((col) => renderCell(col.key))}
- {isExpanded && group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( - + {isExpanded && ( + - {row.inspection_type} - {resolveInspLabel(row.inspection_standard_id)} - {row.inspection_item_name || "-"} - {row.inspection_method || "-"} - {row.pass_criteria || "-"} + + + + + 검사유형 + 검사기준 + 검사항목 + 검사방법 + 합격기준 + + + + {group.rows.filter((r: any) => r.inspection_standard_id).map((row: any) => ( + + {row.inspection_type} + {resolveInspLabel(row.inspection_standard_id)} + {row.inspection_item_name || "-"} + {row.inspection_method || "-"} + {row.pass_criteria || "-"} + + ))} + +
+
- ))} + )} ); })}