From 6f22fc9ebc8d175041aa18cf0986592e57d03f63 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 3 Jul 2025 02:47:09 +0000 Subject: (최겸) 기술영업 벤더별 아이템 조회 기능 추가 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table/excel-import.tsx | 220 +++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 lib/tech-vendor-possible-items/table/excel-import.tsx (limited to 'lib/tech-vendor-possible-items/table/excel-import.tsx') diff --git a/lib/tech-vendor-possible-items/table/excel-import.tsx b/lib/tech-vendor-possible-items/table/excel-import.tsx new file mode 100644 index 00000000..fbf984dd --- /dev/null +++ b/lib/tech-vendor-possible-items/table/excel-import.tsx @@ -0,0 +1,220 @@ +"use client"; + +import * as ExcelJS from 'exceljs'; +import { ImportTechVendorPossibleItemData, ImportResult, importTechVendorPossibleItems } from '../service'; +import { saveAs } from "file-saver"; + +export interface ExcelImportResult extends ImportResult { + errorFileUrl?: string; +} + +/** + * Excel 파일에서 tech vendor possible items 데이터를 읽고 import + */ +export async function importTechVendorPossibleItemsFromExcel( + file: File +): Promise { + try { + const buffer = await file.arrayBuffer(); + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.load(buffer); + + // 첫 번째 워크시트에서 데이터 읽기 + const worksheet = workbook.getWorksheet(1); + if (!worksheet) { + return { + success: false, + totalRows: 0, + successCount: 0, + failedRows: [{ row: 0, error: "워크시트를 찾을 수 없습니다." }], + }; + } + + const data: ImportTechVendorPossibleItemData[] = []; + + // 데이터 행 읽기 (헤더 제외) + worksheet.eachRow((row, rowNumber) => { + if (rowNumber === 1) return; // 헤더 건너뛰기 + + const itemCode = row.getCell(1).value?.toString()?.trim(); + const vendorCode = row.getCell(2).value?.toString()?.trim(); + const vendorEmail = row.getCell(3).value?.toString()?.trim(); + + // 빈 행 건너뛰기 + if (!itemCode && !vendorCode && !vendorEmail) return; + + // 벤더 코드 또는 이메일 중 하나는 있어야 함 + if (itemCode && (vendorCode || vendorEmail)) { + data.push({ + vendorCode: vendorCode || '', + vendorEmail: vendorEmail || '', + itemCode, + }); + } else { + // 불완전한 데이터 처리 + data.push({ + vendorCode: vendorCode || '', + vendorEmail: vendorEmail || '', + itemCode: itemCode || '', + }); + } + }); + + if (data.length === 0) { + return { + success: false, + totalRows: 0, + successCount: 0, + failedRows: [{ row: 0, error: "가져올 데이터가 없습니다. 템플릿 형식을 확인하세요." }], + }; + } + + // 서비스를 통해 import 실행 + const result = await importTechVendorPossibleItems(data); + + // 실패한 항목이 있으면 오류 파일 생성 + if (result.failedRows.length > 0) { + const errorFileUrl = await createErrorExcelFile(result.failedRows); + return { + ...result, + errorFileUrl, + }; + } + + return result; + } catch (error) { + console.error("Excel import 중 오류:", error); + return { + success: false, + totalRows: 0, + successCount: 0, + failedRows: [ + { + row: 0, + error: error instanceof Error ? error.message : "파일 처리 중 오류가 발생했습니다. 파일 형식을 확인하세요.", + }, + ], + }; + } +} + +/** + * 실패한 항목들을 포함한 오류 Excel 파일 생성 + */ +async function createErrorExcelFile( + failedRows: ImportResult['failedRows'] +): Promise { + try { + const workbook = new ExcelJS.Workbook(); + const worksheet = workbook.addWorksheet('Import 오류 목록'); + + // 헤더 설정 + worksheet.columns = [ + { header: '행 번호', key: 'row', width: 10 }, + { header: '아이템코드', key: 'itemCode', width: 20 }, + { header: '벤더코드', key: 'vendorCode', width: 15 }, + { header: '벤더이메일', key: 'vendorEmail', width: 30 }, + { header: '오류 내용', key: 'error', width: 60 }, + { header: '해결 방법', key: 'solution', width: 40 }, + ]; + + // 헤더 스타일 + const headerRow = worksheet.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' } + }; + }); + + // 오류 데이터 추가 + failedRows.forEach((item) => { + let solution = '시스템 관리자에게 문의하세요'; + + if (item.error.includes('벤더 코드') || item.error.includes('벤더 이메일')) { + solution = '등록된 벤더 코드 또는 이메일인지 확인하세요'; + } else if (item.error.includes('아이템 코드')) { + solution = '벤더 타입에 맞는 아이템 코드인지 확인하세요'; + } else if (item.error.includes('이미 존재')) { + solution = '중복된 조합입니다. 제거하거나 건너뛰세요'; + } + + const row = worksheet.addRow({ + row: item.row, + itemCode: item.itemCode || '누락', + vendorCode: item.vendorCode || '누락', + vendorEmail: item.vendorEmail || '누락', + error: item.error, + solution: solution, + }); + + row.eachCell((cell) => { + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + }); + }); + + // 안내사항 추가 + const instructionSheet = workbook.addWorksheet('오류 해결 가이드'); + const instructions = [ + ['📋 오류 유형별 해결 방법', ''], + ['', ''], + ['1. 벤더 코드/이메일 오류:', ''], + [' • 시스템에 등록된 벤더 코드 또는 이메일인지 확인', ''], + [' • 벤더 관리 메뉴에서 등록 상태 확인', ''], + [' • 벤더 코드가 없으면 벤더 이메일로 대체 가능', ''], + ['', ''], + ['2. 아이템 코드 오류:', ''], + [' • 벤더 타입과 일치하는 아이템인지 확인', ''], + [' • 조선 벤더 → item_shipbuilding 테이블', ''], + [' • 해양TOP 벤더 → item_offshore_top 테이블', ''], + [' • 해양HULL 벤더 → item_offshore_hull 테이블', ''], + ['', ''], + ['3. 중복 오류:', ''], + [' • 이미 등록된 벤더-아이템 조합', ''], + [' • 기존 데이터 확인 후 중복 제거', ''], + ['', ''], + ['📞 추가 문의: 시스템 관리자', ''], + ]; + + 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(':')) { + row.getCell(1).font = { bold: true, color: { argb: 'FF1F4E79' } }; + } + }); + + instructionSheet.getColumn(1).width = 50; + + // 파일 생성 및 다운로드 + const buffer = await workbook.xlsx.writeBuffer(); + const blob = new Blob([buffer], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + }); + + const fileName = `Import_오류_${new Date().toISOString().split('T')[0]}_${Date.now()}.xlsx`; + saveAs(blob, fileName); + + return fileName; + } catch (error) { + console.error("오류 파일 생성 중 오류:", error); + return ''; + } +} \ No newline at end of file -- cgit v1.2.3