/* eslint-disable @typescript-eslint/no-explicit-any */ import { PrismaClient } from '@prisma/client'; import { ExcelReaderService } from './excel-reader'; import { BulkInserter } from './bulk-inserter'; import { ImportProgressServer } from './websocket-server'; import { ImportProgress, ImportResult } from './types'; export class CintasImportProcessor { 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 processCintasImport(importId: number): Promise { try { console.log(`[${new Date().toISOString()}] [CintasImport] Starting import processing for ID: ${importId}`); // Initialize the progress server if not already done if (!this.progressServer.isServerInitialized()) { this.progressServer.initialize(); } // Get import record with layout configuration const importRecord = await this.prisma.import.findUnique({ where: { id: importId }, include: { layout: { include: { sections: { include: { fields: true } } } } } }); // Get the file separately const file = importRecord?.fileId ? await this.prisma.file.findUnique({ where: { id: importRecord.fileId } }) : null; if (!importRecord || !file) { console.error(`[${new Date().toISOString()}] [CintasImport] ERROR: Import not found or no file attached`); throw new Error('Import not found or no file attached'); } console.log(`[${new Date().toISOString()}] [CintasImport] Loaded import record: ${importRecord.id}`); // 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 console.log(`[${new Date().toISOString()}] [CintasImport] Starting Excel file reading...`); const sections = await this.reader.readExcelFile( Buffer.from(file.data), importRecord.layout, (sectionProgress) => { this.progressServer.broadcastProgress(importId, sectionProgress); } ); console.log(`[${new Date().toISOString()}] [CintasImport] Excel file read successfully. Found ${sections.length} sections`); // Process each section const processedSections = []; let totalInserted = 0; for (let i = 0; i < sections.length; i++) { const section = sections[i]; console.log(`[${new Date().toISOString()}] [CintasImport] Processing section ${i+1}/${sections.length}: ${section.name}`); progress.currentSection = section.name; progress.processedSections = i + 1; this.progressServer.broadcastProgress(importId, progress); try { // Ensure table exists for this section console.log(`[${new Date().toISOString()}] [CintasImport] Creating table ${section.tableName} for section ${section.name}`); await this.inserter.createImportTable(section.tableName, section.fields); const insertedRows = await this.inserter.insertSectionData( section, importId, (rows) => { progress.currentRow = rows; this.progressServer.broadcastProgress(importId, progress); } ); processedSections.push({ sectionData: section, insertedRows }); totalInserted += insertedRows; console.log(`[${new Date().toISOString()}] [CintasImport] Completed section ${section.name}: ${insertedRows} rows inserted`); } catch (error) { const errorMessage = `Error processing section ${section.name}: ${error instanceof Error ? error.message : 'Unknown error'}`; progress.errors.push(errorMessage); console.error(`[${new Date().toISOString()}] [CintasImport] ERROR: ${errorMessage}`); this.progressServer.broadcastProgress(importId, progress); } } // Run the stored procedure to calculate summary console.log(`[${new Date().toISOString()}] [CintasImport] Running summary calculation procedure...`); try { await this.prisma.$executeRawUnsafe( `CALL cintas_calculate_summary(${importId})` ); console.log(`[${new Date().toISOString()}] [CintasImport] Summary calculation completed successfully`); } catch (error) { console.error(`[${new Date().toISOString()}] [CintasImport] ERROR: Summary calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); progress.errors.push(`Stored procedure error: ${error instanceof Error ? error.message : 'Unknown error'}`); } progress.status = 'completed'; this.progressServer.broadcastProgress(importId, progress); console.log(`[${new Date().toISOString()}] [CintasImport] Import processing completed successfully. Total inserted: ${totalInserted}`); return { success: true, totalInserted, sections: processedSections }; } catch (error) { 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); console.error(`[${new Date().toISOString()}] [CintasImport] ERROR: Import processing failed: ${error instanceof Error ? error.message : 'Unknown error'}`); 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[] = []; console.log(`[${new Date().toISOString()}] [CintasImport] Starting validation for import ID: ${importId}`); try { const importRecord = await this.prisma.import.findUnique({ where: { id: importId } }); if (!importRecord) { errors.push('Import record not found'); console.error(`[${new Date().toISOString()}] [CintasImport] Validation failed: Import record not found`); return { valid: false, errors }; } const file = importRecord.fileId ? await this.prisma.file.findUnique({ where: { id: importRecord.fileId } }) : null; if (!file) { errors.push('No file attached to import'); console.error(`[${new Date().toISOString()}] [CintasImport] Validation failed: No file attached`); } const layout = importRecord.layoutId ? await this.prisma.layoutConfiguration.findUnique({ where: { id: importRecord.layoutId } }) : null; if (!layout) { errors.push('No layout configuration found'); console.error(`[${new Date().toISOString()}] [CintasImport] Validation failed: No layout configuration`); } // Check if this is a Cintas layout if (layout?.name !== 'Cintas Install Calendar') { errors.push('This import is not configured for Cintas Install Calendar'); console.error(`[${new Date().toISOString()}] [CintasImport] Validation failed: Not a Cintas layout`); } console.log(`[${new Date().toISOString()}] [CintasImport] Validation completed: ${errors.length === 0 ? 'Valid' : 'Invalid'}`); return { valid: errors.length === 0, errors }; } catch (error) { errors.push(`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`); console.error(`[${new Date().toISOString()}] [CintasImport] Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`); return { valid: false, errors }; } } }