summaryrefslogtreecommitdiff
path: root/lib/tech-vendor-possible-items/table/excel-import.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tech-vendor-possible-items/table/excel-import.tsx')
-rw-r--r--lib/tech-vendor-possible-items/table/excel-import.tsx220
1 files changed, 220 insertions, 0 deletions
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<ExcelImportResult> {
+ 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<string> {
+ 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