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 { try { // Get import record with layout configuration const importRecord = await this.prisma.import.findUnique({ where: { id: importId }, include: { layout: { include: { sections: { include: { fields: true } } } }, file: true } }); if (!importRecord || !importRecord.file) { throw new Error('Import not found or no file attached'); } // Initialize progress tracking const progress: ImportProgress = { importId, status: 'processing', currentSection: '', currentRow: 0, totalRows: 0, errors: [], processedSections: 0, totalSections: importRecord.layout?.sections?.length || 0 }; // Read Excel file const sections = await this.reader.readExcelFile( Buffer.from(importRecord.file.data), importRecord.layout, (sectionProgress) => { this.progressServer.broadcastProgress(importId, sectionProgress); } ); // Process each section const processedSections: ProcessedSection[] = []; let totalInserted = 0; for (let i = 0; i < sections.length; i++) { const section = sections[i]; progress.currentSection = section.name; progress.processedSections = i + 1; this.progressServer.broadcastProgress(importId, progress); try { const insertedRows = await this.inserter.insertSectionData( section, importId, (rows) => { progress.currentRow = rows; this.progressServer.broadcastProgress(importId, progress); } ); processedSections.push({ sectionData: section, insertedRows }); totalInserted += insertedRows; } catch (error) { const errorMessage = `Error processing section ${section.name}: ${error instanceof Error ? error.message : 'Unknown error'}`; progress.errors.push(errorMessage); this.progressServer.broadcastProgress(importId, progress); } } // Import processing completed - no status field to update progress.status = 'completed'; this.progressServer.broadcastProgress(importId, progress); return { success: true, totalInserted, sections: processedSections }; } catch (error) { // Import processing failed - no status field to update const progress: ImportProgress = { importId, status: 'failed', currentSection: '', currentRow: 0, totalRows: 0, errors: [error instanceof Error ? error.message : 'Unknown error'], processedSections: 0, totalSections: 0 }; 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: { file: true, layout: true } }); if (!importRecord) { errors.push('Import record not found'); return { valid: false, errors }; } if (!importRecord.file) { 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 }; } } }