# Pre-Discovery: Aggiornamento Forzato ## πŸ“‹ Requisito Implementato Quando il **Pre-Discovery** trova un record esistente nella destinazione e crea una nuova associazione, il sistema deve **forzare l'aggiornamento** del record **saltando il controllo hash**. ## 🎯 Rationale Quando un'associazione viene creata dal Pre-Discovery significa che: 1. βœ… Il record **esiste nella destinazione** Salesforce 2. ❌ **Non esisteva** un'associazione in `KeyAssociations` 3. πŸ” I dati potrebbero essere **diversi** tra sorgente e destinazione 4. πŸ”„ Vogliamo **sincronizzare** i dati al primo trasferimento Quindi ha senso **forzare l'aggiornamento** senza controllare l'hash, per assicurarci che i dati siano allineati. ## πŸ”§ Implementazione ### Identificazione Associazioni Pre-Discovery Le associazioni create dal Pre-Discovery hanno un marker nel campo `AdditionalInfo`: ```json { "CreatedBy": "PreDiscovery", "DiscoveredAt": "2025-10-21T10:30:00Z", "MappingCount": 5 } ``` ### Logica di Controllo Il sistema verifica il campo `AdditionalInfo` per determinare se l'associazione proviene dal Pre-Discovery: ```csharp // Verifica se l'associazione Γ¨ stata creata dal Pre-Discovery var isPreDiscoveryAssociation = false; if (!string.IsNullOrEmpty(existingAssociation.AdditionalInfo)) { try { var additionalInfo = JsonSerializer.Deserialize>(existingAssociation.AdditionalInfo); if (additionalInfo != null && additionalInfo.ContainsKey("CreatedBy")) { var createdBy = additionalInfo["CreatedBy"]?.ToString(); isPreDiscoveryAssociation = createdBy == "PreDiscovery"; } } catch { // Ignora errori di parsing } } ``` ### Flusso di Decisione ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Esiste associazione attiva? β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”œβ”€ NO ──> Crea nuovo record β”‚ └─ SI ──> È da Pre-Discovery? β”‚ β”œβ”€ SI ──> βœ… FORZA AGGIORNAMENTO (skip hash) β”‚ └─ NO ──> Controlla hash β”‚ β”œβ”€ Hash identico ──> Skip record β”‚ └─ Hash diverso ──> Aggiorna record ``` ### Codice Modificato **File**: `Data_Coupler/Pages/DataCoupler.razor.cs` **Metodo**: `StartDataTransferWithComposite()` (linea ~2840-2890) ```csharp if (existingAssociation != null && existingAssociation.IsActive) { // Verifica se l'associazione Γ¨ stata creata dal Pre-Discovery var isPreDiscoveryAssociation = false; if (!string.IsNullOrEmpty(existingAssociation.AdditionalInfo)) { try { var additionalInfo = System.Text.Json.JsonSerializer.Deserialize>(existingAssociation.AdditionalInfo); if (additionalInfo != null && additionalInfo.ContainsKey("CreatedBy")) { var createdBy = additionalInfo["CreatedBy"]?.ToString(); isPreDiscoveryAssociation = createdBy == "PreDiscovery"; } } catch { // Ignora errori di parsing } } // πŸ” PRE-DISCOVERY: Se l'associazione Γ¨ stata appena creata dal Pre-Discovery, FORZA l'aggiornamento if (isPreDiscoveryAssociation) { // Forza aggiornamento senza controllo hash recordsForUpdate.Add((restData, existingAssociation.DestinationId, record, recordNumber, currentDataHash)); Logger.LogInformation("COMPOSITE PARALLEL: Record {RecordNumber} marcato per AGGIORNAMENTO FORZATO (Pre-Discovery) - EntityId: {EntityId}", recordNumber, existingAssociation.DestinationId); } else { // CONTROLLO HASH: Verifica se i dati sono cambiati (solo per associazioni esistenti) var existingHash = existingAssociation.Data_Hash; if (!string.IsNullOrEmpty(existingHash) && existingHash.Equals(currentDataHash, StringComparison.OrdinalIgnoreCase)) { // I dati non sono cambiati, salta questo record recordsSkipped.Add((record, recordNumber, "Dati non modificati (hash identico)")); Logger.LogDebug("COMPOSITE PARALLEL: Record {RecordNumber} saltato - hash identico: {Hash}", recordNumber, currentDataHash); } else { // I dati sono cambiati o l'hash Γ¨ vuoto, procedi con l'aggiornamento recordsForUpdate.Add((restData, existingAssociation.DestinationId, record, recordNumber, currentDataHash)); Logger.LogDebug("COMPOSITE PARALLEL: Record {RecordNumber} marcato per aggiornamento (EntityId: {EntityId}) - hash diverso: old={OldHash}, new={NewHash}", recordNumber, existingAssociation.DestinationId, existingHash ?? "NULL", currentDataHash); } } } ``` ## πŸ“Š Logging Il sistema ora produce log specifici per aggiornamenti forzati: ```log COMPOSITE PARALLEL: Record 42 marcato per AGGIORNAMENTO FORZATO (Pre-Discovery) - EntityId: 003xxxxxxxxxxxxx ``` vs. aggiornamenti normali: ```log COMPOSITE PARALLEL: Record 42 marcato per aggiornamento (EntityId: 003xxxxxxxxxxxxx) - hash diverso: old=abc123, new=def456 ``` ## 🎯 Comportamento Atteso ### Scenario 1: Prima Esecuzione con Pre-Discovery **Setup**: - ❌ Tabella `KeyAssociations` vuota - βœ… Record giΓ  presenti in Salesforce **Risultato**: 1. Pre-Discovery trova i record esistenti 2. Crea associazioni con `CreatedBy: "PreDiscovery"` 3. **Forza l'aggiornamento** di tutti i record trovati (ignora hash) 4. Aggiorna `Data_Hash` con il nuovo valore 5. Record sincronizzati tra sorgente e destinazione ### Scenario 2: Seconda Esecuzione (Associazioni Esistenti) **Setup**: - βœ… Tabella `KeyAssociations` popolata (dalla prima esecuzione) - βœ… Record giΓ  sincronizzati **Risultato**: 1. Trova associazioni esistenti (NON da Pre-Discovery) 2. **Controlla l'hash** dei dati 3. Se hash identico β†’ **Skip record** (performance ottimizzata) 4. Se hash diverso β†’ Aggiorna record normalmente ### Scenario 3: Record Modificato in Sorgente **Setup**: - βœ… Associazione esistente - πŸ”„ Dati modificati in sorgente **Risultato**: 1. Trova associazione esistente 2. Calcola nuovo hash: `new_hash β‰  old_hash` 3. **Aggiorna il record** in destinazione 4. Salva il nuovo hash ## πŸ”„ Ciclo di Vita Associazione Pre-Discovery ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1Β° TRASFERIMENTO (KeyAssociations vuota) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Pre-Discovery: Cerca record in Salesforce β”‚ β”‚ βœ… Trovato β†’ Crea associazione con CreatedBy="PreDiscovery" β”‚ β”‚ πŸ”„ AGGIORNAMENTO FORZATO (skip hash check) β”‚ β”‚ πŸ’Ύ Salva nuovo hash β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 2Β° TRASFERIMENTO (Associazione esistente) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Trova associazione (CreatedBy="PreDiscovery") β”‚ β”‚ πŸ” CONTROLLO HASH β”‚ β”‚ β€’ Hash identico β†’ ⏭️ Skip (performance) β”‚ β”‚ β€’ Hash diverso β†’ πŸ”„ Aggiorna normalmente β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ TRASFERIMENTI SUCCESSIVI β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Comportamento normale con controllo hash β”‚ β”‚ (l'associazione non Γ¨ piΓΉ "nuova" dal Pre-Discovery) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## ⚠️ Note Importanti ### Metodo Original (Non-Composite) Il metodo `StartDataTransferOriginal()` **non ha controllo hash**. Tenta sempre l'aggiornamento direttamente, quindi non necessita di modifiche per il Pre-Discovery. ### Performance - **Prima esecuzione**: Tutti i record trovati dal Pre-Discovery vengono aggiornati forzatamente - **Esecuzioni successive**: Controllo hash ottimizza le performance saltando record non modificati ### Pulizia Marker Il marker `CreatedBy: "PreDiscovery"` rimane nell'associazione per tutta la sua vita. Questo permette di: 1. Tracciare l'origine dell'associazione 2. Debugging piΓΉ semplice 3. Statistiche su quante associazioni sono state scoperte automaticamente ## πŸ§ͺ Test Case ### Test 1: Pre-Discovery con Aggiornamento Forzato ``` SETUP: - KeyAssociations: vuota - Salesforce: Record con Email="test@example.com" - Sorgente: Record con Email="test@example.com" + dati aggiornati EXPECTED: 1. Pre-Discovery trova record in Salesforce 2. Crea associazione con CreatedBy="PreDiscovery" 3. FORZA aggiornamento (salta controllo hash) 4. Log: "AGGIORNAMENTO FORZATO (Pre-Discovery)" 5. Record aggiornato in Salesforce ``` ### Test 2: Secondo Trasferimento con Hash Check ``` SETUP: - KeyAssociations: popolata (da Test 1) - Sorgente: Dati NON modificati EXPECTED: 1. Trova associazione esistente 2. Calcola hash: identico 3. Skip record (performance) 4. Log: "saltato - hash identico" 5. Nessuna chiamata API a Salesforce ``` --- **Data Implementazione**: 21 Ottobre 2025 **File Modificato**: `Data_Coupler/Pages/DataCoupler.razor.cs` **Metodo**: `StartDataTransferWithComposite()` **Linee**: ~2840-2890