SAP GWQ
This commit is contained in:
@@ -24,6 +24,7 @@ public class DatabaseInitializationService : IDatabaseInitializationService
|
||||
|
||||
private static void EnsureSchema(AppDbContext db)
|
||||
{
|
||||
EnsureSitesTableSupportsOptionalHanaServer(db);
|
||||
AddColumnIfMissing(db, "HanaServers", "DatabaseName", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "HanaServers", "UseSsl", "INTEGER NOT NULL DEFAULT 0");
|
||||
AddColumnIfMissing(db, "HanaServers", "ValidateCertificate", "INTEGER NOT NULL DEFAULT 0");
|
||||
@@ -31,6 +32,10 @@ public class DatabaseInitializationService : IDatabaseInitializationService
|
||||
AddColumnIfMissing(db, "Sites", "SourceSystem", "TEXT NOT NULL DEFAULT 'SAP'");
|
||||
AddColumnIfMissing(db, "Sites", "UsernameOverride", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "Sites", "PasswordOverride", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "Sites", "SapServiceUrl", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "Sites", "SapEntitySet", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "Sites", "SapEntitySetsCache", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "Sites", "SapEntitySetsRefreshedAtUtc", "TEXT NULL");
|
||||
AddColumnIfMissing(db, "ExportSettings", "SapUsername", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "ExportSettings", "SapPassword", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "ExportSettings", "Bi1Username", "TEXT NOT NULL DEFAULT ''");
|
||||
@@ -40,6 +45,104 @@ public class DatabaseInitializationService : IDatabaseInitializationService
|
||||
EnsureTransformationTable(db);
|
||||
}
|
||||
|
||||
private static void EnsureSitesTableSupportsOptionalHanaServer(AppDbContext db)
|
||||
{
|
||||
var conn = db.Database.GetDbConnection();
|
||||
if (conn.State != ConnectionState.Open)
|
||||
conn.Open();
|
||||
|
||||
var hanaServerIdIsRequired = false;
|
||||
{
|
||||
using var pragma = conn.CreateCommand();
|
||||
pragma.CommandText = "PRAGMA table_info(Sites)";
|
||||
using var reader = pragma.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (string.Equals(reader["name"]?.ToString(), "HanaServerId", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
hanaServerIdIsRequired = Convert.ToInt32(reader["notnull"]) == 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hanaServerIdIsRequired)
|
||||
return;
|
||||
|
||||
using var disableFk = conn.CreateCommand();
|
||||
disableFk.CommandText = "PRAGMA foreign_keys = OFF;";
|
||||
disableFk.ExecuteNonQuery();
|
||||
|
||||
using var transaction = conn.BeginTransaction();
|
||||
|
||||
using (var rename = conn.CreateCommand())
|
||||
{
|
||||
rename.Transaction = transaction;
|
||||
rename.CommandText = "ALTER TABLE Sites RENAME TO Sites_old;";
|
||||
rename.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var create = conn.CreateCommand())
|
||||
{
|
||||
create.Transaction = transaction;
|
||||
create.CommandText = @"
|
||||
CREATE TABLE Sites (
|
||||
Id INTEGER NOT NULL CONSTRAINT PK_Sites PRIMARY KEY AUTOINCREMENT,
|
||||
HanaServerId INTEGER NULL,
|
||||
Schema TEXT NOT NULL,
|
||||
TSC TEXT NOT NULL,
|
||||
Land TEXT NOT NULL,
|
||||
SourceSystem TEXT NOT NULL DEFAULT 'SAP',
|
||||
UsernameOverride TEXT NOT NULL DEFAULT '',
|
||||
PasswordOverride TEXT NOT NULL DEFAULT '',
|
||||
SapServiceUrl TEXT NOT NULL DEFAULT '',
|
||||
SapEntitySet TEXT NOT NULL DEFAULT '',
|
||||
SapEntitySetsCache TEXT NOT NULL DEFAULT '',
|
||||
SapEntitySetsRefreshedAtUtc TEXT NULL,
|
||||
IsActive INTEGER NOT NULL,
|
||||
CONSTRAINT FK_Sites_HanaServers_HanaServerId FOREIGN KEY (HanaServerId) REFERENCES HanaServers (Id)
|
||||
);";
|
||||
create.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var copy = conn.CreateCommand())
|
||||
{
|
||||
copy.Transaction = transaction;
|
||||
copy.CommandText = @"
|
||||
INSERT INTO Sites (
|
||||
Id, HanaServerId, Schema, TSC, Land, SourceSystem,
|
||||
UsernameOverride, PasswordOverride, SapServiceUrl, SapEntitySet,
|
||||
SapEntitySetsCache, SapEntitySetsRefreshedAtUtc, IsActive
|
||||
)
|
||||
SELECT
|
||||
Id, HanaServerId, Schema, TSC, Land,
|
||||
COALESCE(SourceSystem, 'SAP'),
|
||||
COALESCE(UsernameOverride, ''),
|
||||
COALESCE(PasswordOverride, ''),
|
||||
COALESCE(SapServiceUrl, ''),
|
||||
COALESCE(SapEntitySet, ''),
|
||||
COALESCE(SapEntitySetsCache, ''),
|
||||
SapEntitySetsRefreshedAtUtc,
|
||||
IsActive
|
||||
FROM Sites_old;";
|
||||
copy.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var drop = conn.CreateCommand())
|
||||
{
|
||||
drop.Transaction = transaction;
|
||||
drop.CommandText = "DROP TABLE Sites_old;";
|
||||
drop.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
using var enableFk = conn.CreateCommand();
|
||||
enableFk.CommandText = "PRAGMA foreign_keys = ON;";
|
||||
enableFk.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private static void AddColumnIfMissing(AppDbContext db, string table, string column, string type)
|
||||
{
|
||||
var conn = db.Database.GetDbConnection();
|
||||
|
||||
@@ -23,6 +23,16 @@ public class ExcelExportService : IExcelExportService
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
public string CreateGenericExcelFile(string outputDirectory, string filePrefix, DateTime fileDate, string worksheetName, IReadOnlyList<IReadOnlyDictionary<string, object?>> rows)
|
||||
{
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
var safePrefix = string.IsNullOrWhiteSpace(filePrefix) ? "Export" : filePrefix.Trim();
|
||||
var fileName = $"{safePrefix}_{fileDate:yyyy-MM-dd}.xlsx";
|
||||
var fullPath = Path.Combine(outputDirectory, fileName);
|
||||
WriteGenericWorkbook(fullPath, worksheetName, rows);
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
private static void WriteWorkbook(string fullPath, List<SalesRecord> records)
|
||||
{
|
||||
using var workbook = new XLWorkbook();
|
||||
@@ -99,4 +109,35 @@ public class ExcelExportService : IExcelExportService
|
||||
ws.Columns().AdjustToContents();
|
||||
workbook.SaveAs(fullPath);
|
||||
}
|
||||
|
||||
private static void WriteGenericWorkbook(string fullPath, string worksheetName, IReadOnlyList<IReadOnlyDictionary<string, object?>> rows)
|
||||
{
|
||||
using var workbook = new XLWorkbook();
|
||||
var sheetName = string.IsNullOrWhiteSpace(worksheetName) ? "Export" : worksheetName.Trim();
|
||||
var ws = workbook.Worksheets.Add(sheetName.Length > 31 ? sheetName[..31] : sheetName);
|
||||
|
||||
var headers = rows
|
||||
.SelectMany(r => r.Keys)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
for (var i = 0; i < headers.Count; i++)
|
||||
{
|
||||
ws.Cell(1, i + 1).Value = headers[i];
|
||||
ws.Cell(1, i + 1).Style.Font.Bold = true;
|
||||
}
|
||||
|
||||
for (var rowIndex = 0; rowIndex < rows.Count; rowIndex++)
|
||||
{
|
||||
var row = rows[rowIndex];
|
||||
for (var colIndex = 0; colIndex < headers.Count; colIndex++)
|
||||
{
|
||||
row.TryGetValue(headers[colIndex], out var value);
|
||||
ws.Cell(rowIndex + 2, colIndex + 1).Value = value?.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
ws.Columns().AdjustToContents();
|
||||
workbook.SaveAs(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ public class ExportOrchestrationService
|
||||
|
||||
private async Task<SiteExportResult?> ExportSiteAsync(Site site)
|
||||
{
|
||||
if (site.HanaServer is null) return null;
|
||||
SiteExportResult? result = null;
|
||||
|
||||
lock (_lock)
|
||||
|
||||
@@ -6,4 +6,5 @@ public interface IExcelExportService
|
||||
{
|
||||
string CreateExcelFile(string outputDirectory, string tsc, DateTime fileDate, List<SalesRecord> records);
|
||||
string CreateConsolidatedExcelFile(string outputDirectory, DateTime fileDate, List<SalesRecord> records);
|
||||
string CreateGenericExcelFile(string outputDirectory, string filePrefix, DateTime fileDate, string worksheetName, IReadOnlyList<IReadOnlyDictionary<string, object?>> rows);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TrafagSalesExporter.Services;
|
||||
|
||||
public interface ISapGatewayService
|
||||
{
|
||||
Task TestConnectionAsync(string serviceUrl, string username, string password, CancellationToken cancellationToken = default);
|
||||
Task<List<string>> GetEntitySetsAsync(string serviceUrl, string username, string password, CancellationToken cancellationToken = default);
|
||||
Task<List<Dictionary<string, object?>>> GetEntityRowsAsync(string serviceUrl, string entitySet, string username, string password, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace TrafagSalesExporter.Services;
|
||||
|
||||
public class SapGatewayService : ISapGatewayService
|
||||
{
|
||||
private static readonly XNamespace AppNs = "http://www.w3.org/2007/app";
|
||||
private static readonly XNamespace EdmNs = "http://docs.oasis-open.org/odata/ns/edm";
|
||||
|
||||
public async Task TestConnectionAsync(string serviceUrl, string username, string password, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var client = CreateClient(username, password);
|
||||
using var response = await client.GetAsync(BuildServiceUri(serviceUrl), cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetEntitySetsAsync(string serviceUrl, string username, string password, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var client = CreateClient(username, password);
|
||||
var baseUrl = BuildServiceUri(serviceUrl);
|
||||
|
||||
var entitySets = await TryReadEntitySetsFromServiceRootAsync(client, baseUrl, cancellationToken);
|
||||
if (entitySets.Count > 0)
|
||||
return entitySets;
|
||||
|
||||
return await ReadEntitySetsFromMetadataAsync(client, baseUrl, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<List<Dictionary<string, object?>>> GetEntityRowsAsync(string serviceUrl, string entitySet, string username, string password, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var client = CreateClient(username, password);
|
||||
var requestUrl = $"{BuildServiceUri(serviceUrl)}{entitySet}?$format=json";
|
||||
using var response = await client.GetAsync(requestUrl, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
using var document = JsonDocument.Parse(json);
|
||||
if (!document.RootElement.TryGetProperty("d", out var dNode))
|
||||
return [];
|
||||
|
||||
if (!dNode.TryGetProperty("results", out var resultsNode) || resultsNode.ValueKind != JsonValueKind.Array)
|
||||
return [];
|
||||
|
||||
var rows = new List<Dictionary<string, object?>>();
|
||||
foreach (var item in resultsNode.EnumerateArray())
|
||||
{
|
||||
var row = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var property in item.EnumerateObject())
|
||||
{
|
||||
row[property.Name] = ConvertJsonValue(property.Value);
|
||||
}
|
||||
|
||||
rows.Add(row);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static HttpClient CreateClient(string username, string password)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
client.Timeout = TimeSpan.FromSeconds(15);
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/atomsvc+xml"));
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
|
||||
"Basic",
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")));
|
||||
return client;
|
||||
}
|
||||
|
||||
private static string BuildServiceUri(string serviceUrl)
|
||||
{
|
||||
var trimmed = serviceUrl.Trim();
|
||||
if (string.IsNullOrWhiteSpace(trimmed))
|
||||
throw new InvalidOperationException("SAP Service URL darf nicht leer sein.");
|
||||
|
||||
var entityPathMarker = "/sap/opu/odata/sap/";
|
||||
var markerIndex = trimmed.IndexOf(entityPathMarker, StringComparison.OrdinalIgnoreCase);
|
||||
if (markerIndex >= 0)
|
||||
{
|
||||
var servicePath = trimmed[(markerIndex + entityPathMarker.Length)..].Trim('/');
|
||||
var parts = servicePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
trimmed = $"{trimmed[..(markerIndex + entityPathMarker.Length)]}{parts[0]}/";
|
||||
}
|
||||
}
|
||||
|
||||
return trimmed.EndsWith('/') ? trimmed : $"{trimmed}/";
|
||||
}
|
||||
|
||||
private static async Task<List<string>> TryReadEntitySetsFromServiceRootAsync(HttpClient client, string baseUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
using var response = await client.GetAsync(baseUrl, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var xml = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
var document = XDocument.Parse(xml);
|
||||
|
||||
return document
|
||||
.Descendants(AppNs + "collection")
|
||||
.Select(x => x.Attribute("href")?.Value ?? string.Empty)
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static async Task<List<string>> ReadEntitySetsFromMetadataAsync(HttpClient client, string baseUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
using var response = await client.GetAsync($"{baseUrl}$metadata", cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var xml = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
var document = XDocument.Parse(xml);
|
||||
|
||||
return document
|
||||
.Descendants(EdmNs + "EntitySet")
|
||||
.Select(x => x.Attribute("Name")?.Value ?? string.Empty)
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static object? ConvertJsonValue(JsonElement element) => element.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => element.GetString(),
|
||||
JsonValueKind.Number => element.ToString(),
|
||||
JsonValueKind.True => true,
|
||||
JsonValueKind.False => false,
|
||||
JsonValueKind.Null => null,
|
||||
_ => element.ToString()
|
||||
};
|
||||
}
|
||||
@@ -9,6 +9,7 @@ public class SiteExportService : ISiteExportService
|
||||
{
|
||||
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
||||
private readonly IHanaQueryService _hanaService;
|
||||
private readonly ISapGatewayService _sapGatewayService;
|
||||
private readonly IExcelExportService _excelService;
|
||||
private readonly ISharePointUploadService _sharePointService;
|
||||
private readonly IRecordTransformationService _transformationService;
|
||||
@@ -17,6 +18,7 @@ public class SiteExportService : ISiteExportService
|
||||
public SiteExportService(
|
||||
IDbContextFactory<AppDbContext> dbFactory,
|
||||
IHanaQueryService hanaService,
|
||||
ISapGatewayService sapGatewayService,
|
||||
IExcelExportService excelService,
|
||||
ISharePointUploadService sharePointService,
|
||||
IRecordTransformationService transformationService,
|
||||
@@ -24,6 +26,7 @@ public class SiteExportService : ISiteExportService
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
_hanaService = hanaService;
|
||||
_sapGatewayService = sapGatewayService;
|
||||
_excelService = excelService;
|
||||
_sharePointService = sharePointService;
|
||||
_transformationService = transformationService;
|
||||
@@ -32,9 +35,6 @@ public class SiteExportService : ISiteExportService
|
||||
|
||||
public async Task<SiteExportResult> ExportAsync(Site site, Action<string>? updateStatus = null)
|
||||
{
|
||||
if (site.HanaServer is null)
|
||||
throw new InvalidOperationException($"Standort '{site.Land}' hat keinen HANA-Server.");
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
var log = new ExportLog
|
||||
{
|
||||
@@ -49,22 +49,44 @@ public class SiteExportService : ISiteExportService
|
||||
using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var settings = await db.ExportSettings.FirstOrDefaultAsync() ?? new ExportSettings();
|
||||
var spConfig = await db.SharePointConfigs.FirstOrDefaultAsync();
|
||||
var exportServer = BuildEffectiveServer(site, settings);
|
||||
|
||||
updateStatus?.Invoke("HANA Abfrage...");
|
||||
var records = await Task.Run(() => _hanaService.GetSalesRecords(
|
||||
exportServer, site.Schema, site.TSC, site.Land, settings.DateFilter));
|
||||
|
||||
updateStatus?.Invoke("Transformationen anwenden...");
|
||||
var rules = await db.FieldTransformationRules
|
||||
.Where(r => r.IsActive && r.SourceSystem == (string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem))
|
||||
.OrderBy(r => r.SortOrder)
|
||||
.ToListAsync();
|
||||
_transformationService.Apply(records, rules);
|
||||
|
||||
updateStatus?.Invoke("Excel erstellen...");
|
||||
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
|
||||
var filePath = _excelService.CreateExcelFile(outputDir, site.TSC, DateTime.UtcNow.Date, records);
|
||||
var sourceSystem = NormalizeSourceSystem(site.SourceSystem);
|
||||
var records = new List<SalesRecord>();
|
||||
string filePath;
|
||||
|
||||
if (sourceSystem == "SAP")
|
||||
{
|
||||
var credentials = ResolveCredentials(site, settings, sourceSystem);
|
||||
if (string.IsNullOrWhiteSpace(site.SapServiceUrl))
|
||||
throw new InvalidOperationException($"Standort '{site.Land}' hat keine SAP Service URL.");
|
||||
if (string.IsNullOrWhiteSpace(site.SapEntitySet))
|
||||
throw new InvalidOperationException($"Standort '{site.Land}' hat kein SAP Entity Set ausgewählt.");
|
||||
|
||||
updateStatus?.Invoke("SAP Gateway Abfrage...");
|
||||
var rows = await _sapGatewayService.GetEntityRowsAsync(site.SapServiceUrl, site.SapEntitySet, credentials.Username, credentials.Password);
|
||||
updateStatus?.Invoke("Excel erstellen...");
|
||||
filePath = _excelService.CreateGenericExcelFile(outputDir, $"SAP_{site.TSC}_{site.SapEntitySet}", DateTime.UtcNow.Date, site.SapEntitySet, rows);
|
||||
log.RowCount = rows.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
var exportServer = BuildEffectiveServer(site, settings, sourceSystem);
|
||||
updateStatus?.Invoke("HANA Abfrage...");
|
||||
records = await Task.Run(() => _hanaService.GetSalesRecords(
|
||||
exportServer, site.Schema, site.TSC, site.Land, settings.DateFilter));
|
||||
|
||||
updateStatus?.Invoke("Transformationen anwenden...");
|
||||
var rules = await db.FieldTransformationRules
|
||||
.Where(r => r.IsActive && r.SourceSystem == sourceSystem)
|
||||
.OrderBy(r => r.SortOrder)
|
||||
.ToListAsync();
|
||||
_transformationService.Apply(records, rules);
|
||||
|
||||
updateStatus?.Invoke("Excel erstellen...");
|
||||
filePath = _excelService.CreateExcelFile(outputDir, site.TSC, DateTime.UtcNow.Date, records);
|
||||
log.RowCount = records.Count;
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
|
||||
if (spConfig is not null &&
|
||||
@@ -80,12 +102,11 @@ public class SiteExportService : ISiteExportService
|
||||
|
||||
sw.Stop();
|
||||
log.Status = "OK";
|
||||
log.RowCount = records.Count;
|
||||
log.FileName = fileName;
|
||||
log.DurationSeconds = sw.Elapsed.TotalSeconds;
|
||||
|
||||
_logger.LogInformation("Export OK: {Land} ({TSC}) - {Rows} Zeilen in {Duration:F1}s",
|
||||
site.Land, site.TSC, records.Count, sw.Elapsed.TotalSeconds);
|
||||
site.Land, site.TSC, log.RowCount, sw.Elapsed.TotalSeconds);
|
||||
|
||||
return new SiteExportResult
|
||||
{
|
||||
@@ -113,14 +134,12 @@ public class SiteExportService : ISiteExportService
|
||||
}
|
||||
}
|
||||
|
||||
private static HanaServer BuildEffectiveServer(Site site, ExportSettings settings)
|
||||
private static HanaServer BuildEffectiveServer(Site site, ExportSettings settings, string sourceSystem)
|
||||
{
|
||||
if (site.HanaServer is null)
|
||||
throw new InvalidOperationException($"Standort '{site.Land}' hat keinen HANA-Server.");
|
||||
|
||||
var sourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem.Trim().ToUpperInvariant();
|
||||
var inheritedUsername = GetCentralUsername(sourceSystem, settings);
|
||||
var inheritedPassword = GetCentralPassword(sourceSystem, settings);
|
||||
var credentials = ResolveCredentials(site, settings, sourceSystem);
|
||||
|
||||
return new HanaServer
|
||||
{
|
||||
@@ -128,8 +147,8 @@ public class SiteExportService : ISiteExportService
|
||||
Name = site.HanaServer.Name,
|
||||
Host = site.HanaServer.Host,
|
||||
Port = site.HanaServer.Port,
|
||||
Username = FirstNonEmpty(site.UsernameOverride, inheritedUsername, site.HanaServer.Username),
|
||||
Password = FirstNonEmpty(site.PasswordOverride, inheritedPassword, site.HanaServer.Password),
|
||||
Username = FirstNonEmpty(credentials.Username, site.HanaServer.Username),
|
||||
Password = FirstNonEmpty(credentials.Password, site.HanaServer.Password),
|
||||
DatabaseName = site.HanaServer.DatabaseName,
|
||||
UseSsl = site.HanaServer.UseSsl,
|
||||
ValidateCertificate = site.HanaServer.ValidateCertificate,
|
||||
@@ -137,6 +156,10 @@ public class SiteExportService : ISiteExportService
|
||||
};
|
||||
}
|
||||
|
||||
private static (string Username, string Password) ResolveCredentials(Site site, ExportSettings settings, string sourceSystem)
|
||||
=> (FirstNonEmpty(site.UsernameOverride, GetCentralUsername(sourceSystem, settings)),
|
||||
FirstNonEmpty(site.PasswordOverride, GetCentralPassword(sourceSystem, settings)));
|
||||
|
||||
private static string GetCentralUsername(string sourceSystem, ExportSettings settings) => sourceSystem switch
|
||||
{
|
||||
"BI1" => settings.Bi1Username,
|
||||
@@ -151,6 +174,9 @@ public class SiteExportService : ISiteExportService
|
||||
_ => settings.SapPassword
|
||||
};
|
||||
|
||||
private static string NormalizeSourceSystem(string? sourceSystem)
|
||||
=> string.IsNullOrWhiteSpace(sourceSystem) ? "SAP" : sourceSystem.Trim().ToUpperInvariant();
|
||||
|
||||
private static string FirstNonEmpty(params string[] values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
|
||||
Reference in New Issue
Block a user