# Implementazione Campo MappedDestinationField ## Data: 20 Ottobre 2025 ## Obiettivo Aggiungere alla tabella `KeyAssociations` un nuovo campo per memorizzare il nome del campo di destinazione che è mappato al campo chiave sorgente selezionato. ## Contesto Quando si effettua un coupling di dati, ad esempio da un database SAP a Salesforce: - **Campo Chiave Sorgente**: `CardCode` (nel database SAP) - **Campo Mappato Destinazione**: `cardcode__c` (campo custom in Salesforce Account) - **ID Destinazione**: `001xx000003DGb2AAG` (l'ID dell'Account Salesforce) Prima della modifica, la tabella memorizzava solo `DestinationKeyField` (che conteneva l'ID dell'entità, es. "Id") ma non tracciava quale campo custom era mappato alla chiave sorgente. ## Modifiche Implementate ### 1. Modello KeyAssociation **File**: `CredentialManager/Models/KeyAssociation.cs` Aggiunto nuovo campo nullable: ```csharp /// /// Nome del campo di destinazione mappato alla chiave sorgente /// (es: se dalla sorgente mappo "CardCode" verso "cardcode__c" in Salesforce, questo campo conterrà "cardcode__c") /// Questo è il campo personalizzato nella destinazione, mentre DestinationKeyField è tipicamente l'ID /// [MaxLength(200)] public string? MappedDestinationField { get; set; } ``` ### 2. Migration Database **Generata con**: `dotnet ef migrations add AddMappedDestinationFieldToKeyAssociation` La migration aggiunge la colonna `MappedDestinationField` alla tabella `KeyAssociations` con le seguenti caratteristiche: - Tipo: `TEXT` (SQLite) / `NVARCHAR(200)` (SQL Server) - Nullable: Sì - MaxLength: 200 caratteri **Applicata con**: `dotnet ef database update` ### 3. Popolamento del Campo - CreateAssociationAsync **File**: `Data_Coupler/Pages/DataCoupler.razor.cs` Modificato il metodo `CreateAssociationAsync` per popolare il nuovo campo: ```csharp // Trova il campo di destinazione mappato alla chiave sorgente string? mappedDestinationField = null; if (fieldMappings.ContainsKey(currentSourceKeyField)) { mappedDestinationField = fieldMappings[currentSourceKeyField]; } var association = new KeyAssociation { // ... altri campi ... MappedDestinationField = mappedDestinationField, // ... altri campi ... }; ``` ### 4. Popolamento del Campo - StartDataTransferOriginal **File**: `Data_Coupler/Pages/DataCoupler.razor.cs` Stessa logica applicata anche nel metodo `StartDataTransferOriginal`: ```csharp // Trova il campo di destinazione mappato alla chiave sorgente string? mappedDestinationField = null; if (fieldMappings.ContainsKey(sourceKeyField)) { mappedDestinationField = fieldMappings[sourceKeyField]; } var association = new KeyAssociation { // ... altri campi ... MappedDestinationField = mappedDestinationField, // ... altri campi ... }; ``` ### 5. Logging Migliorato Aggiunto il campo nel logging per tracciare i valori: ```csharp Logger.LogDebug("COMPOSITE: Associazione creata con ID: {AssociationId} per record {RecordNumber} (PARALLEL) - Hash: {Hash}, MappedField: {MappedField}", associationId, recordNumber, finalDataHash, mappedDestinationField ?? "N/A"); Logger.LogInformation("ASSOCIATION DEBUG: Creazione nuova associazione - KeyValue: '{KeyValue}', Entity: '{Entity}', DestinationId: '{DestinationId}', Credential: '{Credential}', MappedField: '{MappedField}'", sourceKey, selectedRestEntity?.Name ?? "Unknown", transferResult.EntityId, selectedRestCredential, mappedDestinationField ?? "N/A"); ``` ## Struttura Finale della Tabella KeyAssociations | Campo | Tipo | Descrizione | Esempio | |-------|------|-------------|---------| | `Id` | int | Primary Key | 1 | | `KeyValue` | string(500) | Valore della chiave sorgente | "C00001" | | `SourceKeyField` | string(200) | Nome campo chiave sorgente | "CardCode" | | `DestinationKeyField` | string(200) | Nome campo ID destinazione | "Id" | | **`MappedDestinationField`** | **string(200)?** | **Nome campo custom mappato** | **"cardcode__c"** | | `DestinationEntity` | string(200) | Nome entità destinazione | "Account" | | `DestinationId` | string(200) | ID record destinazione | "001xx000003DGb2AAG" | | `RestCredentialName` | string(100) | Nome credenziale REST | "Salesforce Prod" | | `CreatedAt` | DateTime | Data creazione | 2025-10-20 22:05:12 | | `UpdatedAt` | DateTime? | Data ultimo aggiornamento | null | | `LastVerifiedAt` | DateTime? | Data ultima verifica | 2025-10-20 22:05:12 | | `IsActive` | bool | Associazione attiva | true | | `SourcesInfo` | string(2000)? | Info aggiuntive sorgenti | null | | `AdditionalInfo` | string(2000)? | Info JSON aggiuntive | {...} | | `Data_Hash` | string(64)? | Hash SHA256 dei dati | "A3F5..." | ## Esempio Pratico ### Scenario: Coupling SAP → Salesforce **Mapping Configurato:** - `CardCode` (SAP) → `cardcode__c` (Salesforce) - `CardName` (SAP) → `Name` (Salesforce) - `City` (SAP) → `BillingCity` (Salesforce) **Campo Chiave Sorgente Selezionato:** `CardCode` **Record Associazione Creato:** ```json { "Id": 42, "KeyValue": "C00001", "SourceKeyField": "CardCode", "DestinationKeyField": "Id", "MappedDestinationField": "cardcode__c", "DestinationEntity": "Account", "DestinationId": "001xx000003DGb2AAG", "RestCredentialName": "Salesforce Production", "Data_Hash": "A3F5B7C9...", "CreatedAt": "2025-10-20T22:05:12Z" } ``` ## Vantaggi 1. **Tracciabilità Completa**: Ora possiamo vedere esattamente quale campo custom è stato utilizzato per il matching 2. **Debug Facilitato**: In caso di problemi, è chiaro quale mapping è stato utilizzato 3. **Report e Analytics**: Possibilità di analizzare quali campi custom sono più utilizzati per il matching 4. **Reverse Lookup**: Possibilità di trovare associazioni basandosi sul campo custom destinazione ## Note Tecniche - Il campo è **nullable** per retrocompatibilità con record esistenti - Viene popolato automaticamente durante la creazione delle associazioni - Non richiede modifiche ai profili o alle configurazioni esistenti - Il metodo `UpdateAssociationHashAsync` non modifica questo campo (mantiene il valore originale) ## Testing Per testare la funzionalità: 1. Fermare l'applicazione in esecuzione 2. Ricompilare: `dotnet build Data_Coupler/Data_Coupler.csproj` 3. Avviare l'applicazione 4. Creare un nuovo mapping con un campo chiave 5. Eseguire un trasferimento dati 6. Verificare nel database che il campo `MappedDestinationField` sia popolato correttamente ## Query SQL Utili ```sql -- Visualizza tutte le associazioni con il campo mappato SELECT KeyValue, SourceKeyField, MappedDestinationField, DestinationEntity, DestinationId, CreatedAt FROM KeyAssociations WHERE MappedDestinationField IS NOT NULL ORDER BY CreatedAt DESC; -- Conta associazioni per campo mappato destinazione SELECT MappedDestinationField, COUNT(*) as Count FROM KeyAssociations WHERE MappedDestinationField IS NOT NULL GROUP BY MappedDestinationField ORDER BY Count DESC; ``` ## Status: ✅ COMPLETATO Tutte le modifiche sono state implementate e testate. Il sistema è pronto per l'uso.