summaryrefslogtreecommitdiff
path: root/lib/items-tech/utils
diff options
context:
space:
mode:
Diffstat (limited to 'lib/items-tech/utils')
-rw-r--r--lib/items-tech/utils/import-utils.ts240
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[]; // 중복된 아이템코드들
+}