feat: SPECIAL/UNCLASSIFIED 카테고리 추가 및 WELD GAP 자동 제외
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

주요 변경사항:
- SPECIAL 카테고리 추가: 특수 제작 품목 관리 (Type, Drawing, Detail1-4)
- UNCLASSIFIED 카테고리 추가: 미분류 자재 원본 그대로 표시
- UNKNOWN → UNCLASSIFIED 통합: 기존 UNKNOWN 카테고리 제거
- WELD GAP 자동 제외: BOM 업로드 시 WELD GAP 항목 자동 필터링

백엔드:
- integrated_classifier.py: UNKNOWN → UNCLASSIFIED 변경, SPECIAL 우선순위 분류
- files.py: parse_dataframe에서 WELD GAP 필터링, UNKNOWN 참조 제거
- exclude_classifier.py: WELD GAP 제외 로직 유지

프론트엔드:
- SpecialMaterialsView.jsx: 특수 제작 품목 관리 컴포넌트
- UnclassifiedMaterialsView.jsx: 미분류 자재 관리 컴포넌트
- BOMManagementPage.jsx: 새 카테고리 추가 및 라우팅
- excelExport.js: SPECIAL/UNCLASSIFIED 엑셀 내보내기 지원
- 모든 UNKNOWN 참조를 UNCLASSIFIED로 변경

기능 개선:
- 저장 기능: 모든 카테고리에 추가요청사항 저장/편집 기능
- P열 납기일 규칙: 모든 카테고리 엑셀 내보내기 통일
- UI 개선: Detail1-4 컬럼명으로 혼동 방지
- 데이터 정리: 모든 프로젝트 및 BOM 데이터 초기화
This commit is contained in:
hyungi
2025-10-17 13:48:48 +09:00
parent f336b5a4a8
commit e0ad21bfad
14 changed files with 1335 additions and 41 deletions

View File

@@ -8,7 +8,9 @@ import {
ValveMaterialsView,
GasketMaterialsView,
BoltMaterialsView,
SupportMaterialsView
SupportMaterialsView,
SpecialMaterialsView,
UnclassifiedMaterialsView
} from '../components/bom';
import './BOMManagementPage.css';
@@ -52,7 +54,9 @@ const BOMManagementPage = ({
{ key: 'VALVE', label: 'Valves', color: '#ef4444' },
{ key: 'GASKET', label: 'Gaskets', color: '#8b5cf6' },
{ key: 'BOLT', label: 'Bolts', color: '#6b7280' },
{ key: 'SUPPORT', label: 'Supports', color: '#f97316' }
{ key: 'SUPPORT', label: 'Supports', color: '#f97316' },
{ key: 'SPECIAL', label: 'Special Items', color: '#ec4899' },
{ key: 'UNCLASSIFIED', label: 'Unclassified', color: '#64748b' }
];
// 자료 로드 함수들
@@ -208,6 +212,10 @@ const BOMManagementPage = ({
return <BoltMaterialsView {...commonProps} />;
case 'SUPPORT':
return <SupportMaterialsView {...commonProps} />;
case 'SPECIAL':
return <SpecialMaterialsView {...commonProps} />;
case 'UNCLASSIFIED':
return <UnclassifiedMaterialsView {...commonProps} />;
default:
return <div>카테고리를 선택해주세요.</div>;
}

View File

@@ -874,7 +874,7 @@
background: #d97706;
}
/* UNKNOWN 전용 헤더 - 5개 컬럼 */
/* UNCLASSIFIED 전용 헤더 - 5개 컬럼 */
.detailed-grid-header.unknown-header {
grid-template-columns: 5% 10% 1fr 20% 10%;
@@ -891,7 +891,7 @@
border-right: none;
}
/* UNKNOWN 전용 행 - 5개 컬럼 */
/* UNCLASSIFIED 전용 행 - 5개 컬럼 */
.detailed-material-row.unknown-row {
grid-template-columns: 5% 10% 1fr 20% 10%;
@@ -906,7 +906,7 @@
border-right: none;
}
/* UNKNOWN 설명 셀 스타일 */
/* UNCLASSIFIED 설명 셀 스타일 */
.description-cell {
overflow: visible;
text-overflow: initial;

View File

@@ -433,7 +433,7 @@ const NewMaterialsPage = ({
const getCategoryCounts = () => {
const counts = {};
materials.forEach(material => {
const category = material.classified_category || 'UNKNOWN';
const category = material.classified_category || 'UNCLASSIFIED';
counts[category] = (counts[category] || 0) + 1;
});
return counts;
@@ -476,7 +476,7 @@ const NewMaterialsPage = ({
'BOLT': 'BOLT',
'GASKET': 'GASKET',
'INSTRUMENT': 'INSTRUMENT',
'UNKNOWN': 'UNKNOWN'
'UNCLASSIFIED': 'UNCLASSIFIED'
};
return categoryMap[category] || category;
};
@@ -1090,17 +1090,17 @@ const NewMaterialsPage = ({
unit: '개',
isSpecial: true
};
} else if (category === 'UNKNOWN') {
} else if (category === 'UNCLASSIFIED') {
return {
type: 'UNKNOWN',
description: material.original_description || 'Unknown Item',
type: 'UNCLASSIFIED',
description: material.original_description || 'Unclassified Item',
quantity: Math.round(material.quantity || 0),
unit: '개',
isUnknown: true
};
} else {
return {
type: category || 'UNKNOWN',
type: category || 'UNCLASSIFIED',
subtype: '-',
size: material.size_spec || '-',
schedule: '-',
@@ -1939,7 +1939,7 @@ const NewMaterialsPage = ({
<div>사용자요구</div>
<FilterableHeader sortKey="quantity" filterKey="quantity">수량</FilterableHeader>
</div>
) : selectedCategory === 'UNKNOWN' ? (
) : selectedCategory === 'UNCLASSIFIED' ? (
<div className="detailed-grid-header unknown-header">
<div>선택</div>
<div>종류</div>
@@ -2464,7 +2464,7 @@ const NewMaterialsPage = ({
);
}
if (material.classified_category === 'UNKNOWN') {
if (material.classified_category === 'UNCLASSIFIED') {
// 구매신청 여부 확인
const isPurchased = material.grouped_ids ?
material.grouped_ids.some(id => purchasedMaterials.has(id)) :

View File

@@ -339,7 +339,7 @@ const PurchaseRequestPage = ({ onNavigate, fileId, jobNo, selectedProject }) =>
{(() => {
// 카테고리별로 자재 그룹화
const groupedByCategory = requestMaterials.reduce((acc, material) => {
const category = material.category || material.classified_category || 'UNKNOWN';
const category = material.category || material.classified_category || 'UNCLASSIFIED';
if (!acc[category]) acc[category] = [];
acc[category].push(material);
return acc;