import { PrismaClient } from '@prisma/client'; import { ExcelReaderService } from './excel-reader'; import { BulkInserter } from './bulk-inserter'; import { ImportProgressServer } from './websocket-server'; import { ImportProgress, ImportResult, ProcessedSection } from './types'; export class ImportProcessor { private prisma: PrismaClient; private reader: ExcelReaderService; private inserter: BulkInserter; private progressServer: ImportProgressServer; constructor() { this.prisma = new PrismaClient(); this.reader = new ExcelReaderService(); this.inserter = new BulkInserter(); this.progressServer = ImportProgressServer.getInstance(); } async processImport(importId: number): Promise { console.log('[IMPORT_PROCESSOR] 🔷 Starting processImport for importId:', importId); try { // Get import record with layout configuration const importRecord = await this.prisma.import.findUnique({ where: { id: importId }, include: { layout: { include: { sections: { include: { fields: true } } } } } }); console.log('[IMPORT_PROCESSOR] 📄 Import record:', { id: importRecord?.id, name: importRecord?.name, hasFileId: !!importRecord?.fileId, layoutName: importRecord?.layout?.name, sectionsCount: importRecord?.layout?.sections?.length }); if (!importRecord) { console.error('[IMPORT_PROCESSOR] ❌ Import not found'); throw new Error('Import not found'); } if (!importRecord.fileId) { console.error('[IMPORT_PROCESSOR] ❌ No file attached to import'); throw new Error('No file attached to import'); } // Get the actual file const fileRecord = await this.prisma.file.findUnique({ where: { id: importRecord.fileId } }); console.log('[IMPORT_PROCESSOR] 📁 File record:', { id: fileRecord?.id, filename: fileRecord?.filename, size: fileRecord?.size, mimetype: fileRecord?.mimetype }); if (!fileRecord) { console.error('[IMPORT_PROCESSOR] ❌ File not found'); throw new Error('File not found'); } // Initialize progress tracking const progress: ImportProgress = { importId, status: 'processing', currentSection: '', currentRow: 0, totalRows: 0, errors: [], processedSections: 0, totalSections: importRecord.layout?.sections?.length ?? 0 }; console.log('[IMPORT_PROCESSOR] 📊 Initial progress:', { totalSections: progress.totalSections }); // Read Excel file console.log('[IMPORT_PROCESSOR] 📖 Starting to read Excel file...'); const sections = await this.reader.readExcelFile( fileRecord.data, importRecord.layout, (sectionProgress: ImportProgress) => { this.progressServer.broadcastProgress(importId, sectionProgress); } ); console.log('[IMPORT_PROCESSOR] 📖 Excel read complete. Sections parsed:', sections.length); sections.forEach((section, idx) => { console.log(`[IMPORT_PROCESSOR] Section ${idx + 1}:`, { name: section.name, tableName: section.tableName, dataRows: section.data?.length }); }); // Process each section const processedSections: ProcessedSection[] = []; let totalInserted = 0; console.log('[IMPORT_PROCESSOR] 🔄 Starting to process', sections.length, 'sections...'); for (let i = 0; i < sections.length; i++) { const section = sections[i]; if (section) { progress.currentSection = section.name ?? ''; progress.processedSections = i + 1; this.progressServer.broadcastProgress(importId, progress); console.log(`[IMPORT_PROCESSOR] 📥 Processing section ${i + 1}/${sections.length}:`, { name: section.name, tableName: section.tableName, dataRows: section.data?.length }); try { const insertedRows = await this.inserter.insertSectionData( section, importId, (rows: number) => { progress.currentRow = rows; this.progressServer.broadcastProgress(importId, progress); } ); console.log(`[IMPORT_PROCESSOR] ✅ Section ${section.name} inserted ${insertedRows} rows`); processedSections.push({ sectionData: section, insertedRows }); totalInserted += insertedRows; } catch (error: unknown) { const errorMessage = `Error processing section ${section.name ?? 'unknown'}: ${error instanceof Error ? error.message : 'Unknown error'}`; console.error('[IMPORT_PROCESSOR] ❌', errorMessage); progress.errors.push(errorMessage); this.progressServer.broadcastProgress(importId, progress); } } } // Import processing completed progress.status = 'completed'; this.progressServer.broadcastProgress(importId, progress); console.log('[IMPORT_PROCESSOR] ✅ Import processing completed:', { totalInserted, sectionsProcessed: processedSections.length, errors: progress.errors.length }); return { success: true, totalInserted, sections: processedSections }; } catch (error) { // Import processing failed const progress: ImportProgress = { importId, status: 'failed', currentSection: '', currentRow: 0, totalRows: 0, errors: [error instanceof Error ? error.message : 'Unknown error'], processedSections: 0, totalSections: 0 }; console.error('[IMPORT_PROCESSOR] ❌ Import processing failed:', error); this.progressServer.broadcastProgress(importId, progress); return { success: false, totalInserted: 0, sections: [], errors: [error instanceof Error ? error.message : 'Unknown error'] }; } } async validateImport(importId: number): Promise<{ valid: boolean; errors: string[] }> { const errors: string[] = []; try { const importRecord = await this.prisma.import.findUnique({ where: { id: importId }, include: { layout: true } }); if (!importRecord) { errors.push('Import record not found'); return { valid: false, errors }; } if (!importRecord.fileId) { errors.push('No file attached to import'); } if (!importRecord.layout) { errors.push('No layout configuration found'); } return { valid: errors.length === 0, errors }; } catch (error) { errors.push(`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`); return { valid: false, errors }; } } }