false, 'message' => '']; try { if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['iddatadb'])) { throw new Exception('Richiesta non valida'); } $iddatadb = intval($_POST['iddatadb']); $idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null; $clienteFornitoreId = isset($_POST['cliente_fornitore_id']) ? (is_numeric($_POST['cliente_fornitore_id']) ? intval($_POST['cliente_fornitore_id']) : null) : null; $testedComponent = isset($_POST['tested_component']) ? trim((string)$_POST['tested_component']) : null; $db = DBHandlerSelect::getInstance(); $pdo = $db->getConnection(); // ---------------- FIXED FIELDS (template_fixed_mapping) ---------------- // ALIAS: fixed_field_key "logico" -> colonna reale su datadb // (NON tocchiamo MySQL, gestiamo solo qui la traduzione) $fixedAliasMap = [ 'ClienteResponsabile' => 'cliente_responsabile_id', 'ClienteFornitore' => 'cliente_fornitore_id', 'ClienteAnalisi' => 'clienteAnalisi', 'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id', 'AnagraficaCertestObject' => 'anagrafica_certest_object_id', 'AnagraficaCertestService' => 'anagrafica_certest_service_id', 'ConsegnaRichiesta' => 'consegna_richiesta', ]; // 1) Recupera templateid dalla riga datadb (serve per sapere quali fixed_field_key sono permessi) $stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = ?"); $stmtTpl->execute([$iddatadb]); $tplRow = $stmtTpl->fetch(PDO::FETCH_ASSOC); $templateId = isset($tplRow['templateid']) ? (int)$tplRow['templateid'] : 0; if ($templateId <= 0) { throw new Exception("Template non trovato per iddatadb=$iddatadb"); } // 2) Recupera elenco fixed fields visibili per quel template $fxStmt = $pdo->prepare(" SELECT fixed_field_key, data_type, is_required, default_value FROM template_fixed_mapping WHERE template_id = ? AND is_visible_import = 1 "); $fxStmt->execute([$templateId]); $fixedList = $fxStmt->fetchAll(PDO::FETCH_ASSOC); // 3) Crea whitelist LOGICA: fixed_field_key => metadata $fixedWhitelist = []; foreach ($fixedList as $fx) { $k = (string)$fx['fixed_field_key']; // sicurezza: key ammessa solo se "safe" if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) { continue; } $fixedWhitelist[$k] = [ 'data_type' => (string)$fx['data_type'], // INT | DATE 'is_required' => (int)$fx['is_required'], 'default_value' => $fx['default_value'] ?? null ]; } // 3b) Crea whitelist REALE: colonna datadb reale => metadata $realWhitelist = []; foreach ($fixedWhitelist as $logicalKey => $meta) { $realCol = $fixedAliasMap[$logicalKey] ?? $logicalKey; // sicurezza: anche la colonna reale deve essere "safe" if (!preg_match('/^[a-zA-Z0-9_]+$/', $realCol)) { continue; } $realWhitelist[$realCol] = $meta; } // ---------------- DETAILS (import_data_details) ---------------- $data = $_POST; $details = []; // 1. Estrarre i dettagli da POST foreach ($data as $key => $value) { if (preg_match('/^details(\d+)field_value$/', $key, $matches)) { $id = $matches[1]; $details[$id] = $value; } } // 2. Recupera i valori esistenti da import_data_details $stmt = $pdo->prepare("SELECT mapping_id, field_value FROM import_data_details WHERE id = ?"); $stmt->execute([$iddatadb]); $currentValues = []; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $currentValues[$row['mapping_id']] = $row['field_value']; } // 3. Confronta i valori nuovi con quelli esistenti $changed = []; foreach ($details as $id => $newValue) { $oldValue = $currentValues[$id] ?? null; if ($oldValue !== $newValue) { $changed[$id] = [ 'old' => $oldValue, 'new' => $newValue ]; } } // 4. Aggiorna i dettagli se ci sono modifiche if (!empty($changed)) { $updateStmt = $pdo->prepare(" UPDATE import_data_details SET field_value = :newValue WHERE id = :iddatadb AND mapping_id = :mappingId "); foreach ($changed as $mappingId => $values) { $updateStmt->execute([ ':newValue' => $values['new'], ':iddatadb' => $iddatadb, ':mappingId' => $mappingId ]); } } // ---------------- UPDATE datadb: idclient + FIXED FIELDS ---------------- $setParts = []; $params = []; // 5a) idclient (se presente) if (isset($idclient)) { $setParts[] = "idclient = ?"; $params[] = $idclient; } // 5a2) cliente_fornitore_id (se presente) if (isset($clienteFornitoreId)) { $setParts[] = "cliente_fornitore_id = ?"; $params[] = $clienteFornitoreId; } // 5a3) tested_component (se presente) if (isset($testedComponent)) { $setParts[] = "tested_component = ?"; $params[] = ($testedComponent === '') ? null : $testedComponent; } // 5b) fixed fields dal POST // QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist), // ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale // perché sotto controlliamo anche l'alias. foreach ($realWhitelist as $realCol => $meta) { $postKeyToRead = null; // Caso 1: POST contiene già la colonna reale if (array_key_exists($realCol, $_POST)) { $postKeyToRead = $realCol; } else { // Caso 2: POST contiene la key logica -> troviamo quale logica mappa su questa colonna reale // (fallback, utile se non hai ancora aggiornato il JS) foreach ($fixedAliasMap as $logical => $mappedReal) { if ($mappedReal === $realCol && array_key_exists($logical, $_POST)) { $postKeyToRead = $logical; break; } } } if ($postKeyToRead === null) { continue; // non inviato } $val = $_POST[$postKeyToRead]; // Normalizzazione per tipo if ($meta['data_type'] === 'DATE') { $val = trim((string)$val); $val = ($val === '') ? null : $val; // atteso formato Y-m-d } else { // INT $val = trim((string)$val); $val = ($val === '') ? null : (int)$val; } $setParts[] = "`$realCol` = ?"; $params[] = $val; } // esegui update solo se c'è qualcosa da aggiornare if (!empty($setParts)) { $params[] = $iddatadb; $sqlUpd = "UPDATE datadb SET " . implode(", ", $setParts) . " WHERE iddatadb = ?"; $updStmt = $pdo->prepare($sqlUpd); $updStmt->execute($params); } // Messaggio risposta (mantengo la tua logica ma includo fixed) if (!empty($setParts) && !empty($changed)) { $response['message'] = "Updated details and datadb fields successfully"; } elseif (!empty($setParts)) { $response['message'] = "Updated datadb fields successfully"; } elseif (!empty($changed)) { $response['message'] = "Updated details successfully"; } else { $response['message'] = "No changes found"; } $response['success'] = true; $response['changed'] = $changed; // Debug / optional } catch (Exception $e) { $response['success'] = false; $response['message'] = $e->getMessage(); error_log("Errore in save_edited_row.php: " . $e->getMessage()); } echo json_encode($response); exit;