[Feature] Implementato sistema di valori default per campi mapping

- Creato modello FieldMappingEntry per gestione unificata di field mapping e default values

- Aggiunta colonna DefaultValuesJson alla tabella DataCouplerProfile (max 4000 caratteri)

- Implementata UI con toggle per selezionare modalità Mapping o Default

- Supporto per 9 tipi di dati: string, int, long, decimal, double, float, boolean, datetime, datetimeoffset

- Aggiornata logica TransformRecordToRestEntity per applicare valori default dopo field mapping

- Implementata serializzazione/deserializzazione DefaultValues in DataCouplerProfileService

- Sistema completo di salvataggio/caricamento valori default nei profili

- Migrazione database AddDefaultValuesJsonToProfile creata e applicata
This commit is contained in:
Alessio Dal Santo
2026-02-16 14:42:03 +01:00
parent 483eb7b407
commit b9670ae426
10 changed files with 1249 additions and 65 deletions
+187 -8
View File
@@ -51,10 +51,18 @@ public partial class DataCoupler : ComponentBase
(int)Math.Ceiling((double)fileData[sheetName].Count / pageSize) : 0;
// Mapping campi
private Dictionary<string, string> fieldMappings = new(); // DbColumn -> RestProperty
private Dictionary<string, string> fieldMappings = new(); // DbColumn -> RestProperty (legacy)
private List<FieldMappingEntry> fieldMappingEntries = new(); // New system: supporta sia mapping che default values
private Dictionary<string, (object? Value, string? Type)> defaultValues = new(); // DestinationField -> (DefaultValue, Type)
private HashSet<string> keyFields = new(); // REST properties marked as keys
private string selectedDbColumn = "";
// UI per configurazione mapping/default value
private bool isAddingDefaultValue = false; // Toggle tra mapping normale e default value
private string defaultValueField = ""; // Campo destinazione per default value
private string defaultValueInput = ""; // Input utente per default value
private string defaultValueType = "string"; // Tipo del default value (string, int, decimal, boolean, datetime)
// External ID Relationships (Salesforce)
private List<ExternalIdRelationshipDto> externalIdRelationships = new();
private string selectedRelationshipObject = "";
@@ -345,11 +353,13 @@ public partial class DataCoupler : ComponentBase
// Applica i mapping
fieldMappings.Clear();
fieldMappingEntries.Clear();
keyFields.Clear();
foreach (var mapping in mappings)
{
fieldMappings[mapping.SourceField] = mapping.DestinationField;
fieldMappingEntries.Add(FieldMappingEntry.CreateFieldMapping(mapping.SourceField, mapping.DestinationField));
if (mapping.IsKey)
{
keyFields.Add(mapping.DestinationField);
@@ -370,6 +380,42 @@ public partial class DataCoupler : ComponentBase
{
Logger.LogInformation("Nessun mapping campi da applicare");
}
// Step 4.5: Applica default values se disponibili
if (!string.IsNullOrEmpty(profile.DefaultValuesJson))
{
Logger.LogInformation("Step 4.5 - Applicazione default values...");
try
{
var deserializedDefaults = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, DefaultValueDto>>(
profile.DefaultValuesJson,
new System.Text.Json.JsonSerializerOptions { PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase });
if (deserializedDefaults != null)
{
defaultValues.Clear();
foreach (var entry in deserializedDefaults)
{
defaultValues[entry.Key] = (entry.Value.Value, entry.Value.Type);
fieldMappingEntries.Add(FieldMappingEntry.CreateDefaultValue(entry.Key, entry.Value.Value, entry.Value.Type));
Logger.LogInformation("Default value applicato: {Field} = {Value} ({Type})",
entry.Key, entry.Value.Value, entry.Value.Type);
}
Logger.LogInformation("Default values applicati - Totale: {Count}", defaultValues.Count);
}
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Errore nel caricamento dei default values dal profilo");
}
}
else
{
Logger.LogInformation("Nessun default value da applicare");
}
// Step 5: Applica configurazione chiave sorgente
if (!string.IsNullOrEmpty(profile.SourceKeyField))
@@ -721,6 +767,8 @@ public partial class DataCoupler : ComponentBase
ResetSourceState();
ResetDestinationState();
fieldMappings.Clear();
fieldMappingEntries.Clear();
defaultValues.Clear();
keyFields.Clear();
externalIdRelationships.Clear(); // Reset relazioni
transferResults.Clear();
@@ -1328,6 +1376,17 @@ public partial class DataCoupler : ComponentBase
// Crea il nuovo mapping
fieldMappings[selectedDbColumn] = selectedRestProperty;
// Aggiorna anche la lista FieldMappingEntries
var existingEntry = fieldMappingEntries.FirstOrDefault(e =>
e.Type == CredentialManager.Models.MappingType.FieldMapping && e.SourceField == selectedDbColumn);
if (existingEntry != null)
{
fieldMappingEntries.Remove(existingEntry);
}
fieldMappingEntries.Add(FieldMappingEntry.CreateFieldMapping(selectedDbColumn, selectedRestProperty));
Logger.LogInformation("Creato mapping: {DbColumn} -> {RestProperty}", selectedDbColumn, selectedRestProperty);
// Deseleziona i campi
@@ -1335,14 +1394,108 @@ public partial class DataCoupler : ComponentBase
selectedRestProperty = "";
}
private void CreateDefaultValue()
{
if (string.IsNullOrEmpty(selectedRestProperty) || string.IsNullOrEmpty(defaultValueInput))
return;
try
{
// Converti il valore nel tipo appropriato
object? convertedValue = ConvertDefaultValue(defaultValueInput, defaultValueType);
// Rimuovi eventuale default value esistente per questo campo
if (defaultValues.ContainsKey(selectedRestProperty))
{
defaultValues.Remove(selectedRestProperty);
}
// Rimuovi anche dalla lista entries
var existingEntry = fieldMappingEntries.FirstOrDefault(e =>
e.Type == CredentialManager.Models.MappingType.DefaultValue && e.DestinationField == selectedRestProperty);
if (existingEntry != null)
{
fieldMappingEntries.Remove(existingEntry);
}
// Aggiungi il nuovo default value
defaultValues[selectedRestProperty] = (convertedValue, defaultValueType);
fieldMappingEntries.Add(FieldMappingEntry.CreateDefaultValue(selectedRestProperty, convertedValue, defaultValueType));
Logger.LogInformation("Creato default value: {RestProperty} = {Value} ({Type})",
selectedRestProperty, convertedValue, defaultValueType);
// Reset campi
selectedRestProperty = "";
defaultValueInput = "";
isAddingDefaultValue = false;
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella conversione del valore di default");
transferMessage = $"Errore: {ex.Message}";
transferMessageType = "error";
}
}
private object? ConvertDefaultValue(string input, string type)
{
if (string.IsNullOrEmpty(input))
return null;
return type.ToLower() switch
{
"string" => input,
"int" => int.Parse(input),
"long" => long.Parse(input),
"decimal" => decimal.Parse(input, System.Globalization.CultureInfo.InvariantCulture),
"double" => double.Parse(input, System.Globalization.CultureInfo.InvariantCulture),
"float" => float.Parse(input, System.Globalization.CultureInfo.InvariantCulture),
"boolean" => bool.Parse(input),
"datetime" => DateTime.Parse(input),
"datetimeoffset" => DateTimeOffset.Parse(input),
_ => input
};
}
private void RemoveMapping()
{
if (string.IsNullOrEmpty(selectedDbColumn) || !fieldMappings.ContainsKey(selectedDbColumn))
return;
fieldMappings.Remove(selectedDbColumn);
// Rimuovi anche dalla lista entries
var entry = fieldMappingEntries.FirstOrDefault(e =>
e.Type == CredentialManager.Models.MappingType.FieldMapping && e.SourceField == selectedDbColumn);
if (entry != null)
{
fieldMappingEntries.Remove(entry);
}
Logger.LogInformation("Rimosso mapping per campo: {DbColumn}", selectedDbColumn);
}
private void RemoveDefaultValue(string destinationField)
{
if (defaultValues.ContainsKey(destinationField))
{
defaultValues.Remove(destinationField);
// Rimuovi anche dalla lista entries
var entry = fieldMappingEntries.FirstOrDefault(e =>
e.Type == CredentialManager.Models.MappingType.DefaultValue && e.DestinationField == destinationField);
if (entry != null)
{
fieldMappingEntries.Remove(entry);
}
Logger.LogInformation("Rimosso default value per campo: {Field}", destinationField);
StateHasChanged();
}
}
private void RemoveSpecificMapping(string dbColumn)
{
if (fieldMappings.ContainsKey(dbColumn))
@@ -1351,20 +1504,22 @@ public partial class DataCoupler : ComponentBase
Logger.LogInformation("Rimosso mapping specifico per campo: {DbColumn}", dbColumn);
}
}
Logger.LogInformation("Rimosso mapping specifico per campo: {DbColumn}", dbColumn);
}
}
private void ClearAllMappings()
{
fieldMappings.Clear();
fieldMappingEntries.Clear();
defaultValues.Clear();
selectedDbColumn = "";
selectedRestProperty = "";
sourceKeyField = "";
transferMessage = "";
transferMessageType = "";
isAddingDefaultValue = false;
defaultValueField = "";
defaultValueInput = "";
externalIdRelationships.Clear(); // Pulisce anche le relazioni
Logger.LogInformation("Tutti i mapping e le configurazioni sono stati cancellati");
Logger.LogInformation("Tutti i mapping, default values e le configurazioni sono stati cancellati");
}
// External ID Relationships Methods
@@ -2108,6 +2263,7 @@ public partial class DataCoupler : ComponentBase
.Select(r => r.SourceField)
.ToHashSet();
// STEP 1: Applica i mapping normali (campo sorgente -> campo destinazione)
foreach (var mapping in fieldMappings)
{
string dbColumn = mapping.Key;
@@ -2134,7 +2290,29 @@ public partial class DataCoupler : ComponentBase
}
}
// Aggiungi External ID Relationships (per Salesforce)
// STEP 2: Applica i valori di default per i campi NON ancora popolati
foreach (var defaultValue in defaultValues)
{
string destinationField = defaultValue.Key;
var (value, valueType) = defaultValue.Value;
// Applica il default value solo se il campo non è già stato popolato dal mapping
if (!restData.ContainsKey(destinationField))
{
if (value != null)
{
restData[destinationField] = value;
Logger.LogDebug("Applicato default value: {Field} = {Value} ({Type})",
destinationField, value, valueType);
}
}
else
{
Logger.LogDebug("Campo {Field} già popolato da mapping, default value ignorato", destinationField);
}
}
// STEP 3: Aggiungi External ID Relationships (per Salesforce)
if (externalIdRelationships.Any())
{
foreach (var relationship in externalIdRelationships)
@@ -2163,9 +2341,10 @@ public partial class DataCoupler : ComponentBase
}
}
Logger.LogDebug("Record trasformato: {DbColumns} → {RestProperties}",
Logger.LogDebug("Record trasformato: {DbColumns} → {RestProperties} (inclusi {DefaultCount} default values)",
string.Join(", ", dbRecord.Keys),
string.Join(", ", restData.Keys));
string.Join(", ", restData.Keys),
defaultValues.Count(dv => restData.ContainsKey(dv.Key)));
return restData;
}