"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 ''; } }