fix: [수입검사] 테스트입력 시 standard 텍스트 파싱으로 적정값 생성

tolerance/standard_criteria JSON이 미설정된 템플릿에서 테스트입력 시
모든 값이 100으로 채워지던 문제 해결.
standard 텍스트("500 이상", "규격 ±5%" 등)를 파싱하여
적정 테스트값 생성 및 자동 판정 로직에 반영.
This commit is contained in:
김보곤
2026-03-20 14:43:02 +09:00
parent 17bc89b35c
commit a25c2c6d16

View File

@@ -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<string, { tolerance: RawToleranceData | null; criteria: RawCriteriaData | null }>();
const map = new Map<string, { tolerance: RawToleranceData | null; criteria: RawCriteriaData | null; standard: string | null }>();
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<string, unknown>).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;
}
}