diff --git a/public/userarea/edit_template_xls.php b/public/userarea/edit_template_xls.php index 9677ba6..1ac0d71 100644 --- a/public/userarea/edit_template_xls.php +++ b/public/userarea/edit_template_xls.php @@ -207,6 +207,21 @@ if (!array_key_exists($currentButtonTextColor, array_change_key_case($buttonText +
+ + + + Attention: if left empty, the system will read the entire XLS/XLSX sheet. + Dirty Excel files may cause memory errors or timeout. + +
+
fetchAll(PDO::FETCH_ASSOC);
+
+ + + + Attention: if left empty, the system will read the entire XLS/XLSX sheet. + Dirty Excel files may cause memory errors or timeout. + +
+
fetchAll(PDO::FETCH_ASSOC); const headerRowWrapper = document.getElementById("headerRowWrapper"); const startColumnWrapper = document.getElementById("startColumnWrapper"); + const xlsEndColumnWrapper = document.getElementById("xlsEndColumnWrapper"); const xlsSheetNumberWrapper = document.getElementById("xlsSheetNumberWrapper"); const apiConfigWrapper = document.getElementById("apiConfigWrapper"); const headerRow = document.getElementById("headerRow"); const startColumn = document.getElementById("startColumn"); + const xlsEndColumn = document.getElementById("xlsEndColumn"); const xlsSheetIndex = document.getElementById("xlsSheetIndex"); const apiConfigSelect = document.getElementById("apiConfigSelect"); @@ -295,14 +312,20 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC); if (isXls) { headerRowWrapper.style.display = 'block'; startColumnWrapper.style.display = 'block'; + xlsEndColumnWrapper.style.display = 'block'; xlsSheetNumberWrapper.style.display = 'block'; headerRow.required = true; startColumn.required = true; xlsSheetIndex.required = true; + // Last Column is optional. + // If empty, the import will read the entire sheet. + xlsEndColumn.required = false; + headerRow.disabled = false; startColumn.disabled = false; + xlsEndColumn.disabled = false; xlsSheetIndex.disabled = false; apiConfigWrapper.style.display = 'none'; @@ -312,16 +335,21 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC); } else { headerRowWrapper.style.display = 'none'; startColumnWrapper.style.display = 'none'; + xlsEndColumnWrapper.style.display = 'none'; xlsSheetNumberWrapper.style.display = 'none'; headerRow.required = false; startColumn.required = false; + xlsEndColumn.required = false; xlsSheetIndex.required = false; headerRow.disabled = true; startColumn.disabled = true; + xlsEndColumn.disabled = true; xlsSheetIndex.disabled = true; + xlsEndColumn.value = ''; + if (isApiJson) { apiConfigWrapper.style.display = 'block'; apiConfigSelect.required = true; diff --git a/public/userarea/process_edit_template_xls.php b/public/userarea/process_edit_template_xls.php index e333d9a..8f3a654 100644 --- a/public/userarea/process_edit_template_xls.php +++ b/public/userarea/process_edit_template_xls.php @@ -4,6 +4,35 @@ require_once 'class/db-functions.php'; $response = ["success" => false, "message" => ""]; +function excelColumnToIndex($column) +{ + $column = strtoupper(trim((string)$column)); + + if ($column === '') { + return null; + } + + // Numeric column index, example: 40 + if (ctype_digit($column)) { + $index = (int)$column; + return $index > 0 ? $index : null; + } + + // Excel column letters, example: A, AN, XFC + if (!preg_match('/^[A-Z]+$/', $column)) { + return null; + } + + $index = 0; + $length = strlen($column); + + for ($i = 0; $i < $length; $i++) { + $index = ($index * 26) + (ord($column[$i]) - ord('A') + 1); + } + + return $index; +} + try { if ($_SERVER["REQUEST_METHOD"] !== "POST") { throw new Exception("Invalid request method."); @@ -19,6 +48,8 @@ try { : null; $start_column = trim($_POST['start_column'] ?? ''); + $xls_end_column = strtoupper(trim($_POST['xls_end_column'] ?? '')); + $xls_end_column = $xls_end_column !== '' ? $xls_end_column : null; $xls_sheet_index = isset($_POST['xls_sheet_index']) && $_POST['xls_sheet_index'] !== '' ? intval($_POST['xls_sheet_index']) @@ -60,6 +91,24 @@ try { throw new Exception("XLS Sheet Number cannot be negative."); } + $startColumnIndex = excelColumnToIndex($start_column); + + if ($startColumnIndex === null) { + throw new Exception("Start Column is not valid. Use Excel column letters like A, AN or a positive number."); + } + + if ($xls_end_column !== null) { + $endColumnIndex = excelColumnToIndex($xls_end_column); + + if ($endColumnIndex === null) { + throw new Exception("Last Column is not valid. Use Excel column letters like AN or a positive number."); + } + + if ($endColumnIndex < $startColumnIndex) { + throw new Exception("Last Column cannot be before Start Column."); + } + } + $api_config_id = null; } @@ -71,6 +120,7 @@ try { $header_row = null; $start_column = null; + $xls_end_column = null; $xls_sheet_index = null; } @@ -78,6 +128,7 @@ try { if ($source_type === 'PDF') { $header_row = null; $start_column = null; + $xls_end_column = null; $xls_sheet_index = null; $api_config_id = null; } @@ -109,6 +160,7 @@ try { source_type = ?, header_row = ?, start_column = ?, + xls_end_column = ?, xls_sheet_index = ?, api_config_id = ?, description = ?, @@ -131,6 +183,7 @@ try { $source_type, $header_row, $start_column, + $xls_end_column, $xls_sheet_index, $api_config_id, $description, diff --git a/public/userarea/process_import_xls2.php b/public/userarea/process_import_xls2.php index 407ea08..71c19b6 100644 --- a/public/userarea/process_import_xls2.php +++ b/public/userarea/process_import_xls2.php @@ -74,6 +74,7 @@ try { id, header_row, start_column, + xls_end_column, xls_sheet_index, idroutine, idclient @@ -93,6 +94,12 @@ try { $start_column_raw = $template['start_column'] ?? 'A'; $start_column = normalizeColumnIndex($start_column_raw); + $xls_end_column_raw = trim((string)($template['xls_end_column'] ?? '')); + $xls_end_column = $xls_end_column_raw !== '' ? normalizeColumnIndex($xls_end_column_raw) : 0; + + if ($xls_end_column > 0 && $xls_end_column < $start_column) { + throw new Exception("Last Column cannot be before Start Column."); + } $xlsSheetIndex = isset($template['xls_sheet_index']) && $template['xls_sheet_index'] !== null ? (int)$template['xls_sheet_index'] @@ -109,7 +116,15 @@ try { // 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"); + error_log( + "Template XLS settings - " . + "header_row: $header_row, " . + "start_column_raw: $start_column_raw, " . + "start_column_index: $start_column, " . + "xls_end_column_raw: " . ($xls_end_column_raw !== '' ? $xls_end_column_raw : '[empty = read all]') . ", " . + "xls_end_column_index: " . ($xls_end_column > 0 ? $xls_end_column : '[no limit]') . ", " . + "xls_sheet_index: $xlsSheetIndex" + ); $file = $_FILES['excel_file']; $fileError = $file['error']; @@ -161,8 +176,33 @@ try { 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); + // Load the XLS/XLSX file. + // If Last Column is configured in the template, load only the configured column range. + // If Last Column is empty, keep the original behavior and read the entire sheet. + $reader = IOFactory::createReaderForFile($destination); + $reader->setReadDataOnly(true); + + if ($xls_end_column > 0) { + $reader->setReadFilter(new class($start_column, $xls_end_column) implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { + private int $startColumn; + private int $endColumn; + + public function __construct(int $startColumn, int $endColumn) + { + $this->startColumn = $startColumn; + $this->endColumn = $endColumn; + } + + public function readCell($columnAddress, $row, $worksheetName = ''): bool + { + $columnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($columnAddress); + + return $columnIndex >= $this->startColumn && $columnIndex <= $this->endColumn; + } + }); + } + + $spreadsheet = $reader->load($destination); $sheetCount = $spreadsheet->getSheetCount(); $sheetNames = $spreadsheet->getSheetNames(); @@ -193,6 +233,12 @@ try { $highestColumn = $worksheet->getHighestColumn(); $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn); + // Force the effective highest column to Last Column, if configured. + if ($xls_end_column > 0) { + $highestColumnIndex = $xls_end_column; + $highestColumn = Coordinate::stringFromColumnIndex($highestColumnIndex); + } + $startRow = max(1, $header_row); $startColumn = max(1, $start_column); diff --git a/public/userarea/process_insert_template_xls.php b/public/userarea/process_insert_template_xls.php index e257656..389d76a 100644 --- a/public/userarea/process_insert_template_xls.php +++ b/public/userarea/process_insert_template_xls.php @@ -4,6 +4,35 @@ require_once 'class/db-functions.php'; $response = ["success" => false, "message" => ""]; +function excelColumnToIndex($column) +{ + $column = strtoupper(trim((string)$column)); + + if ($column === '') { + return null; + } + + // Numeric column index, example: 40 + if (ctype_digit($column)) { + $index = (int)$column; + return $index > 0 ? $index : null; + } + + // Excel column letters, example: A, AN, XFC + if (!preg_match('/^[A-Z]+$/', $column)) { + return null; + } + + $index = 0; + $length = strlen($column); + + for ($i = 0; $i < $length; $i++) { + $index = ($index * 26) + (ord($column[$i]) - ord('A') + 1); + } + + return $index; +} + try { if ($_SERVER["REQUEST_METHOD"] !== "POST") { throw new Exception("Invalid request method."); @@ -18,6 +47,8 @@ try { : null; $start_column = trim($_POST['start_column'] ?? ''); + $xls_end_column = strtoupper(trim($_POST['xls_end_column'] ?? '')); + $xls_end_column = $xls_end_column !== '' ? $xls_end_column : null; $xls_sheet_index = isset($_POST['xls_sheet_index']) && $_POST['xls_sheet_index'] !== '' ? intval($_POST['xls_sheet_index']) @@ -63,6 +94,24 @@ try { throw new Exception("XLS Sheet Number cannot be negative."); } + $startColumnIndex = excelColumnToIndex($start_column); + + if ($startColumnIndex === null) { + throw new Exception("Start Column is not valid. Use Excel column letters like A, AN or a positive number."); + } + + if ($xls_end_column !== null) { + $endColumnIndex = excelColumnToIndex($xls_end_column); + + if ($endColumnIndex === null) { + throw new Exception("Last Column is not valid. Use Excel column letters like AN or a positive number."); + } + + if ($endColumnIndex < $startColumnIndex) { + throw new Exception("Last Column cannot be before Start Column."); + } + } + $api_config_id = null; } @@ -75,6 +124,7 @@ try { $header_row = null; $start_column = null; $xls_sheet_index = null; + $xls_end_column = null; } // PDF currently does not require XLS coordinates or API configuration @@ -82,6 +132,7 @@ try { $header_row = null; $start_column = null; $xls_sheet_index = null; + $xls_end_column = null; $api_config_id = null; } @@ -112,6 +163,7 @@ try { source_type, header_row, start_column, + xls_end_column, xls_sheet_index, api_config_id, description, @@ -130,7 +182,7 @@ try { ) VALUES ( - ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW() @@ -142,6 +194,7 @@ try { $source_type, $header_row, $start_column, + $xls_end_column, $xls_sheet_index, $api_config_id, $description, diff --git a/public/userarea/schemi_base_response.json b/public/userarea/schemi_base_response.json index 4e39ea7..b7dd566 100644 --- a/public/userarea/schemi_base_response.json +++ b/public/userarea/schemi_base_response.json @@ -730,8 +730,8 @@ { "IdSchemaCustomFields": 177, "ConteggioClienti": 0, - "Nome": "Phoebe philo ACC", - "Descrizione": "(scarpe, borse, cinture, occhiali, gioielleria)\r\n" + "Nome": "Phoebe Philo ", + "Descrizione": "\r\n\r\n" }, { "IdSchemaCustomFields": 178,