diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 84736b8..c0b1c6e 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -27,6 +27,22 @@
"options": {
"cwd": "${workspaceFolder}/Data_Coupler"
}
+ },
+ {
+ "label": "Publish Data_Coupler",
+ "type": "shell",
+ "command": "dotnet",
+ "args": [
+ "publish",
+ "--configuration",
+ "Release",
+ "--output",
+ "${workspaceFolder}/publish",
+ "--project",
+ "Data_Coupler/Data_Coupler.csproj"
+ ],
+ "group": "build",
+ "problemMatcher": []
}
]
}
\ No newline at end of file
diff --git a/DataConnection/REST/Implementations/SalesforceServiceClient.cs b/DataConnection/REST/Implementations/SalesforceServiceClient.cs
index a7b4fd2..dd3308e 100644
--- a/DataConnection/REST/Implementations/SalesforceServiceClient.cs
+++ b/DataConnection/REST/Implementations/SalesforceServiceClient.cs
@@ -23,6 +23,17 @@ namespace DataConnection.REST.Implementations
private string? _instanceUrl;
private DateTime _tokenExpiry;
+ ///
+ /// Configurazione JSON per garantire la compatibilità con Salesforce API
+ /// Utilizza sempre la cultura invariante per i numeri per evitare problemi con virgole/punti decimali
+ ///
+ private static readonly JsonSerializerOptions SalesforceJsonOptions = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString,
+ Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ };
+
public SalesforceServiceClient(HttpClient httpClient, RestServiceOptions options)
: base(httpClient, options)
{
@@ -392,9 +403,19 @@ namespace DataConnection.REST.Implementations
Console.WriteLine($"--- Salesforce Entity Creation Attempt ---");
Console.WriteLine($"SObject: {entityName}");
Console.WriteLine($"Target URL: {createUri}");
- Console.WriteLine($"Data: {System.Text.Json.JsonSerializer.Serialize(entityData)}");
+ Console.WriteLine($"Data: {JsonSerializer.Serialize(entityData, SalesforceJsonOptions)}");
- var response = await _httpClient.PostAsJsonAsync(createUri, entityData, cancellationToken);
+ // Normalizza i valori numerici per evitare problemi con virgole decimali
+ var normalizedData = NormalizeNumericValues(entityData);
+
+ // Usa StringContent con configurazione JSON specifica per Salesforce
+ var jsonContent = new StringContent(
+ JsonSerializer.Serialize(normalizedData, SalesforceJsonOptions),
+ System.Text.Encoding.UTF8,
+ "application/json"
+ );
+
+ var response = await _httpClient.PostAsync(createUri, jsonContent, cancellationToken);
if (!response.IsSuccessStatusCode)
{
@@ -414,7 +435,7 @@ namespace DataConnection.REST.Implementations
return entityData; // Return original data if no response content
// Salesforce returns creation result with Id and success status
- var creationResult = System.Text.Json.JsonSerializer.Deserialize>(responseContent);
+ var creationResult = JsonSerializer.Deserialize>(responseContent, SalesforceJsonOptions);
// Merge the original data with the creation result (which includes the new Id)
if (creationResult != null)
@@ -653,6 +674,76 @@ namespace DataConnection.REST.Implementations
return await AuthenticateAsync(cancellationToken);
}
+ ///
+ /// Normalizza i valori numerici in un dictionary per garantire compatibilità con Salesforce API
+ /// Converte valori decimali con virgola in formato con punto decimale
+ ///
+ private static Dictionary NormalizeNumericValues(Dictionary data)
+ {
+ var normalizedData = new Dictionary();
+ bool hasNormalized = false;
+
+ foreach (var kvp in data)
+ {
+ var value = kvp.Value;
+
+ if (value != null)
+ {
+ // Se è una stringa che rappresenta un numero decimale con virgola, convertila
+ if (value is string stringValue && IsNumericWithComma(stringValue))
+ {
+ if (decimal.TryParse(stringValue, System.Globalization.NumberStyles.Number,
+ System.Globalization.CultureInfo.CurrentCulture, out decimal decimalValue))
+ {
+ // Converte in double usando cultura invariante per garantire punto decimale
+ var normalizedValue = double.Parse(decimalValue.ToString(System.Globalization.CultureInfo.InvariantCulture));
+ normalizedData[kvp.Key] = normalizedValue;
+ Console.WriteLine($"NUMERIC NORMALIZATION: {kvp.Key}: '{stringValue}' → {normalizedValue}");
+ hasNormalized = true;
+ }
+ else
+ {
+ normalizedData[kvp.Key] = value;
+ }
+ }
+ // Se è già un decimal, convertilo in double con cultura invariante
+ else if (value is decimal dec)
+ {
+ var normalizedValue = double.Parse(dec.ToString(System.Globalization.CultureInfo.InvariantCulture));
+ normalizedData[kvp.Key] = normalizedValue;
+ Console.WriteLine($"DECIMAL NORMALIZATION: {kvp.Key}: {dec} → {normalizedValue}");
+ hasNormalized = true;
+ }
+ else
+ {
+ normalizedData[kvp.Key] = value;
+ }
+ }
+ else
+ {
+ normalizedData[kvp.Key] = value!;
+ }
+ }
+
+ if (hasNormalized)
+ {
+ Console.WriteLine($"NORMALIZATION SUMMARY: Processed {data.Count} fields, normalized {normalizedData.Count(kvp => kvp.Value is double)} numeric values");
+ }
+
+ return normalizedData;
+ }
+
+ ///
+ /// Verifica se una stringa rappresenta un numero con virgola decimale
+ ///
+ private static bool IsNumericWithComma(string value)
+ {
+ if (string.IsNullOrEmpty(value)) return false;
+
+ // Pattern per numeri con virgola: opzionale segno, cifre, virgola, cifre
+ return System.Text.RegularExpressions.Regex.IsMatch(value.Trim(), @"^[+-]?\d+,\d+$");
+ }
+
// --- Nested classes for deserializing Salesforce responses ---
private class SalesforceTokenResponse
{
@@ -928,17 +1019,27 @@ namespace DataConnection.REST.Implementations
for (int i = 0; i < batch.Count; i++)
{
+ // Normalizza i valori numerici per evitare problemi con virgole decimali
+ var normalizedData = NormalizeNumericValues(batch[i]);
+
var subrequest = new SalesforceCompositeSubRequest
{
Method = "POST",
Url = $"/services/data/v60.0/sobjects/{entityName}/",
ReferenceId = $"create_{startIndex + i}",
- Body = batch[i]
+ Body = normalizedData
};
compositeRequest.CompositeRequest.Add(subrequest);
}
- var response = await _httpClient.PostAsJsonAsync(compositeUri, compositeRequest, cancellationToken);
+ // Usa StringContent con configurazione JSON specifica per Salesforce
+ var jsonContent = new StringContent(
+ JsonSerializer.Serialize(compositeRequest, SalesforceJsonOptions),
+ System.Text.Encoding.UTF8,
+ "application/json"
+ );
+
+ var response = await _httpClient.PostAsync(compositeUri, jsonContent, cancellationToken);
if (!response.IsSuccessStatusCode)
{
@@ -956,7 +1057,7 @@ namespace DataConnection.REST.Implementations
}
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
- var compositeResponse = JsonSerializer.Deserialize(responseContent);
+ var compositeResponse = JsonSerializer.Deserialize(responseContent, SalesforceJsonOptions);
var results = new List();
@@ -975,7 +1076,7 @@ namespace DataConnection.REST.Implementations
{
if (subResponse.Body is JsonElement bodyElement)
{
- var bodyDict = JsonSerializer.Deserialize>(bodyElement.GetRawText());
+ var bodyDict = JsonSerializer.Deserialize>(bodyElement.GetRawText(), SalesforceJsonOptions);
result.CreatedData = bodyDict;
// Extract the created ID
@@ -1082,18 +1183,28 @@ namespace DataConnection.REST.Implementations
var entityId = kvp.Key;
var entityData = kvp.Value;
+ // Normalizza i valori numerici per evitare problemi con virgole decimali
+ var normalizedData = NormalizeNumericValues(entityData);
+
var subrequest = new SalesforceCompositeSubRequest
{
Method = "PATCH",
Url = $"/services/data/v60.0/sobjects/{entityName}/{entityId}",
ReferenceId = $"update_{startIndex + index}",
- Body = entityData
+ Body = normalizedData
};
compositeRequest.CompositeRequest.Add(subrequest);
index++;
}
- var response = await _httpClient.PostAsJsonAsync(compositeUri, compositeRequest, cancellationToken);
+ // Usa StringContent con configurazione JSON specifica per Salesforce
+ var jsonContent = new StringContent(
+ JsonSerializer.Serialize(compositeRequest, SalesforceJsonOptions),
+ System.Text.Encoding.UTF8,
+ "application/json"
+ );
+
+ var response = await _httpClient.PostAsync(compositeUri, jsonContent, cancellationToken);
if (!response.IsSuccessStatusCode)
{
@@ -1112,7 +1223,7 @@ namespace DataConnection.REST.Implementations
}
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
- var compositeResponse = JsonSerializer.Deserialize(responseContent);
+ var compositeResponse = JsonSerializer.Deserialize(responseContent, SalesforceJsonOptions);
var results = new List();
diff --git a/Data_Coupler/Pages/DataCoupler.razor.cs b/Data_Coupler/Pages/DataCoupler.razor.cs
index f555451..dc20a3d 100644
--- a/Data_Coupler/Pages/DataCoupler.razor.cs
+++ b/Data_Coupler/Pages/DataCoupler.razor.cs
@@ -1627,7 +1627,12 @@ public partial class DataCoupler : ComponentBase
case "edm.decimal":
case "edm.double":
- if (decimal.TryParse(value.ToString(), out decimal decVal))
+ // Usa InvariantCulture per garantire che i decimali usino il punto come separatore
+ if (decimal.TryParse(value.ToString(), System.Globalization.NumberStyles.Number,
+ System.Globalization.CultureInfo.InvariantCulture, out decimal decVal))
+ return decVal;
+ // Fallback: prova con la cultura corrente nel caso il valore usi la virgola
+ if (decimal.TryParse(value.ToString(), out decVal))
return decVal;
break;
diff --git a/Data_Coupler/wwwroot/data/credentials.db-shm b/Data_Coupler/wwwroot/data/credentials.db-shm
index c8d0cbb..fe9ac28 100644
Binary files a/Data_Coupler/wwwroot/data/credentials.db-shm and b/Data_Coupler/wwwroot/data/credentials.db-shm differ
diff --git a/SALESFORCE_JSON_SERIALIZATION_FIX.md b/SALESFORCE_JSON_SERIALIZATION_FIX.md
new file mode 100644
index 0000000..e69de29