fix: [수입검사] 테스트입력 시 standard 텍스트 파싱으로 적정값 생성
tolerance/standard_criteria JSON이 미설정된 템플릿에서 테스트입력 시
모든 값이 100으로 채워지던 문제 해결.
standard 텍스트("500 이상", "규격 ±5%" 등)를 파싱하여
적정 테스트값 생성 및 자동 판정 로직에 반영.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user