diff options
Diffstat (limited to 'lib/items-tech/utils')
| -rw-r--r-- | lib/items-tech/utils/import-utils.ts | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/lib/items-tech/utils/import-utils.ts b/lib/items-tech/utils/import-utils.ts new file mode 100644 index 00000000..e8a0d7a5 --- /dev/null +++ b/lib/items-tech/utils/import-utils.ts @@ -0,0 +1,240 @@ +import * as ExcelJS from 'exceljs'; +import { saveAs } from 'file-saver'; + +/** + * itemCode 문자열을 분할하여 배열로 반환 + * 공백이나 콤마로 구분된 여러 itemCode를 처리 + * 빈 itemCode의 경우 빈 문자열 하나를 포함한 배열 반환 + */ +export function splitItemCodes(itemCode: string): string[] { + if (!itemCode || typeof itemCode !== 'string') { + return [""]; // 빈 itemCode의 경우 빈 문자열 하나 반환 + } + + const trimmedCode = itemCode.trim(); + if (trimmedCode === '') { + return [""]; // 공백만 있는 경우도 빈 문자열 하나 반환 + } + + // 공백과 콤마로 분할하고, trim 처리 (빈 문자열도 유지) + return trimmedCode + .split(/[\s,]+/) + .map(code => code.trim()); +} + +/** + * 임시 IM 코드 생성 (4자리 숫자 형식) + */ +export function generateTempIMCode(startNumber: number): string { + return `IM${startNumber.toString().padStart(4, '0')}`; +} + +/** + * itemCode가 비어있거나 유효하지 않을 때 임시 코드 생성 + * @param itemCodes 분할된 itemCode 배열 + * @param startNumber 시작 번호 + * @returns 처리된 itemCode 배열 + */ +export function processEmptyItemCodes(itemCodes: string[], startNumber: number): { codes: string[], nextNumber: number } { + const processedCodes: string[] = []; + let currentNumber = startNumber; + + for (const code of itemCodes) { + if (!code || code.trim() === '') { + // 빈 코드인 경우 임시 코드 생성 + processedCodes.push(generateTempIMCode(currentNumber)); + currentNumber++; + } else { + // 유효한 코드인 경우 그대로 사용 + processedCodes.push(code); + } + } + + return { + codes: processedCodes, + nextNumber: currentNumber + }; +} + +/** + * 에러 정보를 포함한 Excel 파일 생성 및 다운로드 + */ +export async function createErrorExcelFile( + errors: Array<{ + row: number; + message: string; + itemCode?: string; + workType?: string; + originalData?: Record<string, any>; + }>, + itemType: 'top' | 'hull' | 'ship' +): Promise<string> { + try { + const workbook = new ExcelJS.Workbook(); + + // 에러 시트 생성 + const errorSheet = workbook.addWorksheet('Import 오류 목록'); + + // 헤더 설정 + const headers = [ + '행 번호', + '아이템코드', + '기능(공종)', + '자재명', + '자재명(상세)', + '선종', // 조선 아이템의 경우 + '오류 내용', + '해결 방법' + ]; + + errorSheet.addRow(headers); + + // 헤더 스타일 + const headerRow = errorSheet.getRow(1); + headerRow.eachCell((cell) => { + cell.fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: 'FFFF6B6B' } + }; + cell.font = { + bold: true, + color: { argb: 'FFFFFFFF' } + }; + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + }); + + // 에러 데이터 추가 + errors.forEach((error) => { + const originalData = error.originalData || {}; + const errorRow = errorSheet.addRow([ + error.row, + error.itemCode || originalData.itemCode || '', + error.workType || originalData.workType || '', + originalData.itemList || '', + originalData.subItemList || '', + originalData.shipTypes || '', // 조선 아이템의 경우 + error.message, + getSolutionMessage(error.message) + ]); + + // 에러 행 스타일 + errorRow.eachCell((cell, colNumber) => { + if (colNumber === 7) { // 오류 내용 컬럼 + cell.fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: 'FFFFE0E0' } + }; + } + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + }); + }); + + // 안내 시트 생성 + const instructionSheet = workbook.addWorksheet('오류 해결 가이드'); + + const instructions = [ + ['📌 오류 해결 방법 안내', ''], + ['', ''], + ['🔍 중복 아이템코드 오류', ''], + ['• 원인: 이미 존재하는 아이템코드입니다.', ''], + ['• 해결: 다른 아이템코드를 사용하거나 기존 아이템을 수정하세요.', ''], + ['', ''], + ['🔍 필수 필드 누락 오류', ''], + ['• 원인: 기능(공종) 등 필수 필드가 비어있습니다.', ''], + ['• 해결: 모든 필수 필드를 입력하세요.', ''], + ['', ''], + ['🔍 데이터 형식 오류', ''], + ['• 원인: 데이터 형식이 올바르지 않습니다.', ''], + ['• 해결: 올바른 형식으로 데이터를 입력하세요.', ''], + ['', ''], + ['📞 추가 문의: 시스템 관리자', ''] + ]; + + instructions.forEach((rowData, index) => { + const row = instructionSheet.addRow(rowData); + if (index === 0) { + row.getCell(1).font = { bold: true, size: 14, color: { argb: 'FF1F4E79' } }; + } else if (rowData[0]?.includes('📌') || rowData[0]?.includes('🔍')) { + row.getCell(1).font = { bold: true, color: { argb: 'FF1F4E79' } }; + } else if (rowData[0]?.includes(':')) { + row.getCell(1).font = { bold: true, color: { argb: 'FF1F4E79' } }; + } + }); + + instructionSheet.getColumn(1).width = 60; + + // 기본 컬럼 너비 설정 + errorSheet.getColumn(1).width = 10; // 행 번호 + errorSheet.getColumn(2).width = 20; // 아이템코드 + errorSheet.getColumn(3).width = 15; // 기능(공종) + errorSheet.getColumn(4).width = 30; // 자재명 + errorSheet.getColumn(5).width = 30; // 자재명(상세) + errorSheet.getColumn(6).width = 20; // 선종 + errorSheet.getColumn(7).width = 60; // 오류 내용 + errorSheet.getColumn(8).width = 40; // 해결 방법 + + // 파일 생성 및 다운로드 + const buffer = await workbook.xlsx.writeBuffer(); + const blob = new Blob([buffer], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + }); + + const itemTypeNames = { + top: '해양TOP', + hull: '해양HULL', + ship: '조선' + }; + + const fileName = `${itemTypeNames[itemType]}_Import_오류_${new Date().toISOString().split('T')[0]}_${Date.now()}.xlsx`; + saveAs(blob, fileName); + + return fileName; + } catch (error) { + console.error("오류 파일 생성 중 오류:", error); + return ''; + } +} + +/** + * 오류 메시지에 따른 해결 방법 반환 + */ +function getSolutionMessage(errorMessage: string): string { + if (errorMessage.includes('중복')) { + return '다른 아이템코드를 사용하거나 기존 아이템을 수정하세요.'; + } else if (errorMessage.includes('필수')) { + return '모든 필수 필드를 입력하세요.'; + } else if (errorMessage.includes('형식')) { + return '올바른 형식으로 데이터를 입력하세요.'; + } else { + return '데이터를 확인하고 다시 시도하세요.'; + } +} + +/** + * 확장된 ProcessResult 인터페이스 + */ +export interface ExtendedProcessResult { + successCount: number; + errorCount: number; + errors: Array<{ + row: number; + message: string; + itemCode?: string; + workType?: string; + originalData?: Record<string, any>; + }>; + processedItemCodes: string[]; // 성공적으로 처리된 아이템코드들 + duplicateItemCodes: string[]; // 중복된 아이템코드들 +} |
