From 56eee99a67e2d376550455ed29bdbf51986d9347 Mon Sep 17 00:00:00 2001 From: solocla Date: Sat, 9 May 2026 15:39:43 +0200 Subject: [PATCH] change template and import o use also sheet number of XLS --- public/userarea/import_xls2.php | 12 +- .../userarea/mapping_template_xls_scheme2.php | 60 ++++- public/userarea/process_import_xls2.php | 221 +++++++++++++++--- public/userarea/update_xls_headers.php | 84 +++++-- 4 files changed, 320 insertions(+), 57 deletions(-) diff --git a/public/userarea/import_xls2.php b/public/userarea/import_xls2.php index ad7fe20..dcf3202 100644 --- a/public/userarea/import_xls2.php +++ b/public/userarea/import_xls2.php @@ -167,7 +167,12 @@ error_log("Loaded template: " . print_r($template, true));
- Template ID: , Start Row: , Start Column: + + Template ID: , + Sheet Number: , + Start Row: , + Start Column: +
@@ -244,8 +249,9 @@ error_log("Loaded template: " . print_r($template, true)); const templateId = ; console.log('Template ID passed to formData:', templateId); formData.append('template_id', templateId); - formData.append('header_row', ); - formData.append('start_column', ); + formData.append('header_row', ); + formData.append('start_column', ); + formData.append('xls_sheet_index', ); fetch('process_import_xls2.php', { method: 'POST', diff --git a/public/userarea/mapping_template_xls_scheme2.php b/public/userarea/mapping_template_xls_scheme2.php index a9f8d92..028e9b1 100644 --- a/public/userarea/mapping_template_xls_scheme2.php +++ b/public/userarea/mapping_template_xls_scheme2.php @@ -15,8 +15,9 @@ $stmt = $pdo->prepare(" start_column, target_table, source_type, + xls_sheet_index, sample_xlsx, - idclient, + idclient, clientname, idschema, schemaname, @@ -39,6 +40,14 @@ if (!in_array($sourceType, ['XLS', 'API', 'PDF'], true)) { $sourceType = 'XLS'; } +$xlsSheetIndex = isset($template['xls_sheet_index']) && $template['xls_sheet_index'] !== null + ? (int)$template['xls_sheet_index'] + : 0; + +if ($xlsSheetIndex < 0) { + $xlsSheetIndex = 0; +} + $clientName = $template['clientname'] ?: ''; $schemaName = $template['schemaname'] ?: ''; $schemajson = $template['schemajson'] ? json_decode($template['schemajson'], true) : []; @@ -267,7 +276,8 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; Source: | - Header Row: | + Sheet Number: | + Header Row: | Start Column:

@@ -656,7 +666,30 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; let workbook = XLSX.read(data, { type: 'array' }); - let sheet = workbook.Sheets[workbook.SheetNames[0]]; + + const selectedSheetIndex = ; + + if (!workbook.SheetNames || workbook.SheetNames.length === 0) { + alert("No sheets found in this XLS file."); + return; + } + + if (!workbook.SheetNames[selectedSheetIndex]) { + alert( + "The selected sheet number " + selectedSheetIndex + + " does not exist in this XLS file. Available sheets: " + + workbook.SheetNames.map((name, index) => index + " = " + name).join(", ") + ); + return; + } + + const selectedSheetName = workbook.SheetNames[selectedSheetIndex]; + let sheet = workbook.Sheets[selectedSheetName]; + + console.log("Selected XLS sheet:", { + index: selectedSheetIndex, + name: selectedSheetName + }); // Read sheet range to determine column offset const sheetRange = XLSX.utils.decode_range(sheet['!ref'] || 'A1'); @@ -745,7 +778,8 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; const uniqueLabels = [...new Set(knownLabels)]; console.group('🔍 Auto-detect header row'); - console.log('Sheet name:', workbook.SheetNames[0]); + console.log('Sheet index:', selectedSheetIndex); + console.log('Sheet name:', selectedSheetName); console.log('Total rows in sheet:', sheetData.length); console.log('Labels from schema field titles:', knownLabels); console.log('Unique labels to match against:', uniqueLabels); @@ -884,8 +918,10 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; function saveXlsHeaders(headers, headerRow, startColumn) { const payload = { template_id: , - xls_headers: JSON.stringify(headers) + xls_headers: JSON.stringify(headers), + xls_sheet_index: }; + if (headerRow !== undefined) payload.header_row = headerRow; if (startColumn !== undefined) payload.start_column = startColumn; @@ -897,8 +933,18 @@ $apiSampleJson = $template['api_sample_json'] ?? ''; body: JSON.stringify(payload) }).then(response => response.json()) .then(data => { - if (!data.success) console.error("❌ Error saving XLS headers:", data.message); - else console.log("✅ Saved headers, header_row:", headerRow, "start_column:", startColumn); + if (!data.success) { + console.error("❌ Error saving XLS headers:", data.message); + } else { + console.log( + "✅ Saved headers, header_row:", + headerRow, + "start_column:", + startColumn, + "xls_sheet_index:", + + ); + } }) .catch(error => console.error("❌ Fetch error:", error)); } diff --git a/public/userarea/process_import_xls2.php b/public/userarea/process_import_xls2.php index afd0cf0..407ea08 100644 --- a/public/userarea/process_import_xls2.php +++ b/public/userarea/process_import_xls2.php @@ -11,17 +11,105 @@ session_start(); require_once '../../vendor/autoload.php'; require_once __DIR__ . '/class/db-functions.php'; -$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'apply_routine' => false]; +use PhpOffice\PhpSpreadsheet\IOFactory; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; + +$response = [ + 'error' => '', + '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; - $header_row = isset($_POST['header_row']) ? intval($_POST['header_row']) : 1; - $start_column = isset($_POST['start_column']) ? intval($_POST['start_column']) : 1; + + 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']; @@ -38,23 +126,32 @@ try { $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"); - // Connessione al database - $db = DBHandlerSelect::getInstance(); - $pdo = $db->getConnection(); - // 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 = $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); @@ -65,19 +162,45 @@ try { $response['error'] = "Nessun mapping trovato per il template con ID $template_id"; } else { // Carica il file rinominato con PHPSpreadsheet - $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($destination); - $worksheet = $spreadsheet->getActiveSheet(); + $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 = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn); + $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn); $startRow = max(1, $header_row); $startColumn = max(1, $start_column); - // Advance startColumn to first non-empty cell in header row (match JS behavior) + // Advance startColumn to first non-empty cell in header row, matching JS behavior for ($sc = $startColumn; $sc <= $highestColumnIndex; $sc++) { - $cl = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($sc); + $cl = Coordinate::stringFromColumnIndex($sc); $cv = trim((string)($worksheet->getCell($cl . $header_row)->getCalculatedValue() ?? '')); + if ($cv !== '') { $startColumn = $sc; break; @@ -85,24 +208,32 @@ try { } // Debug dei parametri - error_log("Processing - template_id: $template_id, startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex"); + 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)."; + $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)."; + $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 = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $startCell)); - $mEndCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $endCell)); + + $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; @@ -111,12 +242,17 @@ try { } // Build logical columns: each merge = one column - $logicalCols = []; // array of physical column indices (one per logical 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; + + if (in_array($ms, $seen, true)) { + continue; + } + $seen[] = $ms; $logicalCols[] = $ms; } else { @@ -127,38 +263,48 @@ try { // Build header row using logical columns $headerRowData = []; $logicalNum = 0; + foreach ($logicalCols as $physCol) { $logicalNum++; - $columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($physCol); + + $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)); + 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; + 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 = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($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++; @@ -166,17 +312,25 @@ try { } if ($filledCount >= $minFilled) { - $excelData[] = ['data' => $rowData, 'excelrow' => $row]; + $excelData[] = [ + 'data' => $rowData, + 'excelrow' => $row + ]; } } // Recupera routine dal template - $stmt = $pdo->prepare("SELECT idroutine, idclient FROM excel_templates WHERE id = ?"); - $stmt->execute([$template_id]); - $template = $stmt->fetch(PDO::FETCH_ASSOC); - - if ($template && $template['idroutine']) { - $stmtRoutine = $pdo->prepare("SELECT idroutine, name, filename, headerrow, instruction FROM routine WHERE idroutine = ?"); + 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); @@ -188,6 +342,7 @@ try { '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']}"); @@ -204,6 +359,8 @@ try { $_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; @@ -211,6 +368,8 @@ try { $response['columns'] = $headerRowData; $response['template_id'] = $template_id; $response['filename'] = $newFilename; + $response['xls_sheet_index'] = $xlsSheetIndex; + $response['xls_sheet_name'] = $selectedSheetName; } } } else { diff --git a/public/userarea/update_xls_headers.php b/public/userarea/update_xls_headers.php index 752d9af..d4c8bba 100644 --- a/public/userarea/update_xls_headers.php +++ b/public/userarea/update_xls_headers.php @@ -6,22 +6,57 @@ error_reporting(E_ALL); require_once(__DIR__ . '/class/db-functions.php'); -$db = DBHandlerSelect::getInstance(); -$pdo = $db->getConnection(); - -$data = json_decode(file_get_contents("php://input"), true); - -if (!$data || !isset($data['template_id'], $data['xls_headers'])) { - echo json_encode(["success" => false, "message" => "Invalid or missing data"]); - exit; -} - -$templateId = $data['template_id']; -$xlsHeaders = $data['xls_headers']; -$headerRow = isset($data['header_row']) ? (int)$data['header_row'] : null; -$startColumn = isset($data['start_column']) ? (int)$data['start_column'] : null; - try { + $db = DBHandlerSelect::getInstance(); + $pdo = $db->getConnection(); + + $data = json_decode(file_get_contents("php://input"), true); + + if (!$data || !isset($data['template_id'], $data['xls_headers'])) { + echo json_encode(["success" => false, "message" => "Invalid or missing data"]); + exit; + } + + $templateId = (int)$data['template_id']; + $xlsHeaders = $data['xls_headers']; + + $headerRow = isset($data['header_row']) && $data['header_row'] !== '' + ? (int)$data['header_row'] + : null; + + $startColumn = isset($data['start_column']) && $data['start_column'] !== '' + ? (int)$data['start_column'] + : null; + + $xlsSheetIndex = isset($data['xls_sheet_index']) && $data['xls_sheet_index'] !== '' + ? (int)$data['xls_sheet_index'] + : null; + + if ($templateId <= 0) { + echo json_encode(["success" => false, "message" => "Invalid template ID"]); + exit; + } + + if ($xlsHeaders === '') { + echo json_encode(["success" => false, "message" => "XLS headers cannot be empty"]); + exit; + } + + if ($headerRow !== null && $headerRow <= 0) { + echo json_encode(["success" => false, "message" => "Header row must be greater than 0"]); + exit; + } + + if ($startColumn !== null && $startColumn <= 0) { + echo json_encode(["success" => false, "message" => "Start column must be greater than 0"]); + exit; + } + + if ($xlsSheetIndex !== null && $xlsSheetIndex < 0) { + echo json_encode(["success" => false, "message" => "XLS sheet number cannot be negative"]); + exit; + } + $sql = "UPDATE excel_templates SET xls_headers = ?"; $params = [$xlsHeaders]; @@ -29,11 +64,18 @@ try { $sql .= ", header_row = ?"; $params[] = $headerRow; } + if ($startColumn !== null) { $sql .= ", start_column = ?"; $params[] = $startColumn; } + if ($xlsSheetIndex !== null) { + $sql .= ", xls_sheet_index = ?"; + $params[] = $xlsSheetIndex; + } + + $sql .= ", updated_at = NOW()"; $sql .= " WHERE id = ?"; $params[] = $templateId; @@ -45,8 +87,18 @@ try { exit; } - echo json_encode(["success" => true, "message" => "XLS headers saved successfully"]); + echo json_encode([ + "success" => true, + "message" => "XLS headers saved successfully", + "debug" => [ + "template_id" => $templateId, + "header_row" => $headerRow, + "start_column" => $startColumn, + "xls_sheet_index" => $xlsSheetIndex + ] + ]); } catch (Exception $e) { echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]); } + exit;