feat: 피팅류 엑셀 내보내기 개선 및 프로젝트 비활성화 버그 수정
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

 피팅류 엑셀 내보내기 개선:
- 품목명에 상세 피팅 타입 표시 (SOCK-O-LET, ELBOW 90° LR 등)
- G열부터 압력등급/스케줄/재질/사용자요구/추가요청사항 체계적 배치
- 분류기 추출 요구사항(J열)과 사용자 입력 요구사항(K열) 분리
- P열 납기일 고정 규칙 유지, 관리항목 자동 채움

🐛 프로젝트 비활성화 버그 수정:
- 백엔드: job_no 필드 추가로 프론트엔드 호환성 확보
- 프론트엔드: 안전한 프로젝트 식별자 처리 로직 구현
- 개별 프로젝트 비활성화 시 전체 프로젝트 영향 문제 해결
- 디버깅 로그 추가로 상태 변경 추적 가능

🔧 기타 개선사항:
- BOM 페이지 이모지 제거
- 구매신청 후 자재 비활성화 기능 구현
- 모든 카테고리 뷰에 onPurchasedMaterialsUpdate 콜백 추가
This commit is contained in:
hyungi
2025-10-16 14:00:44 +09:00
parent 22baea38e1
commit c7297c6fb7
9 changed files with 339 additions and 86 deletions

View File

@@ -180,49 +180,112 @@ const formatMaterialForExcel = (material, includeComparison = false) => {
}
}
} else if (category === 'FITTING') {
// 피팅 상세 타입 표시 (OLET 등 풀네임)
// 피팅 상세 타입 표시 - 프론트엔드 표시와 동일한 로직 사용
const fittingDetails = material.fitting_details || {};
const fittingType = fittingDetails.fitting_type || '';
const fittingSubtype = fittingDetails.fitting_subtype || '';
const classificationDetails = material.classification_details || {};
const fittingTypeInfo = classificationDetails.fitting_type || {};
const fittingType = fittingTypeInfo.type || fittingDetails.fitting_type || '';
const fittingSubtype = fittingTypeInfo.subtype || fittingDetails.fitting_subtype || '';
// 프론트엔드와 동일한 displayType 로직 사용
let displayType = '';
if (fittingType === 'OLET') {
// OLET 풀네임 표시
switch (fittingSubtype) {
case 'SOCKOLET':
itemName = 'SOCK-O-LET';
displayType = 'SOCK-O-LET';
break;
case 'WELDOLET':
itemName = 'WELD-O-LET';
displayType = 'WELD-O-LET';
break;
case 'ELLOLET':
itemName = 'ELL-O-LET';
displayType = 'ELL-O-LET';
break;
case 'THREADOLET':
itemName = 'THREAD-O-LET';
displayType = 'THREAD-O-LET';
break;
case 'ELBOLET':
itemName = 'ELB-O-LET';
displayType = 'ELB-O-LET';
break;
case 'NIPOLET':
itemName = 'NIP-O-LET';
displayType = 'NIP-O-LET';
break;
case 'COUPOLET':
itemName = 'COUP-O-LET';
displayType = 'COUP-O-LET';
break;
default:
itemName = 'OLET';
// Description에서 직접 추출
const descUpper = cleanDescription.toUpperCase();
if (descUpper.includes('SOCK-O-LET') || descUpper.includes('SOCKOLET')) {
displayType = 'SOCK-O-LET';
} else if (descUpper.includes('WELD-O-LET') || descUpper.includes('WELDOLET')) {
displayType = 'WELD-O-LET';
} else if (descUpper.includes('ELL-O-LET') || descUpper.includes('ELLOLET')) {
displayType = 'ELL-O-LET';
} else if (descUpper.includes('THREAD-O-LET') || descUpper.includes('THREADOLET')) {
displayType = 'THREAD-O-LET';
} else if (descUpper.includes('ELB-O-LET') || descUpper.includes('ELBOLET')) {
displayType = 'ELB-O-LET';
} else if (descUpper.includes('NIP-O-LET') || descUpper.includes('NIPOLET')) {
displayType = 'NIP-O-LET';
} else if (descUpper.includes('COUP-O-LET') || descUpper.includes('COUPOLET')) {
displayType = 'COUP-O-LET';
} else {
displayType = 'OLET';
}
}
} else if (fittingType === 'TEE' && fittingSubtype === 'REDUCING') {
displayType = 'TEE REDUCING';
} else if (fittingType === 'REDUCER' && fittingSubtype === 'CONCENTRIC') {
displayType = 'REDUCER CONC';
} else if (fittingType === 'REDUCER' && fittingSubtype === 'ECCENTRIC') {
displayType = 'REDUCER ECC';
} else if (cleanDescription.toUpperCase().includes('TEE RED')) {
displayType = 'TEE REDUCING';
} else if (cleanDescription.toUpperCase().includes('RED CONC')) {
displayType = 'REDUCER CONC';
} else if (cleanDescription.toUpperCase().includes('RED ECC')) {
displayType = 'REDUCER ECC';
} else if (cleanDescription.toUpperCase().includes('CAP')) {
if (cleanDescription.includes('NPT(F)')) {
displayType = 'CAP NPT(F)';
} else if (cleanDescription.includes('SW')) {
displayType = 'CAP SW';
} else if (cleanDescription.includes('BW')) {
displayType = 'CAP BW';
} else {
displayType = 'CAP';
}
} else if (cleanDescription.toUpperCase().includes('PLUG')) {
if (cleanDescription.toUpperCase().includes('HEX')) {
if (cleanDescription.includes('NPT(M)')) {
displayType = 'HEX PLUG NPT(M)';
} else {
displayType = 'HEX PLUG';
}
} else if (cleanDescription.includes('NPT(M)')) {
displayType = 'PLUG NPT(M)';
} else if (cleanDescription.includes('NPT')) {
displayType = 'PLUG NPT';
} else {
displayType = 'PLUG';
}
} else if (fittingType === 'NIPPLE') {
const length = fittingDetails.length_mm || fittingDetails.avg_length_mm;
let nippleType = 'NIPPLE';
if (length) nippleType += ` ${length}mm`;
displayType = nippleType;
} else if (fittingType === 'ELBOW') {
// 엘보 상세 정보 표시 (각도, 반경, 연결방식)
// 엘보 상세 정보 표시
let elbowDetails = [];
// 각도 정보
if (fittingSubtype.includes('90DEG') || cleanDescription.includes('90')) {
elbowDetails.push('90');
elbowDetails.push('90°');
} else if (fittingSubtype.includes('45DEG') || cleanDescription.includes('45')) {
elbowDetails.push('45');
} else {
elbowDetails.push('90도'); // 기본값
elbowDetails.push('45°');
}
// 반경 정보
@@ -232,25 +295,12 @@ const formatMaterialForExcel = (material, includeComparison = false) => {
elbowDetails.push('SR');
}
// 연결 방식
if (cleanDescription.includes('SW')) {
elbowDetails.push('SW');
} else if (cleanDescription.includes('BW')) {
elbowDetails.push('BW');
}
itemName = `엘보 ${elbowDetails.join(' ')}`.trim();
} else if (fittingType === 'TEE') {
// 티 타입 표시
const teeType = fittingSubtype === 'EQUAL' ? '등경' : fittingSubtype === 'REDUCING' ? '축소' : '';
itemName = `${teeType}`.trim();
} else if (fittingType === 'REDUCER') {
// 리듀서 타입 표시
const reducerType = fittingSubtype === 'CONCENTRIC' ? '동심' : fittingSubtype === 'ECCENTRIC' ? '편심' : '';
itemName = `리듀서 ${reducerType}`.trim();
displayType = elbowDetails.length > 0 ? `ELBOW ${elbowDetails.join(' ')}` : 'ELBOW';
} else {
itemName = fittingType || 'FITTING';
displayType = fittingType || 'FITTING';
}
itemName = displayType;
} else if (category === 'FLANGE') {
// 플랜지 상세 타입 표시
const flangeDetails = material.flange_details || {};
@@ -674,17 +724,17 @@ const formatMaterialForExcel = (material, includeComparison = false) => {
base['관리항목3'] = ''; // N열
base['관리항목4'] = ''; // O열
} else if (category === 'FITTING') {
// 피팅 전용 컬럼 (F~O) - 타입 제거, 품목명에 포함됨
// 피팅 전용 컬럼 (F~O) - 새로운 구조
base['크기'] = material.size_spec || '-'; // F열
base['압력등급'] = pressure; // G열
base['재질'] = grade; // H열
base['상세내역'] = detailInfo || '-'; // I열
base['사용자요구'] = material.user_requirement || ''; // J열
base['관리항목1'] = ''; // K열
base['관리항목2'] = ''; // L열
base['관리항목3'] = ''; // M열
base['관리항목4'] = ''; // N열
base['관리항목5'] = ''; // O열
base['스케줄'] = schedule; // H열
base['재질'] = grade; // I열
base['사용자요구'] = material.user_requirements?.join(', ') || ''; // J열 (분류기에서 추출)
base['추가요청사항'] = material.user_requirement || ''; // K열 (사용자 입력)
base['관리항목1'] = ''; // L열
base['관리항목2'] = ''; // M열
base['관리항목3'] = ''; // N열
base['관리항목4'] = ''; // O열
} else if (category === 'FLANGE') {
// 플랜지 타입 풀네임 매핑 (영어)
const flangeTypeMap = {