'', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'apply_routine' => false ]; /** * Converts a column value to a PhpSpreadsheet 1-based column index. * Accepted values: * - "A" => 1 * - "B" => 2 * - "AA" => 27 * - "1" => 1 * - 1 => 1 */ function normalizeColumnIndex($value): int { $value = trim((string)$value); if ($value === '') { return 1; } if (ctype_digit($value)) { return max(1, (int)$value); } $value = strtoupper($value); if (preg_match('/^[A-Z]+$/', $value)) { return Coordinate::columnIndexFromString($value); } return 1; } try { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['excel_file'])) { $template_id = isset($_POST['template_id']) ? intval($_POST['template_id']) : 0; if ($template_id <= 0) { throw new Exception("Template ID non valido."); } // Connessione al database $db = DBHandlerSelect::getInstance(); $pdo = $db->getConnection(); /* * Recuperiamo i parametri direttamente dal template. * Così non dipendiamo solo dal form e siamo sicuri di usare i dati salvati. */ $stmt = $pdo->prepare(" SELECT id, header_row, start_column, xls_sheet_index, idroutine, idclient FROM excel_templates WHERE id = ? "); $stmt->execute([$template_id]); $template = $stmt->fetch(PDO::FETCH_ASSOC); if (!$template) { throw new Exception("Template non trovato."); } $header_row = isset($template['header_row']) && $template['header_row'] !== null ? (int)$template['header_row'] : 1; $start_column_raw = $template['start_column'] ?? 'A'; $start_column = normalizeColumnIndex($start_column_raw); $xlsSheetIndex = isset($template['xls_sheet_index']) && $template['xls_sheet_index'] !== null ? (int)$template['xls_sheet_index'] : 0; if ($header_row <= 0) { $header_row = 1; } if ($xlsSheetIndex < 0) { $xlsSheetIndex = 0; } // Debug del template_id ricevuto error_log("Received template_id from POST: " . print_r($_POST['template_id'], true)); error_log("Converted template_id: $template_id"); error_log("Template XLS settings - header_row: $header_row, start_column_raw: $start_column_raw, start_column_index: $start_column, xls_sheet_index: $xlsSheetIndex"); $file = $_FILES['excel_file']; $fileError = $file['error']; if ($fileError === UPLOAD_ERR_OK) { // Recupera l'ID dell'utente loggato if (!isset($iduserlogin)) { $iduserlogin = 1; error_log("Warning: iduserlogin non definito, usando 1 come default"); } // Genera il nome del file rinominato $timestamp = date('YmdHis'); $originalFilename = basename($file['name']); $newFilename = "{$iduserlogin}-{$timestamp}-{$originalFilename}"; $importFolder = __DIR__ . '/imported_trf/'; if (!file_exists($importFolder)) { mkdir($importFolder, 0777, true); } $destination = $importFolder . $newFilename; // Sposta il file if (!move_uploaded_file($file['tmp_name'], $destination)) { throw new Exception("Errore durante lo spostamento del file in $destination"); } error_log("File spostato con successo in: $destination"); // Recupera il mapping da template_mapping $stmt = $pdo->prepare(" SELECT field_id AS excel_column, field_id AS mysql_column, data_type, is_required, default_value, is_manual FROM template_mapping WHERE template_id = ? "); $stmt->execute([$template_id]); $mappings = $stmt->fetchAll(PDO::FETCH_ASSOC); // Debug dei mapping error_log("Mappings found for template_id $template_id: " . print_r($mappings, true)); if (empty($mappings)) { $response['error'] = "Nessun mapping trovato per il template con ID $template_id"; } else { // Carica il file rinominato con PHPSpreadsheet $spreadsheet = IOFactory::load($destination); $sheetCount = $spreadsheet->getSheetCount(); $sheetNames = $spreadsheet->getSheetNames(); if ($sheetCount <= 0) { throw new Exception("Il file XLS non contiene fogli."); } if ($xlsSheetIndex >= $sheetCount) { throw new Exception( "Il foglio XLS selezionato non esiste. " . "Sheet Number selezionato: {$xlsSheetIndex}. " . "Fogli disponibili: " . implode(", ", array_map( fn($name, $index) => "{$index}={$name}", $sheetNames, array_keys($sheetNames) )) ); } // Usa il foglio configurato nel template $worksheet = $spreadsheet->getSheet($xlsSheetIndex); $selectedSheetName = $worksheet->getTitle(); error_log("Selected XLS sheet - index: {$xlsSheetIndex}, name: {$selectedSheetName}"); $highestRow = $worksheet->getHighestRow(); $highestColumn = $worksheet->getHighestColumn(); $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn); $startRow = max(1, $header_row); $startColumn = max(1, $start_column); // Advance startColumn to first non-empty cell in header row, matching JS behavior for ($sc = $startColumn; $sc <= $highestColumnIndex; $sc++) { $cl = Coordinate::stringFromColumnIndex($sc); $cv = trim((string)($worksheet->getCell($cl . $header_row)->getCalculatedValue() ?? '')); if ($cv !== '') { $startColumn = $sc; break; } } // Debug dei parametri error_log( "Processing - template_id: $template_id, " . "sheetIndex: $xlsSheetIndex, sheetName: $selectedSheetName, " . "startRow: $startRow, startColumn: $startColumn, " . "highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex" ); // Validazione degli indici if ($startRow > $highestRow) { $response['error'] = "La riga di partenza ($startRow) supera il numero totale di righe ($highestRow) del foglio '$selectedSheetName'."; } elseif ($startColumn > $highestColumnIndex) { $response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex) del foglio '$selectedSheetName'."; } else { $excelData = []; // Build merge map for header row: physCol -> mergeStartCol $mergeStartMap = []; foreach ($worksheet->getMergeCells() as $range) { [$startCell, $endCell] = explode(':', $range); $mStartCol = Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $startCell)); $mEndCol = Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $endCell)); $mStartRow = (int)preg_replace('/[A-Z]+/i', '', $startCell); $mEndRow = (int)preg_replace('/[A-Z]+/i', '', $endCell); if ($header_row >= $mStartRow && $header_row <= $mEndRow) { for ($c = $mStartCol; $c <= $mEndCol; $c++) { $mergeStartMap[$c] = $mStartCol; } } } // Build logical columns: each merge = one column $logicalCols = []; // array of physical column indices, one per logical column $seen = []; for ($col = $startColumn; $col <= $highestColumnIndex; $col++) { if (isset($mergeStartMap[$col])) { $ms = $mergeStartMap[$col]; if (in_array($ms, $seen, true)) { continue; } $seen[] = $ms; $logicalCols[] = $ms; } else { $logicalCols[] = $col; } } // Build header row using logical columns $headerRowData = []; $logicalNum = 0; foreach ($logicalCols as $physCol) { $logicalNum++; $columnLetter = Coordinate::stringFromColumnIndex($physCol); $cell = $worksheet->getCell($columnLetter . $header_row); $cellValue = trim((string)($cell ? $cell->getCalculatedValue() : '')); $cellValue = preg_replace('/[\r\n\t]+/', ' ', $cellValue); // Empty headers get __empty_N__ to match mapping page $headerRowData[] = ($cellValue !== '') ? $cellValue : '__empty_' . $logicalNum . '__'; } error_log("Logical headers: " . json_encode($headerRowData)); error_log("Logical cols physical indices: " . json_encode($logicalCols)); // Find which logical columns have real headers $headerFilledIndices = []; foreach ($headerRowData as $idx => $hVal) { if (!str_starts_with($hVal, '__empty_')) { $headerFilledIndices[] = $idx; } } $minFilled = max(1, min(2, count($headerFilledIndices))); // Extract data rows using logical columns for ($row = $startRow + 1; $row <= $highestRow; $row++) { $rowData = []; foreach ($logicalCols as $physCol) { $columnLetter = Coordinate::stringFromColumnIndex($physCol); $cell = $worksheet->getCell($columnLetter . $row); $cellValue = $cell ? $cell->getCalculatedValue() : ''; $rowData[] = $cellValue ?: ''; } // Count how many header columns have data in this row $filledCount = 0; foreach ($headerFilledIndices as $idx) { if (isset($rowData[$idx]) && trim((string)$rowData[$idx]) !== '') { $filledCount++; } } if ($filledCount >= $minFilled) { $excelData[] = [ 'data' => $rowData, 'excelrow' => $row ]; } } // Recupera routine dal template if ($template && !empty($template['idroutine'])) { $stmtRoutine = $pdo->prepare(" SELECT idroutine, name, filename, headerrow, instruction FROM routine WHERE idroutine = ? "); $stmtRoutine->execute([$template['idroutine']]); $routineData = $stmtRoutine->fetch(PDO::FETCH_ASSOC); if ($routineData) { $response['apply_routine'] = true; $response['routine_data'] = [ 'name' => $routineData['name'] ?? 'Routine Sconosciuta', 'instruction' => $routineData['instruction'] ?? 'Nessuna descrizione disponibile', 'filename' => $routineData['filename'] ?? '', 'headerrow' => $routineData['headerrow'] ?? $header_row ]; error_log("Routine rilevata per template {$template_id}: " . print_r($routineData, true)); } else { error_log("Errore: Nessuna routine trovata per idroutine {$template['idroutine']}"); } } else { error_log("Nessuna routine associata al template {$template_id}"); } // Aggiungi idclient alla risposta $response['idclient'] = $template['idclient'] ?? null; // Salva i dati in sessione $_SESSION['excel_data'] = $excelData; $_SESSION['template_id'] = $template_id; $_SESSION['headers'] = $headerRowData; $_SESSION['mappings'] = $mappings; $_SESSION['xls_sheet_index'] = $xlsSheetIndex; $_SESSION['xls_sheet_name'] = $selectedSheetName; // Includi excel_data nella risposta JSON in ogni caso $response['excel_data'] = $excelData; $response['rows'] = array_column($excelData, 'data'); $response['columns'] = $headerRowData; $response['template_id'] = $template_id; $response['filename'] = $newFilename; $response['xls_sheet_index'] = $xlsSheetIndex; $response['xls_sheet_name'] = $selectedSheetName; } } } else { $response['error'] = "Errore nell'upload del file: Codice errore $fileError."; } } else { $response['error'] = "Richiesta non valida."; } } catch (Exception $e) { $response['error'] = "Errore durante il caricamento del file: " . $e->getMessage(); error_log("Exception in process_import_xls2.php: " . $e->getMessage()); } // Pulisce qualsiasi output indesiderato ob_end_clean(); // Invia la risposta JSON header('Content-Type: application/json'); echo json_encode($response); exit;