diff --git a/src/components/material/ReceivingManagement/ImportInspectionInputModal.tsx b/src/components/material/ReceivingManagement/ImportInspectionInputModal.tsx index 3faf92d5..c70c33bf 100644 --- a/src/components/material/ReceivingManagement/ImportInspectionInputModal.tsx +++ b/src/components/material/ReceivingManagement/ImportInspectionInputModal.tsx @@ -75,12 +75,58 @@ interface RawCriteriaData { type InspectionItem = InspectionTemplateResponse['inspectionItems'][number]; +// ===== standard 텍스트에서 판정 기준 파싱 ===== +interface ParsedStandard { + type: 'gte' | 'lte' | 'range' | 'percent'; + value?: number; + min?: number; + max?: number; + percent?: number; +} + +function parseStandardText(text: string | undefined): ParsedStandard | null { + if (!text) return null; + const s = text.trim(); + + // "500 이상", "270 이상", "한면 17 이상", "120% 이상" + const gteMatch = s.match(/([\d.]+)(%?)\s*이상/); + if (gteMatch) { + const v = parseFloat(gteMatch[1]); + if (!isNaN(v)) return { type: 'gte', value: v }; + } + + // "200 이하" + const lteMatch = s.match(/([\d.]+)\s*이하/); + if (lteMatch) { + const v = parseFloat(lteMatch[1]); + if (!isNaN(v)) return { type: 'lte', value: v }; + } + + // "400~510" + const rangeMatch = s.match(/([\d.]+)\s*[~~]\s*([\d.]+)/); + if (rangeMatch) { + const min = parseFloat(rangeMatch[1]); + const max = parseFloat(rangeMatch[2]); + if (!isNaN(min) && !isNaN(max)) return { type: 'range', min, max }; + } + + // "규격 ±10%", "규격 ±5%", "규격 ±2%" + const percentMatch = s.match(/규격\s*[±]\s*([\d.]+)%/); + if (percentMatch) { + const pct = parseFloat(percentMatch[1]); + if (!isNaN(pct)) return { type: 'percent', percent: pct }; + } + + return null; +} + // ===== Auto-judgment logic ===== function calculateAutoResult( value: string, rawTolerance: RawToleranceData | null | undefined, rawCriteria: RawCriteriaData | null | undefined, baseValue: number | null | undefined, + standardText?: string, ): 'ok' | 'ng' | null { const num = parseFloat(value); if (isNaN(num)) return null; @@ -127,6 +173,22 @@ function calculateAutoResult( return pass ? 'ok' : 'ng'; } + // 3. standard 텍스트 파싱 기반 판정 (tolerance/criteria 미설정 시 fallback) + const parsed = parseStandardText(standardText); + if (parsed) { + switch (parsed.type) { + case 'gte': return num >= parsed.value! ? 'ok' : 'ng'; + case 'lte': return num <= parsed.value! ? 'ok' : 'ng'; + case 'range': return (num >= parsed.min! && num <= parsed.max!) ? 'ok' : 'ng'; + case 'percent': + if (baseValue != null) { + const tolerance = baseValue * parsed.percent! / 100; + return (num >= baseValue - tolerance && num <= baseValue + tolerance) ? 'ok' : 'ng'; + } + return null; + } + } + return null; } @@ -253,13 +315,14 @@ export function ImportInspectionInputModal({ // ===== Raw data map (tolerance / criteria) for auto-judgment ===== const rawDataMap = useMemo(() => { - const map = new Map(); + const map = new Map(); if (!resolveData) return map; for (const section of resolveData.template.sections) { for (const item of section.items) { map.set(String(item.id), { tolerance: (item.tolerance && typeof item.tolerance === 'object') ? item.tolerance as RawToleranceData : null, criteria: item.standard_criteria || null, + standard: (item as Record).standard as string | null, }); } } @@ -492,6 +555,22 @@ export function ImportInspectionInputModal({ if (max != null) return String(max); } + // 4. standard 텍스트 파싱 기반 (tolerance/criteria 미설정 시 fallback) + const stdText = raw?.standard || item.standard?.description; + const parsed = parseStandardText(stdText); + if (parsed) { + switch (parsed.type) { + case 'gte': return String(parsed.value!); + case 'lte': return String(parsed.value!); + case 'range': return String(Math.round(((parsed.min! + parsed.max!) / 2) * 100) / 100); + case 'percent': + if (base != null) { + return String(base); + } + break; + } + } + return '100'; }, [rawDataMap, baseValueMap] @@ -535,9 +614,12 @@ export function ImportInspectionInputModal({ if (!v) return null; const raw = rawDataMap.get(itemId); const base = baseValueMap.get(itemId); - return calculateAutoResult(v, raw?.tolerance, raw?.criteria, base); + // template inspectionItems에서 standard.description 추출 + const tmplItem = template?.inspectionItems.find(i => i.id === itemId); + const stdText = raw?.standard || tmplItem?.standard?.description; + return calculateAutoResult(v, raw?.tolerance, raw?.criteria, base, stdText); }, - [measurements, rawDataMap, baseValueMap] + [measurements, rawDataMap, baseValueMap, template] ); // ===== 항목별 종합 판정 (c=0: 모든 N 측정값이 입력+통과해야 OK) ===== @@ -562,7 +644,8 @@ export function ImportInspectionInputModal({ } else { const raw = rawDataMap.get(item.id); const base = baseValueMap.get(item.id); - const r = calculateAutoResult(v, raw?.tolerance, raw?.criteria, base); + const stdText = raw?.standard || item.standard?.description; + const r = calculateAutoResult(v, raw?.tolerance, raw?.criteria, base, stdText); if (r === 'ng') hasNg = true; } }