umfangreiches refactoring
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
@page "/"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using System.Diagnostics
|
||||
@using TrafagSalesExporter.Data
|
||||
@using TrafagSalesExporter.Services
|
||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||
@inject IDashboardPageService DashboardPageActions
|
||||
@inject ExportOrchestrationService Orchestrator
|
||||
@inject TimerBackgroundService TimerService
|
||||
@inject ISnackbar Snackbar
|
||||
@@ -170,49 +168,9 @@
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
_loading = true;
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
|
||||
var sites = await db.Sites.Include(s => s.HanaServer).Where(s => s.IsActive).ToListAsync();
|
||||
var sourceSystems = await db.SourceSystemDefinitions.AsNoTracking().ToListAsync();
|
||||
var logs = await db.ExportLogs
|
||||
.GroupBy(l => l.SiteId)
|
||||
.Select(g => g.OrderByDescending(l => l.Timestamp).First())
|
||||
.ToListAsync();
|
||||
var appLogs = await db.AppEventLogs
|
||||
.Where(l => l.SiteId != null)
|
||||
.OrderByDescending(l => l.Timestamp)
|
||||
.Take(1000)
|
||||
.ToListAsync();
|
||||
var latestAppLogsBySite = appLogs
|
||||
.GroupBy(l => l.SiteId!.Value)
|
||||
.ToDictionary(g => g.Key, g => g.OrderByDescending(x => x.Timestamp).First());
|
||||
|
||||
_dashboardRows = sites.Select(s =>
|
||||
{
|
||||
var log = logs.FirstOrDefault(l => l.SiteId == s.Id);
|
||||
latestAppLogsBySite.TryGetValue(s.Id, out var appLog);
|
||||
var sourceSystem = sourceSystems.FirstOrDefault(x => string.Equals(x.Code, s.SourceSystem, StringComparison.OrdinalIgnoreCase));
|
||||
return new DashboardRow
|
||||
{
|
||||
SiteId = s.Id,
|
||||
Land = s.Land,
|
||||
TSC = s.TSC,
|
||||
Schema = s.Schema,
|
||||
ServerName = string.Equals(sourceSystem?.ConnectionKind, SourceSystemConnectionKinds.SapGateway, StringComparison.OrdinalIgnoreCase)
|
||||
? ResolveDashboardSapServiceUrl(s, sourceSystems)
|
||||
: s.HanaServer?.Name ?? "",
|
||||
LastStatus = log?.Status ?? "",
|
||||
RowCount = log?.RowCount ?? 0,
|
||||
LastRun = log?.Timestamp,
|
||||
DurationSeconds = log?.DurationSeconds ?? 0,
|
||||
ErrorMessage = log?.ErrorMessage ?? "",
|
||||
FilePath = log?.FilePath ?? "",
|
||||
LiveMessage = appLog is null ? string.Empty : $"{appLog.Category}: {appLog.Message}",
|
||||
LiveDetails = appLog?.Details ?? ""
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
_consolidatedRows = BuildConsolidatedRows(settings: await db.ExportSettings.FirstOrDefaultAsync() ?? new());
|
||||
var state = await DashboardPageActions.LoadAsync();
|
||||
_dashboardRows = state.DashboardRows;
|
||||
_consolidatedRows = state.ConsolidatedRows;
|
||||
|
||||
_anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId)) || Orchestrator.IsConsolidatedExporting();
|
||||
_loading = false;
|
||||
@@ -321,15 +279,6 @@
|
||||
OpenFile(row.FilePath);
|
||||
}
|
||||
|
||||
private static string ResolveDashboardSapServiceUrl(Site site, List<SourceSystemDefinition> sourceSystems)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(site.SapServiceUrl))
|
||||
return site.SapServiceUrl;
|
||||
|
||||
var sourceSystem = sourceSystems.FirstOrDefault(x => string.Equals(x.Code, site.SourceSystem, StringComparison.OrdinalIgnoreCase));
|
||||
return string.IsNullOrWhiteSpace(sourceSystem?.CentralServiceUrl) ? "SAP Gateway" : sourceSystem.CentralServiceUrl;
|
||||
}
|
||||
|
||||
private void OpenFile(string filePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath))
|
||||
@@ -417,63 +366,6 @@
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static List<ConsolidatedDashboardRow> BuildConsolidatedRows(ExportSettings settings)
|
||||
{
|
||||
var outputDirectory = ResolveConsolidatedOutputDirectory(settings);
|
||||
if (!Directory.Exists(outputDirectory))
|
||||
return [];
|
||||
|
||||
return Directory.GetFiles(outputDirectory, "Sales_All_*.xlsx")
|
||||
.Select(path => new FileInfo(path))
|
||||
.OrderByDescending(file => file.LastWriteTime)
|
||||
.Take(1)
|
||||
.Select(file => new ConsolidatedDashboardRow
|
||||
{
|
||||
Label = "Konsolidierter Export",
|
||||
FilePath = file.FullName,
|
||||
DisplayPath = file.FullName,
|
||||
LastModified = file.LastWriteTime
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static string ResolveConsolidatedOutputDirectory(ExportSettings settings)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(settings.LocalConsolidatedExportFolder))
|
||||
return settings.LocalConsolidatedExportFolder.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(settings.LocalSiteExportFolder))
|
||||
return settings.LocalSiteExportFolder.Trim();
|
||||
|
||||
return Path.Combine(AppContext.BaseDirectory, "output");
|
||||
}
|
||||
|
||||
private class DashboardRow
|
||||
{
|
||||
public int SiteId { get; set; }
|
||||
public string Land { get; set; } = "";
|
||||
public string TSC { get; set; } = "";
|
||||
public string Schema { get; set; } = "";
|
||||
public string ServerName { get; set; } = "";
|
||||
public string LastStatus { get; set; } = "";
|
||||
public int RowCount { get; set; }
|
||||
public DateTime? LastRun { get; set; }
|
||||
public double DurationSeconds { get; set; }
|
||||
public string ErrorMessage { get; set; } = "";
|
||||
public string FilePath { get; set; } = "";
|
||||
public string LiveMessage { get; set; } = "";
|
||||
public string LiveDetails { get; set; } = "";
|
||||
public bool HasOpenableFile => !string.IsNullOrWhiteSpace(FilePath) && File.Exists(FilePath);
|
||||
}
|
||||
|
||||
private class ConsolidatedDashboardRow
|
||||
{
|
||||
public string Label { get; set; } = "";
|
||||
public string FilePath { get; set; } = "";
|
||||
public string DisplayPath { get; set; } = "";
|
||||
public DateTime? LastModified { get; set; }
|
||||
public bool HasOpenableFile => !string.IsNullOrWhiteSpace(FilePath) && File.Exists(FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@page "/logs"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using TrafagSalesExporter.Data
|
||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||
@using TrafagSalesExporter.Services
|
||||
@inject ILogsPageService LogsPageActions
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
@inject TrafagSalesExporter.Services.IUiTextService UiText
|
||||
@@ -117,37 +116,16 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
_availableLands = await db.ExportLogs.Select(l => l.Land).Distinct().OrderBy(l => l).ToListAsync();
|
||||
await LoadLogsAsync();
|
||||
}
|
||||
|
||||
private async Task LoadLogsAsync()
|
||||
{
|
||||
_loading = true;
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
IQueryable<ExportLog> query = db.ExportLogs.OrderByDescending(l => l.Timestamp);
|
||||
|
||||
if (!string.IsNullOrEmpty(_filterLand))
|
||||
query = query.Where(l => l.Land == _filterLand);
|
||||
|
||||
if (!string.IsNullOrEmpty(_filterStatus))
|
||||
query = query.Where(l => l.Status == _filterStatus);
|
||||
|
||||
if (_filterDate.HasValue)
|
||||
query = query.Where(l => l.Timestamp.Date == _filterDate.Value.Date);
|
||||
|
||||
_logs = await query.Take(500).ToListAsync();
|
||||
|
||||
IQueryable<AppEventLog> appLogQuery = db.AppEventLogs.OrderByDescending(l => l.Timestamp);
|
||||
|
||||
if (!string.IsNullOrEmpty(_filterLand))
|
||||
appLogQuery = appLogQuery.Where(l => l.Land == _filterLand);
|
||||
|
||||
if (_filterDate.HasValue)
|
||||
appLogQuery = appLogQuery.Where(l => l.Timestamp.Date == _filterDate.Value.Date);
|
||||
|
||||
_appLogs = await appLogQuery.Take(500).ToListAsync();
|
||||
var state = await LogsPageActions.LoadAsync(_filterLand, _filterStatus, _filterDate);
|
||||
_availableLands = state.AvailableLands;
|
||||
_logs = state.Logs;
|
||||
_appLogs = state.AppLogs;
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
@@ -165,13 +143,9 @@
|
||||
|
||||
if (result != true) return;
|
||||
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var cutoff = DateTime.Now.AddDays(-90);
|
||||
var oldLogs = await db.ExportLogs.Where(l => l.Timestamp < cutoff).ToListAsync();
|
||||
db.ExportLogs.RemoveRange(oldLogs);
|
||||
var count = await db.SaveChangesAsync();
|
||||
var deletedCount = await LogsPageActions.DeleteOldLogsAsync(90);
|
||||
await LoadLogsAsync();
|
||||
Snackbar.Add(string.Format(T("{0} alte Logs geloescht", "{0} old logs deleted"), oldLogs.Count), Severity.Info);
|
||||
Snackbar.Add(string.Format(T("{0} alte Logs geloescht", "{0} old logs deleted"), deletedCount), Severity.Info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/management-cockpit"
|
||||
@using TrafagSalesExporter.Models
|
||||
@using TrafagSalesExporter.Services
|
||||
@inject IManagementCockpitService CockpitService
|
||||
@inject IManagementCockpitPageService CockpitPageService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IUiTextService UiText
|
||||
|
||||
@@ -335,8 +335,11 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await ReloadFiles();
|
||||
await ReloadCentralYears();
|
||||
var state = await CockpitPageService.InitializeAsync(_selectedFilePath, _selectedCentralYear);
|
||||
_files = state.Files;
|
||||
_centralYears = state.CentralYears;
|
||||
_selectedFilePath = state.SelectedFilePath;
|
||||
_selectedCentralYear = state.SelectedCentralYear;
|
||||
}
|
||||
|
||||
private async Task ReloadFiles()
|
||||
@@ -344,7 +347,7 @@
|
||||
_loadingFiles = true;
|
||||
try
|
||||
{
|
||||
_files = await CockpitService.GetAvailableFilesAsync();
|
||||
_files = await CockpitPageService.LoadFilesAsync();
|
||||
_selectedFilePath ??= _files.FirstOrDefault()?.Path;
|
||||
}
|
||||
finally
|
||||
@@ -355,7 +358,7 @@
|
||||
|
||||
private async Task ReloadCentralYears()
|
||||
{
|
||||
_centralYears = await CockpitService.GetAvailableCentralYearsAsync();
|
||||
_centralYears = await CockpitPageService.LoadCentralYearsAsync();
|
||||
if (_selectedCentralYear == 0)
|
||||
_selectedCentralYear = _centralYears.LastOrDefault();
|
||||
}
|
||||
@@ -368,7 +371,7 @@
|
||||
_analyzing = true;
|
||||
try
|
||||
{
|
||||
_result = await CockpitService.AnalyzeAsync(_selectedFilePath);
|
||||
_result = await CockpitPageService.AnalyzeAsync(_selectedFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -388,7 +391,7 @@
|
||||
_analyzingCentral = true;
|
||||
try
|
||||
{
|
||||
_centralResult = await CockpitService.AnalyzeCentralAsync(_selectedCentralYear, _selectedCentralMonth);
|
||||
_centralResult = await CockpitPageService.AnalyzeCentralAsync(_selectedCentralYear, _selectedCentralMonth);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
@page "/settings"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using TrafagSalesExporter.Data
|
||||
@using TrafagSalesExporter.Models
|
||||
@using TrafagSalesExporter.Services
|
||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||
@inject ISharePointUploadService SpService
|
||||
@inject TimerBackgroundService TimerService
|
||||
@inject IHanaQueryService HanaService
|
||||
@inject ISapGatewayService SapGatewayService
|
||||
@inject IConfigTransferService ConfigTransferService
|
||||
@inject IExchangeRateImportService ExchangeRateImportService
|
||||
@inject ISettingsPageService SettingsPageActions
|
||||
@inject IJSRuntime JS
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
@@ -328,35 +320,16 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
_spConfig = await db.SharePointConfigs.FirstOrDefaultAsync() ?? new SharePointConfig();
|
||||
_exportSettings = await db.ExportSettings.FirstOrDefaultAsync() ?? new ExportSettings();
|
||||
_sourceSystems = await db.SourceSystemDefinitions.OrderBy(x => x.Code).ToListAsync();
|
||||
_exchangeRates = await db.CurrencyExchangeRates
|
||||
.OrderBy(x => x.FromCurrency)
|
||||
.ThenBy(x => x.ToCurrency)
|
||||
.ThenByDescending(x => x.ValidFrom)
|
||||
.ToListAsync();
|
||||
var state = await SettingsPageActions.LoadAsync();
|
||||
_spConfig = state.SharePointConfig;
|
||||
_exportSettings = state.ExportSettings;
|
||||
_sourceSystems = state.SourceSystems;
|
||||
_exchangeRates = state.ExchangeRates;
|
||||
}
|
||||
|
||||
private async Task SaveSharePoint()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var existing = await db.SharePointConfigs.FirstOrDefaultAsync();
|
||||
if (existing is null)
|
||||
{
|
||||
db.SharePointConfigs.Add(_spConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.SiteUrl = _spConfig.SiteUrl;
|
||||
existing.ExportFolder = _spConfig.ExportFolder;
|
||||
existing.CentralExportFolder = _spConfig.CentralExportFolder;
|
||||
existing.TenantId = _spConfig.TenantId;
|
||||
existing.ClientId = _spConfig.ClientId;
|
||||
existing.ClientSecret = _spConfig.ClientSecret;
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
await SettingsPageActions.SaveSharePointAsync(_spConfig);
|
||||
Snackbar.Add("SharePoint Konfiguration gespeichert", Severity.Success);
|
||||
}
|
||||
|
||||
@@ -365,15 +338,7 @@
|
||||
_testingSp = true;
|
||||
try
|
||||
{
|
||||
var tenantId = NormalizeConfigValue(_spConfig.TenantId);
|
||||
var clientId = NormalizeConfigValue(_spConfig.ClientId);
|
||||
var clientSecret = NormalizeConfigValue(_spConfig.ClientSecret);
|
||||
var siteUrl = NormalizeConfigValue(_spConfig.SiteUrl);
|
||||
|
||||
_sharePointTestPreview = BuildSharePointTestPreview(tenantId, clientId, clientSecret, siteUrl);
|
||||
|
||||
await SpService.TestConnectionAsync(
|
||||
tenantId, clientId, clientSecret, siteUrl);
|
||||
_sharePointTestPreview = await SettingsPageActions.BuildSharePointTestPreviewAsync(_spConfig);
|
||||
Snackbar.Add("SharePoint Verbindung erfolgreich!", Severity.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -388,24 +353,7 @@
|
||||
|
||||
private async Task SaveExportSettings()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var existing = await db.ExportSettings.FirstOrDefaultAsync();
|
||||
if (existing is null)
|
||||
{
|
||||
db.ExportSettings.Add(_exportSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.DateFilter = _exportSettings.DateFilter;
|
||||
existing.TimerHour = _exportSettings.TimerHour;
|
||||
existing.TimerMinute = _exportSettings.TimerMinute;
|
||||
existing.TimerEnabled = _exportSettings.TimerEnabled;
|
||||
existing.DebugLoggingEnabled = _exportSettings.DebugLoggingEnabled;
|
||||
existing.LocalSiteExportFolder = _exportSettings.LocalSiteExportFolder;
|
||||
existing.LocalConsolidatedExportFolder = _exportSettings.LocalConsolidatedExportFolder;
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
TimerService.Recalculate();
|
||||
await SettingsPageActions.SaveExportSettingsAsync(_exportSettings);
|
||||
Snackbar.Add("Export Einstellungen gespeichert", Severity.Success);
|
||||
}
|
||||
|
||||
@@ -493,46 +441,15 @@
|
||||
|
||||
private async Task SaveSourceSystems()
|
||||
{
|
||||
var normalized = _sourceSystems
|
||||
.Select(x => new SourceSystemDefinition
|
||||
{
|
||||
Id = x.Id,
|
||||
Code = NormalizeSourceSystemCode(x.Code),
|
||||
DisplayName = NormalizeConfigValue(x.DisplayName),
|
||||
ConnectionKind = NormalizeConnectionKind(x.ConnectionKind),
|
||||
IsActive = x.IsActive,
|
||||
CentralServiceUrl = NormalizeConfigValue(x.CentralServiceUrl),
|
||||
CentralUsername = NormalizeConfigValue(x.CentralUsername),
|
||||
CentralPassword = x.CentralPassword ?? string.Empty
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.Code))
|
||||
.ToList();
|
||||
|
||||
if (normalized.Any(x => string.IsNullOrWhiteSpace(x.DisplayName)))
|
||||
try
|
||||
{
|
||||
Snackbar.Add("Jedes Quellsystem braucht einen Anzeigenamen.", Severity.Warning);
|
||||
return;
|
||||
_sourceSystems = await SettingsPageActions.SaveSourceSystemsAsync(_sourceSystems);
|
||||
Snackbar.Add("Quellsysteme gespeichert", Severity.Success);
|
||||
}
|
||||
|
||||
var duplicates = normalized
|
||||
.GroupBy(x => x.Code)
|
||||
.FirstOrDefault(g => g.Count() > 1);
|
||||
if (duplicates is not null)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Quellsystem-Code doppelt vorhanden: {duplicates.Key}", Severity.Warning);
|
||||
return;
|
||||
Snackbar.Add(ex.Message, Severity.Warning);
|
||||
}
|
||||
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var existing = await db.SourceSystemDefinitions.ToListAsync();
|
||||
if (existing.Count > 0)
|
||||
db.SourceSystemDefinitions.RemoveRange(existing);
|
||||
|
||||
db.SourceSystemDefinitions.AddRange(normalized);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
_sourceSystems = await db.SourceSystemDefinitions.OrderBy(x => x.Code).ToListAsync();
|
||||
Snackbar.Add("Quellsysteme gespeichert", Severity.Success);
|
||||
}
|
||||
|
||||
private void AddExchangeRate()
|
||||
@@ -554,32 +471,7 @@
|
||||
|
||||
private async Task SaveExchangeRates()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var existingRates = await db.CurrencyExchangeRates.ToListAsync();
|
||||
if (existingRates.Count > 0)
|
||||
db.CurrencyExchangeRates.RemoveRange(existingRates);
|
||||
|
||||
db.CurrencyExchangeRates.AddRange(_exchangeRates.Select(rate => new CurrencyExchangeRate
|
||||
{
|
||||
FromCurrency = NormalizeConfigValue(rate.FromCurrency).ToUpperInvariant(),
|
||||
ToCurrency = NormalizeConfigValue(rate.ToCurrency).ToUpperInvariant(),
|
||||
Rate = rate.Rate,
|
||||
ValidFrom = rate.ValidFrom.Date,
|
||||
ValidTo = rate.ValidTo?.Date,
|
||||
Notes = NormalizeConfigValue(rate.Notes),
|
||||
IsActive = rate.IsActive
|
||||
}).Where(rate => !string.IsNullOrWhiteSpace(rate.FromCurrency)
|
||||
&& !string.IsNullOrWhiteSpace(rate.ToCurrency)
|
||||
&& rate.Rate > 0m));
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
_exchangeRates = await db.CurrencyExchangeRates
|
||||
.OrderBy(x => x.FromCurrency)
|
||||
.ThenBy(x => x.ToCurrency)
|
||||
.ThenByDescending(x => x.ValidFrom)
|
||||
.ToListAsync();
|
||||
|
||||
_exchangeRates = await SettingsPageActions.SaveExchangeRatesAsync(_exchangeRates);
|
||||
Snackbar.Add("Wechselkurse gespeichert", Severity.Success);
|
||||
}
|
||||
|
||||
@@ -591,8 +483,8 @@
|
||||
_refreshingExchangeRates = true;
|
||||
try
|
||||
{
|
||||
var result = await ExchangeRateImportService.RefreshEcbRatesAsync();
|
||||
_exchangeRates = await LoadExchangeRatesAsync();
|
||||
var result = await SettingsPageActions.RefreshEcbRatesAsync();
|
||||
_exchangeRates = result.ExchangeRates;
|
||||
Snackbar.Add($"ECB-Kurse aktualisiert: {result.ImportedCount} Kurse vom {result.RateDate:yyyy-MM-dd}.", Severity.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -613,7 +505,7 @@
|
||||
_exportingConfig = true;
|
||||
try
|
||||
{
|
||||
var json = await ConfigTransferService.ExportJsonAsync(_includeSecretsInExport);
|
||||
var json = await SettingsPageActions.ExportConfigurationAsync(_includeSecretsInExport);
|
||||
var suffix = _includeSecretsInExport ? "with-secrets" : "without-secrets";
|
||||
var fileName = $"trafag-config-{DateTime.UtcNow:yyyyMMdd-HHmmss}-{suffix}.json";
|
||||
await JS.InvokeVoidAsync("trafagDownload.saveTextFile", fileName, json, "application/json;charset=utf-8");
|
||||
@@ -641,18 +533,11 @@
|
||||
await using var stream = file.OpenReadStream(5 * 1024 * 1024);
|
||||
using var reader = new StreamReader(stream);
|
||||
var json = await reader.ReadToEndAsync();
|
||||
await ConfigTransferService.ImportJsonAsync(json);
|
||||
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
_spConfig = await db.SharePointConfigs.FirstOrDefaultAsync() ?? new SharePointConfig();
|
||||
_exportSettings = await db.ExportSettings.FirstOrDefaultAsync() ?? new ExportSettings();
|
||||
_sourceSystems = await db.SourceSystemDefinitions.OrderBy(x => x.Code).ToListAsync();
|
||||
_exchangeRates = await db.CurrencyExchangeRates
|
||||
.OrderBy(x => x.FromCurrency)
|
||||
.ThenBy(x => x.ToCurrency)
|
||||
.ThenByDescending(x => x.ValidFrom)
|
||||
.ToListAsync();
|
||||
TimerService.Recalculate();
|
||||
var state = await SettingsPageActions.ImportConfigurationAsync(json);
|
||||
_spConfig = state.SharePointConfig;
|
||||
_exportSettings = state.ExportSettings;
|
||||
_sourceSystems = state.SourceSystems;
|
||||
_exchangeRates = state.ExchangeRates;
|
||||
Snackbar.Add("Konfiguration importiert", Severity.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -674,70 +559,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(definition.ConnectionKind, SourceSystemConnectionKinds.SapGateway, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await TestCentralSapCredentials(definition);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(definition.ConnectionKind, SourceSystemConnectionKinds.Hana, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await TestCentralHanaCredentials(definition);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestCentralHanaCredentials(SourceSystemDefinition definition)
|
||||
{
|
||||
var sourceSystem = definition.Code;
|
||||
if (!_testingSystems.Add(sourceSystem))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var username = definition.CentralUsername;
|
||||
var password = definition.CentralPassword;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
Snackbar.Add($"Für {sourceSystem} sind keine zentralen Zugangsdaten gepflegt.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var centralServer = await db.HanaServers
|
||||
.Where(s => s.SourceSystem == sourceSystem)
|
||||
.OrderBy(s => s.Id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (centralServer is null || string.IsNullOrWhiteSpace(centralServer.Host))
|
||||
{
|
||||
Snackbar.Add($"Keine zentrale HANA-Konfiguration fuer {sourceSystem} gefunden.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var testServer = new HanaServer
|
||||
{
|
||||
SourceSystem = sourceSystem,
|
||||
Name = $"{sourceSystem} Central Test",
|
||||
Host = centralServer.Host,
|
||||
Port = centralServer.Port,
|
||||
Username = username.Trim(),
|
||||
Password = password.Trim(),
|
||||
DatabaseName = centralServer.DatabaseName,
|
||||
UseSsl = centralServer.UseSsl,
|
||||
ValidateCertificate = centralServer.ValidateCertificate,
|
||||
AdditionalParams = centralServer.AdditionalParams
|
||||
};
|
||||
|
||||
var result = await Task.Run(() => HanaService.TestConnectionDetailed(testServer));
|
||||
if (result.Success)
|
||||
{
|
||||
Snackbar.Add($"{sourceSystem}: Zentrale HANA-Verbindung erfolgreich.", Severity.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"{sourceSystem}: {result.ExceptionType} - {result.ErrorMessage}", Severity.Error);
|
||||
}
|
||||
var result = await SettingsPageActions.TestCentralCredentialsAsync(definition);
|
||||
Snackbar.Add(result.Message, result.Success ? Severity.Success : result.Warning ? Severity.Warning : Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -745,48 +573,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestCentralSapCredentials(SourceSystemDefinition definition)
|
||||
{
|
||||
var sourceSystem = definition.Code;
|
||||
if (!_testingSystems.Add(sourceSystem))
|
||||
return;
|
||||
private static string NormalizeSourceSystemCode(string? code) => Services.SettingsPageService.NormalizeSourceSystemCode(code);
|
||||
|
||||
try
|
||||
{
|
||||
var username = definition.CentralUsername;
|
||||
var password = definition.CentralPassword;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
Snackbar.Add("Für SAP sind keine zentralen Gateway-Zugangsdaten gepflegt.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(definition.CentralServiceUrl))
|
||||
{
|
||||
Snackbar.Add($"Fuer {sourceSystem} ist keine zentrale SAP Service URL gepflegt.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
await SapGatewayService.TestConnectionAsync(definition.CentralServiceUrl, username.Trim(), password.Trim());
|
||||
Snackbar.Add($"{sourceSystem}: Zentrale SAP Gateway-Verbindung erfolgreich.", Severity.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"{sourceSystem}: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_testingSystems.Remove(sourceSystem);
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeSourceSystemCode(string? code) => NormalizeConfigValue(code).ToUpperInvariant();
|
||||
|
||||
private static string NormalizeConnectionKind(string? connectionKind)
|
||||
=> SourceSystemConnectionKinds.All.Contains(connectionKind ?? string.Empty, StringComparer.OrdinalIgnoreCase)
|
||||
? (connectionKind ?? string.Empty).Trim().ToUpperInvariant()
|
||||
: SourceSystemConnectionKinds.Hana;
|
||||
private static string NormalizeConnectionKind(string? connectionKind) => Services.SettingsPageService.NormalizeConnectionKind(connectionKind);
|
||||
|
||||
private static string GetConnectionKindLabel(string connectionKind) => connectionKind switch
|
||||
{
|
||||
@@ -808,31 +597,6 @@
|
||||
private static string GetUsernameSummary(SourceSystemDefinition definition)
|
||||
=> string.IsNullOrWhiteSpace(definition.CentralUsername) ? "-" : definition.CentralUsername;
|
||||
|
||||
private static string NormalizeConfigValue(string? value) => value?.Trim() ?? string.Empty;
|
||||
|
||||
private static string BuildSharePointTestPreview(string tenantId, string clientId, string clientSecret, string siteUrl)
|
||||
{
|
||||
var maskedSecret = string.IsNullOrEmpty(clientSecret)
|
||||
? "<leer>"
|
||||
: $"{new string('*', Math.Min(clientSecret.Length, 8))} (len={clientSecret.Length})";
|
||||
|
||||
return string.Join(Environment.NewLine,
|
||||
[
|
||||
$"Tenant ID: {tenantId}",
|
||||
$"Client ID: {clientId}",
|
||||
$"Client Secret: {maskedSecret}",
|
||||
$"Site URL: {siteUrl}"
|
||||
]);
|
||||
}
|
||||
|
||||
private async Task<List<CurrencyExchangeRate>> LoadExchangeRatesAsync()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
return await db.CurrencyExchangeRates
|
||||
.OrderBy(x => x.FromCurrency)
|
||||
.ThenBy(x => x.ToCurrency)
|
||||
.ThenByDescending(x => x.ValidFrom)
|
||||
.ToListAsync();
|
||||
}
|
||||
private static string NormalizeConfigValue(string? value) => Services.SettingsPageService.NormalizeConfigValue(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
@page "/standorte"
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using System.Text.Json
|
||||
@using System.Reflection
|
||||
@using TrafagSalesExporter.Data
|
||||
@using TrafagSalesExporter.Models
|
||||
@using TrafagSalesExporter.Services
|
||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||
@inject IHanaQueryService HanaService
|
||||
@inject ISapGatewayService SapGatewayService
|
||||
@inject ISharePointUploadService SharePointService
|
||||
@inject IAppEventLogService AppEventLogService
|
||||
@inject IStandortePageService StandortePageService
|
||||
@inject IStandorteSapEditorService SapEditorService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
|
||||
@@ -466,16 +461,10 @@
|
||||
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
_sourceSystemDefinitions = await db.SourceSystemDefinitions
|
||||
.OrderBy(x => x.Code)
|
||||
.ToListAsync();
|
||||
_servers = await db.HanaServers
|
||||
.Where(s => GetHanaSourceSystemCodes().Contains(s.SourceSystem))
|
||||
.OrderBy(s => s.SourceSystem)
|
||||
.ThenBy(s => s.Name)
|
||||
.ToListAsync();
|
||||
_sites = await db.Sites.Include(s => s.HanaServer).OrderBy(s => s.Land).ToListAsync();
|
||||
var state = await StandortePageService.LoadAsync();
|
||||
_sourceSystemDefinitions = state.SourceSystems;
|
||||
_servers = state.Servers;
|
||||
_sites = state.Sites;
|
||||
}
|
||||
|
||||
private void EditServer(HanaServer server)
|
||||
@@ -492,61 +481,10 @@
|
||||
_savingServer = true;
|
||||
try
|
||||
{
|
||||
_editingServer.SourceSystem = string.IsNullOrWhiteSpace(_editingServer.SourceSystem)
|
||||
? GetHanaSourceSystemCodes().FirstOrDefault() ?? string.Empty
|
||||
: _editingServer.SourceSystem.Trim().ToUpperInvariant();
|
||||
_editingServer.Name = string.IsNullOrWhiteSpace(_editingServer.Name) ? _editingServer.SourceSystem : _editingServer.Name.Trim();
|
||||
_editingServer.Host = _editingServer.Host.Trim();
|
||||
_editingServer.DatabaseName = _editingServer.DatabaseName.Trim();
|
||||
_editingServer.AdditionalParams = _editingServer.AdditionalParams.Trim();
|
||||
_editingServer.Username = string.Empty;
|
||||
_editingServer.Password = string.Empty;
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
if (_editingServer.Id == 0)
|
||||
{
|
||||
var existingForSourceSystem = await db.HanaServers
|
||||
.OrderBy(x => x.Id)
|
||||
.FirstOrDefaultAsync(x => x.SourceSystem == _editingServer.SourceSystem);
|
||||
|
||||
if (existingForSourceSystem is null)
|
||||
{
|
||||
db.HanaServers.Add(_editingServer);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingForSourceSystem.Name = _editingServer.Name;
|
||||
existingForSourceSystem.Host = _editingServer.Host;
|
||||
existingForSourceSystem.Port = _editingServer.Port;
|
||||
existingForSourceSystem.Username = string.Empty;
|
||||
existingForSourceSystem.Password = string.Empty;
|
||||
existingForSourceSystem.DatabaseName = _editingServer.DatabaseName;
|
||||
existingForSourceSystem.UseSsl = _editingServer.UseSsl;
|
||||
existingForSourceSystem.ValidateCertificate = _editingServer.ValidateCertificate;
|
||||
existingForSourceSystem.AdditionalParams = _editingServer.AdditionalParams;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var existing = await db.HanaServers.FindAsync(_editingServer.Id);
|
||||
if (existing is not null)
|
||||
{
|
||||
existing.SourceSystem = _editingServer.SourceSystem;
|
||||
existing.Name = _editingServer.Name;
|
||||
existing.Host = _editingServer.Host;
|
||||
existing.Port = _editingServer.Port;
|
||||
existing.Username = string.Empty;
|
||||
existing.Password = string.Empty;
|
||||
existing.DatabaseName = _editingServer.DatabaseName;
|
||||
existing.UseSsl = _editingServer.UseSsl;
|
||||
existing.ValidateCertificate = _editingServer.ValidateCertificate;
|
||||
existing.AdditionalParams = _editingServer.AdditionalParams;
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
_serverDialogVisible = false;
|
||||
await LoadDataAsync();
|
||||
Snackbar.Add("Server gespeichert", Severity.Success);
|
||||
await StandortePageService.SaveServerAsync(_editingServer, GetHanaSourceSystemCodes());
|
||||
_serverDialogVisible = false;
|
||||
await LoadDataAsync();
|
||||
Snackbar.Add("Server gespeichert", Severity.Success);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -571,27 +509,7 @@
|
||||
|
||||
try
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var linkedSites = await db.Sites
|
||||
.Where(s => s.HanaServerId == server.Id)
|
||||
.OrderBy(s => s.Land)
|
||||
.Select(s => $"{s.Land} ({s.TSC})")
|
||||
.ToListAsync();
|
||||
|
||||
if (linkedSites.Count > 0)
|
||||
{
|
||||
Snackbar.Add(
|
||||
$"Server kann nicht gelöscht werden. Noch verknüpfte Standorte: {string.Join(", ", linkedSites)}",
|
||||
Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = await db.HanaServers.FindAsync(server.Id);
|
||||
if (entity is not null)
|
||||
{
|
||||
db.HanaServers.Remove(entity);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
await StandortePageService.DeleteServerAsync(server);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -605,50 +523,19 @@
|
||||
|
||||
private async Task TestServerConnection(HanaServer server)
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var sourceDefinition = await db.SourceSystemDefinitions
|
||||
.OrderBy(x => x.Id)
|
||||
.FirstOrDefaultAsync(x => x.Code == server.SourceSystem);
|
||||
|
||||
if (sourceDefinition is null)
|
||||
try
|
||||
{
|
||||
Snackbar.Add($"Quellsystem '{server.SourceSystem}' nicht gefunden.", Severity.Warning);
|
||||
return;
|
||||
var result = await StandortePageService.TestServerConnectionAsync(server);
|
||||
_connectionStatus[server.Id] = result;
|
||||
Snackbar.Add(
|
||||
result.Success
|
||||
? $"Verbindung zu '{server.Name}' erfolgreich."
|
||||
: $"{server.Name}: {result.ExceptionType} - {result.ErrorMessage}",
|
||||
result.Success ? Severity.Success : Severity.Error);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(sourceDefinition.CentralUsername) || string.IsNullOrWhiteSpace(sourceDefinition.CentralPassword))
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Fuer {server.SourceSystem} sind keine zentralen Zugangsdaten im Quellsystem gepflegt.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var testServer = new HanaServer
|
||||
{
|
||||
Id = server.Id,
|
||||
SourceSystem = server.SourceSystem,
|
||||
Name = server.Name,
|
||||
Host = server.Host,
|
||||
Port = server.Port,
|
||||
Username = sourceDefinition.CentralUsername.Trim(),
|
||||
Password = sourceDefinition.CentralPassword,
|
||||
DatabaseName = server.DatabaseName,
|
||||
UseSsl = server.UseSsl,
|
||||
ValidateCertificate = server.ValidateCertificate,
|
||||
AdditionalParams = server.AdditionalParams
|
||||
};
|
||||
|
||||
await AppEventLogService.WriteAsync("HANA", "Server-Test aus UI gestartet",
|
||||
details: testServer.GetConnectionStringPreview());
|
||||
var result = await Task.Run(() => HanaService.TestConnectionDetailed(testServer));
|
||||
_connectionStatus[server.Id] = result;
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
Snackbar.Add($"Verbindung zu '{server.Name}' erfolgreich.", Severity.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"{server.Name}: {result.ExceptionType} - {result.ErrorMessage}", Severity.Error);
|
||||
Snackbar.Add(ex.Message, Severity.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,33 +569,18 @@
|
||||
|
||||
private void EditSite(Site site)
|
||||
{
|
||||
_editingSite = new Site
|
||||
{
|
||||
Id = site.Id,
|
||||
HanaServerId = site.HanaServerId,
|
||||
Schema = site.Schema,
|
||||
TSC = site.TSC,
|
||||
Land = site.Land,
|
||||
SourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem)
|
||||
? GetAvailableSourceSystems().FirstOrDefault()?.Code ?? "SAP"
|
||||
: site.SourceSystem,
|
||||
UsernameOverride = site.UsernameOverride,
|
||||
PasswordOverride = site.PasswordOverride,
|
||||
LocalExportFolderOverride = site.LocalExportFolderOverride,
|
||||
ManualImportFilePath = site.ManualImportFilePath,
|
||||
ManualImportLastUploadedAtUtc = site.ManualImportLastUploadedAtUtc,
|
||||
SapServiceUrl = site.SapServiceUrl,
|
||||
SapEntitySet = site.SapEntitySet,
|
||||
SapEntitySetsCache = site.SapEntitySetsCache,
|
||||
SapEntitySetsRefreshedAtUtc = site.SapEntitySetsRefreshedAtUtc,
|
||||
IsActive = site.IsActive
|
||||
};
|
||||
_ = EditSiteAsync(site);
|
||||
}
|
||||
|
||||
private async Task EditSiteAsync(Site site)
|
||||
{
|
||||
var editorState = await StandortePageService.LoadSiteEditorAsync(site, GetAvailableSourceSystems());
|
||||
_editingSite = editorState.Site;
|
||||
_availableSchemas = [];
|
||||
_sapEntitySetsCache = ParseSapEntitySets(site.SapEntitySetsCache);
|
||||
using var db = DbFactory.CreateDbContext();
|
||||
_sapSources = db.SapSourceDefinitions.Where(s => s.SiteId == site.Id).OrderBy(s => s.SortOrder).ThenBy(s => s.Id).ToList();
|
||||
_sapJoins = db.SapJoinDefinitions.Where(j => j.SiteId == site.Id).OrderBy(j => j.SortOrder).ThenBy(j => j.Id).ToList();
|
||||
_sapMappings = db.SapFieldMappings.Where(m => m.SiteId == site.Id).OrderBy(m => m.SortOrder).ThenBy(m => m.Id).ToList();
|
||||
_sapEntitySetsCache = editorState.SapEntitySets;
|
||||
_sapSources = editorState.SapSources;
|
||||
_sapJoins = editorState.SapJoins;
|
||||
_sapMappings = editorState.SapMappings;
|
||||
_sapAvailableSourceExpressions = BuildSourceExpressionsFromMappings();
|
||||
_sapSourceFieldMap = BuildSourceFieldMapFromJoins();
|
||||
_siteDialogVisible = true;
|
||||
@@ -722,40 +594,7 @@
|
||||
_savingSite = true;
|
||||
try
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var serverId = UsesHanaConnection() ? await ResolveCentralHanaServerIdAsync(db, _editingSite.SourceSystem) : (int?)null;
|
||||
_editingSite.HanaServerId = serverId;
|
||||
_editingSite.SapEntitySetsCache = SerializeSapEntitySets(_sapEntitySetsCache);
|
||||
|
||||
if (_editingSite.Id == 0)
|
||||
{
|
||||
db.Sites.Add(_editingSite);
|
||||
}
|
||||
else
|
||||
{
|
||||
var existing = await db.Sites.FindAsync(_editingSite.Id);
|
||||
if (existing is not null)
|
||||
{
|
||||
existing.HanaServerId = serverId;
|
||||
existing.Schema = _editingSite.Schema;
|
||||
existing.TSC = _editingSite.TSC;
|
||||
existing.Land = _editingSite.Land;
|
||||
existing.SourceSystem = _editingSite.SourceSystem;
|
||||
existing.UsernameOverride = _editingSite.UsernameOverride;
|
||||
existing.PasswordOverride = _editingSite.PasswordOverride;
|
||||
existing.LocalExportFolderOverride = _editingSite.LocalExportFolderOverride;
|
||||
existing.ManualImportFilePath = _editingSite.ManualImportFilePath;
|
||||
existing.ManualImportLastUploadedAtUtc = _editingSite.ManualImportLastUploadedAtUtc;
|
||||
existing.SapServiceUrl = _editingSite.SapServiceUrl;
|
||||
existing.SapEntitySet = _editingSite.SapEntitySet;
|
||||
existing.SapEntitySetsCache = _editingSite.SapEntitySetsCache;
|
||||
existing.SapEntitySetsRefreshedAtUtc = _editingSite.SapEntitySetsRefreshedAtUtc;
|
||||
existing.IsActive = _editingSite.IsActive;
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await SaveSapConfigurationAsync(db, _editingSite.Id);
|
||||
await StandortePageService.SaveSiteAsync(_editingSite, UsesHanaConnection(), IsSapSite(), _sapSources, _sapJoins, _sapMappings, _sapEntitySetsCache);
|
||||
_siteDialogVisible = false;
|
||||
await LoadDataAsync();
|
||||
Snackbar.Add("Standort gespeichert", Severity.Success);
|
||||
@@ -779,22 +618,7 @@
|
||||
|
||||
if (result != true) return;
|
||||
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Sites.FindAsync(site.Id);
|
||||
if (entity is not null)
|
||||
{
|
||||
var sources = await db.SapSourceDefinitions.Where(s => s.SiteId == site.Id).ToListAsync();
|
||||
var joins = await db.SapJoinDefinitions.Where(j => j.SiteId == site.Id).ToListAsync();
|
||||
var mappings = await db.SapFieldMappings.Where(m => m.SiteId == site.Id).ToListAsync();
|
||||
var centralRows = await db.CentralSalesRecords.Where(r => r.SiteId == site.Id).ToListAsync();
|
||||
if (sources.Count > 0) db.SapSourceDefinitions.RemoveRange(sources);
|
||||
if (joins.Count > 0) db.SapJoinDefinitions.RemoveRange(joins);
|
||||
if (mappings.Count > 0) db.SapFieldMappings.RemoveRange(mappings);
|
||||
if (centralRows.Count > 0) db.CentralSalesRecords.RemoveRange(centralRows);
|
||||
db.Sites.Remove(entity);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await StandortePageService.DeleteSiteAsync(site);
|
||||
await LoadDataAsync();
|
||||
Snackbar.Add("Standort gelöscht", Severity.Info);
|
||||
}
|
||||
@@ -825,26 +649,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<int> ResolveCentralHanaServerIdAsync(AppDbContext db, string sourceSystem)
|
||||
{
|
||||
_editingSite.UsernameOverride = _editingSite.UsernameOverride.Trim();
|
||||
_editingSite.PasswordOverride = _editingSite.PasswordOverride.Trim();
|
||||
_editingSite.LocalExportFolderOverride = _editingSite.LocalExportFolderOverride.Trim();
|
||||
_editingSite.ManualImportFilePath = _editingSite.ManualImportFilePath.Trim();
|
||||
_editingSite.SapServiceUrl = _editingSite.SapServiceUrl.Trim();
|
||||
_editingSite.SapEntitySet = _editingSite.SapEntitySet.Trim();
|
||||
|
||||
var normalizedSourceSystem = string.IsNullOrWhiteSpace(sourceSystem) ? string.Empty : sourceSystem.Trim().ToUpperInvariant();
|
||||
var centralServer = await db.HanaServers
|
||||
.OrderBy(x => x.Id)
|
||||
.FirstOrDefaultAsync(x => x.SourceSystem == normalizedSourceSystem);
|
||||
|
||||
if (centralServer is null || string.IsNullOrWhiteSpace(centralServer.Host))
|
||||
throw new InvalidOperationException($"Fuer Quellsystem '{normalizedSourceSystem}' ist keine gueltige zentrale HANA-Konfiguration vorhanden.");
|
||||
|
||||
return centralServer.Id;
|
||||
}
|
||||
|
||||
private Task OnSchemaSelected(string schema)
|
||||
{
|
||||
_editingSite.Schema = schema;
|
||||
@@ -939,52 +743,7 @@
|
||||
_loadingSchemas = true;
|
||||
try
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var sourceDefinition = await db.SourceSystemDefinitions
|
||||
.OrderBy(x => x.Id)
|
||||
.FirstOrDefaultAsync(x => x.Code == _editingSite.SourceSystem);
|
||||
|
||||
if (sourceDefinition is null)
|
||||
throw new InvalidOperationException($"Quellsystem '{_editingSite.SourceSystem}' nicht gefunden.");
|
||||
|
||||
var centralServer = await db.HanaServers
|
||||
.OrderBy(x => x.Id)
|
||||
.FirstOrDefaultAsync(x => x.SourceSystem == _editingSite.SourceSystem);
|
||||
|
||||
if (centralServer is null || string.IsNullOrWhiteSpace(centralServer.Host))
|
||||
throw new InvalidOperationException($"Fuer {_editingSite.SourceSystem} ist keine gueltige zentrale HANA-Konfiguration vorhanden.");
|
||||
|
||||
var username = string.IsNullOrWhiteSpace(_editingSite.UsernameOverride)
|
||||
? sourceDefinition.CentralUsername ?? string.Empty
|
||||
: _editingSite.UsernameOverride;
|
||||
var password = string.IsNullOrWhiteSpace(_editingSite.PasswordOverride)
|
||||
? sourceDefinition.CentralPassword ?? string.Empty
|
||||
: _editingSite.PasswordOverride;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||
throw new InvalidOperationException($"Fuer {_editingSite.SourceSystem} sind weder zentrale Zugangsdaten noch Standort-Overrides gesetzt.");
|
||||
|
||||
var lookupServer = new HanaServer
|
||||
{
|
||||
Id = centralServer.Id,
|
||||
SourceSystem = centralServer.SourceSystem,
|
||||
Name = centralServer.Name,
|
||||
Host = centralServer.Host,
|
||||
Port = centralServer.Port,
|
||||
Username = username.Trim(),
|
||||
Password = password,
|
||||
DatabaseName = centralServer.DatabaseName,
|
||||
UseSsl = centralServer.UseSsl,
|
||||
ValidateCertificate = centralServer.ValidateCertificate,
|
||||
AdditionalParams = centralServer.AdditionalParams
|
||||
};
|
||||
|
||||
var schemas = await Task.Run(() => HanaService.GetAvailableSchemas(lookupServer));
|
||||
_availableSchemas = schemas
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
_availableSchemas = await StandortePageService.LoadAvailableSchemasAsync(_editingSite);
|
||||
|
||||
if (_availableSchemas.Count == 0)
|
||||
{
|
||||
@@ -1018,31 +777,10 @@
|
||||
_refreshingSapEntitySets = true;
|
||||
try
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var sourceDefinition = await db.SourceSystemDefinitions
|
||||
.OrderBy(x => x.Id)
|
||||
.FirstOrDefaultAsync(x => x.Code == _editingSite.SourceSystem);
|
||||
var serviceUrl = string.IsNullOrWhiteSpace(_editingSite.SapServiceUrl)
|
||||
? sourceDefinition?.CentralServiceUrl ?? string.Empty
|
||||
: _editingSite.SapServiceUrl;
|
||||
if (string.IsNullOrWhiteSpace(serviceUrl))
|
||||
throw new InvalidOperationException("Es ist weder eine zentrale SAP Service URL noch ein Standort-Override gesetzt.");
|
||||
var username = string.IsNullOrWhiteSpace(_editingSite.UsernameOverride)
|
||||
? sourceDefinition?.CentralUsername ?? string.Empty
|
||||
: _editingSite.UsernameOverride;
|
||||
var password = string.IsNullOrWhiteSpace(_editingSite.PasswordOverride)
|
||||
? sourceDefinition?.CentralPassword ?? string.Empty
|
||||
: _editingSite.PasswordOverride;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||
throw new InvalidOperationException("Für SAP sind weder zentrale Zugangsdaten noch Standort-Overrides gesetzt.");
|
||||
|
||||
await AppEventLogService.WriteAsync("SAP", "Refresh aus UI gestartet", siteId: _editingSite.Id, land: _editingSite.Land,
|
||||
details: serviceUrl);
|
||||
var entitySets = await SapGatewayService.GetEntitySetsAsync(serviceUrl, username.Trim(), password.Trim());
|
||||
_sapEntitySetsCache = entitySets;
|
||||
_editingSite.SapEntitySetsCache = SerializeSapEntitySets(entitySets);
|
||||
_editingSite.SapEntitySetsRefreshedAtUtc = DateTime.UtcNow;
|
||||
var result = await StandortePageService.RefreshSapEntitySetsAsync(_editingSite);
|
||||
_sapEntitySetsCache = result.EntitySets;
|
||||
_editingSite.SapEntitySetsCache = SerializeSapEntitySets(result.EntitySets);
|
||||
_editingSite.SapEntitySetsRefreshedAtUtc = result.RefreshedAtUtc;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_editingSite.SapEntitySet) &&
|
||||
!_sapEntitySetsCache.Contains(_editingSite.SapEntitySet, StringComparer.OrdinalIgnoreCase))
|
||||
@@ -1050,15 +788,11 @@
|
||||
_editingSite.SapEntitySet = string.Empty;
|
||||
}
|
||||
|
||||
Snackbar.Add($"{entitySets.Count} SAP Entity Sets geladen.", Severity.Success);
|
||||
await AppEventLogService.WriteAsync("SAP", "Refresh aus UI erfolgreich", siteId: _editingSite.Id, land: _editingSite.Land,
|
||||
details: $"EntitySets={entitySets.Count}");
|
||||
Snackbar.Add($"{result.EntitySets.Count} SAP Entity Sets geladen.", Severity.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add(ex.Message, Severity.Error);
|
||||
await AppEventLogService.WriteAsync("SAP", "Refresh aus UI fehlgeschlagen", "Error", siteId: _editingSite.Id, land: _editingSite.Land,
|
||||
details: ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1119,12 +853,10 @@
|
||||
_editingSite.ManualImportFilePath = targetPath;
|
||||
_editingSite.ManualImportLastUploadedAtUtc = DateTime.UtcNow;
|
||||
Snackbar.Add("Excel-Datei hochgeladen.", Severity.Success);
|
||||
await AppEventLogService.WriteAsync("ManualImport", "Excel-Datei hochgeladen", siteId: _editingSite.Id, land: _editingSite.Land, details: targetPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Upload fehlgeschlagen: {ex.Message}", Severity.Error);
|
||||
await AppEventLogService.WriteAsync("ManualImport", "Excel-Upload fehlgeschlagen", "Error", siteId: _editingSite.Id, land: _editingSite.Land, details: ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1136,55 +868,12 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_editingSite.ManualImportFilePath = _editingSite.ManualImportFilePath.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_editingSite.ManualImportFilePath))
|
||||
throw new InvalidOperationException("Bitte zuerst einen Dateipfad eintragen.");
|
||||
|
||||
if (!string.Equals(Path.GetExtension(_editingSite.ManualImportFilePath), ".xlsx", StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException("Bitte eine Excel-Datei mit Endung .xlsx angeben.");
|
||||
|
||||
if (File.Exists(_editingSite.ManualImportFilePath))
|
||||
{
|
||||
_editingSite.ManualImportLastUploadedAtUtc = File.GetLastWriteTimeUtc(_editingSite.ManualImportFilePath);
|
||||
}
|
||||
else if (LooksLikeSharePointReference(_editingSite.ManualImportFilePath))
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var spConfig = await db.SharePointConfigs.FirstOrDefaultAsync();
|
||||
if (spConfig is null ||
|
||||
string.IsNullOrWhiteSpace(spConfig.TenantId) ||
|
||||
string.IsNullOrWhiteSpace(spConfig.ClientId) ||
|
||||
string.IsNullOrWhiteSpace(spConfig.ClientSecret) ||
|
||||
string.IsNullOrWhiteSpace(spConfig.SiteUrl))
|
||||
{
|
||||
throw new InvalidOperationException("Fuer SharePoint-Pruefung fehlt eine vollstaendige SharePoint-Konfiguration in Settings.");
|
||||
}
|
||||
|
||||
var tempPath = await SharePointService.DownloadToTempFileAsync(
|
||||
spConfig.TenantId, spConfig.ClientId, spConfig.ClientSecret, spConfig.SiteUrl, _editingSite.ManualImportFilePath);
|
||||
try
|
||||
{
|
||||
_editingSite.ManualImportLastUploadedAtUtc = File.GetLastWriteTimeUtc(tempPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(tempPath))
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Datei nicht gefunden oder nicht erreichbar: {_editingSite.ManualImportFilePath}");
|
||||
}
|
||||
|
||||
_editingSite.ManualImportLastUploadedAtUtc = await StandortePageService.ValidateManualImportPathAsync(_editingSite.ManualImportFilePath);
|
||||
Snackbar.Add("Dateipfad ist gueltig und die Excel-Datei ist erreichbar.", Severity.Success);
|
||||
await AppEventLogService.WriteAsync("ManualImport", "Dateipfad erfolgreich geprueft", siteId: _editingSite.Id, land: _editingSite.Land, details: _editingSite.ManualImportFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Pfadpruefung fehlgeschlagen: {ex.Message}", Severity.Error);
|
||||
await AppEventLogService.WriteAsync("ManualImport", "Dateipfadpruefung fehlgeschlagen", "Error", siteId: _editingSite.Id, land: _editingSite.Land, details: ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1206,182 +895,48 @@
|
||||
private static string SerializeSapEntitySets(List<string> entitySets)
|
||||
=> JsonSerializer.Serialize(entitySets);
|
||||
|
||||
private static bool LooksLikeSharePointReference(string path)
|
||||
=> path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.StartsWith("/Shared Documents/", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.StartsWith("Shared Documents/", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private void AddSapSource()
|
||||
{
|
||||
_sapSources.Add(new SapSourceDefinition
|
||||
{
|
||||
Alias = $"SRC{_sapSources.Count + 1}",
|
||||
EntitySet = _sapEntitySetsCache.FirstOrDefault() ?? string.Empty,
|
||||
IsActive = true,
|
||||
IsPrimary = _sapSources.Count == 0,
|
||||
SortOrder = _sapSources.Count
|
||||
});
|
||||
SapEditorService.AddSapSource(_sapSources, _sapEntitySetsCache);
|
||||
}
|
||||
|
||||
private void RemoveSapSource(SapSourceDefinition source)
|
||||
{
|
||||
_sapSources.Remove(source);
|
||||
SapEditorService.RemoveSapSource(_sapSources, source);
|
||||
}
|
||||
|
||||
private void AddSapJoin()
|
||||
{
|
||||
_sapJoins.Add(new SapJoinDefinition
|
||||
{
|
||||
JoinType = "Left",
|
||||
IsActive = true,
|
||||
SortOrder = _sapJoins.Count
|
||||
});
|
||||
SapEditorService.AddSapJoin(_sapJoins);
|
||||
}
|
||||
|
||||
private void AutoMatchSapJoins()
|
||||
{
|
||||
var activeSources = _sapSources
|
||||
.Where(s => s.IsActive && !string.IsNullOrWhiteSpace(s.Alias))
|
||||
.OrderBy(s => s.SortOrder)
|
||||
.ThenBy(s => s.Id)
|
||||
.ToList();
|
||||
|
||||
if (activeSources.Count < 2)
|
||||
{
|
||||
Snackbar.Add("Für Auto-Match werden mindestens zwei aktive SAP-Quellen benötigt.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sapSourceFieldMap.Count == 0)
|
||||
{
|
||||
Snackbar.Add("Bitte zuerst 'Felder aus Quellen laden' ausführen.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var primary = activeSources.FirstOrDefault(s => s.IsPrimary) ?? activeSources.First();
|
||||
var createdOrUpdated = 0;
|
||||
|
||||
foreach (var source in activeSources.Where(s => !string.Equals(s.Alias, primary.Alias, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (!_sapSourceFieldMap.TryGetValue(primary.Alias, out var leftFields) || leftFields.Count == 0)
|
||||
continue;
|
||||
if (!_sapSourceFieldMap.TryGetValue(source.Alias, out var rightFields) || rightFields.Count == 0)
|
||||
continue;
|
||||
|
||||
var matchingFields = leftFields
|
||||
.Intersect(rightFields, StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (matchingFields.Count == 0)
|
||||
continue;
|
||||
|
||||
var existingJoin = _sapJoins.FirstOrDefault(j =>
|
||||
string.Equals(j.LeftAlias, primary.Alias, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(j.RightAlias, source.Alias, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var keyList = string.Join(',', matchingFields);
|
||||
if (existingJoin is null)
|
||||
{
|
||||
_sapJoins.Add(new SapJoinDefinition
|
||||
{
|
||||
LeftAlias = primary.Alias,
|
||||
RightAlias = source.Alias,
|
||||
LeftKeys = keyList,
|
||||
RightKeys = keyList,
|
||||
JoinType = "Left",
|
||||
IsActive = true,
|
||||
SortOrder = _sapJoins.Count
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
existingJoin.LeftKeys = keyList;
|
||||
existingJoin.RightKeys = keyList;
|
||||
existingJoin.JoinType = "Left";
|
||||
existingJoin.IsActive = true;
|
||||
}
|
||||
|
||||
createdOrUpdated++;
|
||||
}
|
||||
|
||||
if (createdOrUpdated == 0)
|
||||
{
|
||||
Snackbar.Add("Kein passender Join-Vorschlag gefunden.", Severity.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
NormalizeSapConfigCollections();
|
||||
Snackbar.Add($"{createdOrUpdated} Join-Vorschläge gesetzt.", Severity.Success);
|
||||
var result = SapEditorService.AutoMatchSapJoins(_sapSources, _sapJoins, _sapSourceFieldMap);
|
||||
SapEditorService.NormalizeSapConfigCollections(_sapSources, _sapJoins, _sapMappings);
|
||||
Snackbar.Add(result.Message, result.Success ? Severity.Success : result.Warning ? Severity.Warning : Severity.Info);
|
||||
}
|
||||
|
||||
private void RemoveSapJoin(SapJoinDefinition join)
|
||||
{
|
||||
_sapJoins.Remove(join);
|
||||
SapEditorService.RemoveSapJoin(_sapJoins, join);
|
||||
}
|
||||
|
||||
private void AddSapMapping()
|
||||
{
|
||||
_sapMappings.Add(new SapFieldMapping
|
||||
{
|
||||
TargetField = _salesRecordFields.First(),
|
||||
SourceExpression = _sapAvailableSourceExpressions.FirstOrDefault() ?? "=SAP",
|
||||
IsActive = true,
|
||||
SortOrder = _sapMappings.Count
|
||||
});
|
||||
SapEditorService.AddSapMapping(_sapMappings, _salesRecordFields, _sapAvailableSourceExpressions);
|
||||
}
|
||||
|
||||
private void RemoveSapMapping(SapFieldMapping mapping)
|
||||
{
|
||||
_sapMappings.Remove(mapping);
|
||||
SapEditorService.RemoveSapMapping(_sapMappings, mapping);
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetSapAliases()
|
||||
=> _sapSources.Where(s => !string.IsNullOrWhiteSpace(s.Alias)).Select(s => s.Alias).Distinct(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private async Task SaveSapConfigurationAsync(AppDbContext db, int siteId)
|
||||
{
|
||||
var oldSources = await db.SapSourceDefinitions.Where(s => s.SiteId == siteId).ToListAsync();
|
||||
var oldJoins = await db.SapJoinDefinitions.Where(j => j.SiteId == siteId).ToListAsync();
|
||||
var oldMappings = await db.SapFieldMappings.Where(m => m.SiteId == siteId).ToListAsync();
|
||||
if (oldSources.Count > 0) db.SapSourceDefinitions.RemoveRange(oldSources);
|
||||
if (oldJoins.Count > 0) db.SapJoinDefinitions.RemoveRange(oldJoins);
|
||||
if (oldMappings.Count > 0) db.SapFieldMappings.RemoveRange(oldMappings);
|
||||
|
||||
if (IsSapSite())
|
||||
{
|
||||
NormalizeSapConfigCollections();
|
||||
foreach (var source in _sapSources)
|
||||
source.SiteId = siteId;
|
||||
foreach (var join in _sapJoins)
|
||||
join.SiteId = siteId;
|
||||
foreach (var mapping in _sapMappings)
|
||||
mapping.SiteId = siteId;
|
||||
db.SapSourceDefinitions.AddRange(_sapSources);
|
||||
db.SapJoinDefinitions.AddRange(_sapJoins);
|
||||
db.SapFieldMappings.AddRange(_sapMappings);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
=> SapEditorService.GetSapAliases(_sapSources);
|
||||
|
||||
private void NormalizeSapConfigCollections()
|
||||
{
|
||||
for (var i = 0; i < _sapSources.Count; i++)
|
||||
_sapSources[i].SortOrder = i;
|
||||
for (var i = 0; i < _sapJoins.Count; i++)
|
||||
_sapJoins[i].SortOrder = i;
|
||||
for (var i = 0; i < _sapMappings.Count; i++)
|
||||
_sapMappings[i].SortOrder = i;
|
||||
|
||||
var selectedPrimaryIndex = _sapSources.FindIndex(s => s.IsPrimary);
|
||||
var primarySource = selectedPrimaryIndex >= 0 ? _sapSources[selectedPrimaryIndex] : _sapSources.FirstOrDefault();
|
||||
foreach (var source in _sapSources)
|
||||
source.IsPrimary = primarySource is not null && ReferenceEquals(source, primarySource);
|
||||
if (_sapSources.Count > 0 && _sapSources.All(s => !s.IsPrimary))
|
||||
_sapSources[0].IsPrimary = true;
|
||||
}
|
||||
=> SapEditorService.NormalizeSapConfigCollections(_sapSources, _sapJoins, _sapMappings);
|
||||
|
||||
private async Task RefreshSapSourceFields()
|
||||
{
|
||||
@@ -1400,51 +955,9 @@
|
||||
if (activeSources.Count == 0)
|
||||
throw new InvalidOperationException("Es gibt keine aktiven SAP-Quellen mit Alias und Entity Set.");
|
||||
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var sourceDefinition = await db.SourceSystemDefinitions
|
||||
.OrderBy(x => x.Id)
|
||||
.FirstOrDefaultAsync(x => x.Code == _editingSite.SourceSystem);
|
||||
var serviceUrl = string.IsNullOrWhiteSpace(_editingSite.SapServiceUrl)
|
||||
? sourceDefinition?.CentralServiceUrl ?? string.Empty
|
||||
: _editingSite.SapServiceUrl;
|
||||
if (string.IsNullOrWhiteSpace(serviceUrl))
|
||||
throw new InvalidOperationException("Es ist weder eine zentrale SAP Service URL noch ein Standort-Override gesetzt.");
|
||||
var username = string.IsNullOrWhiteSpace(_editingSite.UsernameOverride)
|
||||
? sourceDefinition?.CentralUsername ?? string.Empty
|
||||
: _editingSite.UsernameOverride;
|
||||
var password = string.IsNullOrWhiteSpace(_editingSite.PasswordOverride)
|
||||
? sourceDefinition?.CentralPassword ?? string.Empty
|
||||
: _editingSite.PasswordOverride;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||
throw new InvalidOperationException("Für SAP sind weder zentrale Zugangsdaten noch Standort-Overrides gesetzt.");
|
||||
|
||||
var expressions = new List<string> { "=SAP" };
|
||||
var sourceFieldMap = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var source in activeSources)
|
||||
{
|
||||
var fieldNames = await SapGatewayService.GetEntityFieldNamesAsync(serviceUrl, source.EntitySet, username.Trim(), password.Trim());
|
||||
sourceFieldMap[source.Alias] = fieldNames;
|
||||
expressions.AddRange(fieldNames.Select(field => $"{source.Alias}.{field}"));
|
||||
}
|
||||
|
||||
_sapAvailableSourceExpressions = expressions
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
_sapSourceFieldMap = sourceFieldMap;
|
||||
|
||||
foreach (var current in BuildSourceExpressionsFromMappings())
|
||||
{
|
||||
if (!_sapAvailableSourceExpressions.Contains(current, StringComparer.OrdinalIgnoreCase))
|
||||
_sapAvailableSourceExpressions.Add(current);
|
||||
}
|
||||
|
||||
_sapAvailableSourceExpressions = _sapAvailableSourceExpressions
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
var result = await StandortePageService.RefreshSapSourceFieldsAsync(_editingSite, activeSources, _sapMappings);
|
||||
_sapAvailableSourceExpressions = result.SourceExpressions;
|
||||
_sapSourceFieldMap = result.SourceFieldMap;
|
||||
|
||||
Snackbar.Add($"{_sapAvailableSourceExpressions.Count} Source Expressions geladen.", Severity.Success);
|
||||
}
|
||||
@@ -1459,73 +972,16 @@
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetAvailableSourceExpressions(string? currentValue)
|
||||
{
|
||||
var expressions = new List<string>(_sapAvailableSourceExpressions);
|
||||
if (!string.IsNullOrWhiteSpace(currentValue) && !expressions.Contains(currentValue, StringComparer.OrdinalIgnoreCase))
|
||||
expressions.Insert(0, currentValue);
|
||||
|
||||
return expressions;
|
||||
}
|
||||
=> SapEditorService.GetAvailableSourceExpressions(_sapAvailableSourceExpressions, currentValue);
|
||||
|
||||
private List<string> BuildSourceExpressionsFromMappings()
|
||||
=> _sapMappings
|
||||
.Select(m => m.SourceExpression)
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
=> SapEditorService.BuildSourceExpressionsFromMappings(_sapMappings);
|
||||
|
||||
private Dictionary<string, List<string>> BuildSourceFieldMapFromJoins()
|
||||
{
|
||||
var result = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var join in _sapJoins)
|
||||
{
|
||||
AddJoinKeysToFieldMap(result, join.LeftAlias, join.LeftKeys);
|
||||
AddJoinKeysToFieldMap(result, join.RightAlias, join.RightKeys);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AddJoinKeysToFieldMap(Dictionary<string, List<string>> target, string alias, string keys)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(alias))
|
||||
return;
|
||||
|
||||
if (!target.TryGetValue(alias, out var fields))
|
||||
{
|
||||
fields = [];
|
||||
target[alias] = fields;
|
||||
}
|
||||
|
||||
foreach (var key in GetSelectedJoinKeys(keys))
|
||||
{
|
||||
if (!fields.Contains(key, StringComparer.OrdinalIgnoreCase))
|
||||
fields.Add(key);
|
||||
}
|
||||
|
||||
fields.Sort(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
=> SapEditorService.BuildSourceFieldMapFromJoins(_sapJoins);
|
||||
|
||||
private IEnumerable<string> GetAvailableJoinFields(string? alias, string? currentKeys)
|
||||
{
|
||||
var values = new List<string>();
|
||||
if (!string.IsNullOrWhiteSpace(alias) && _sapSourceFieldMap.TryGetValue(alias, out var fields))
|
||||
values.AddRange(fields);
|
||||
|
||||
foreach (var key in GetSelectedJoinKeys(currentKeys))
|
||||
{
|
||||
if (!values.Contains(key, StringComparer.OrdinalIgnoreCase))
|
||||
values.Add(key);
|
||||
}
|
||||
|
||||
return values
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
=> SapEditorService.GetAvailableJoinFields(_sapSourceFieldMap, alias, currentKeys);
|
||||
|
||||
private static HashSet<string> GetSelectedJoinKeys(string? keys)
|
||||
=> keys?
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
@page "/transformations"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using System.Reflection
|
||||
@using TrafagSalesExporter.Data
|
||||
@using TrafagSalesExporter.Models
|
||||
@using TrafagSalesExporter.Services
|
||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||
@inject ITransformationsPageService TransformationsPageActions
|
||||
@inject ITransformationCatalog TransformationCatalog
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IUiTextService UiText
|
||||
@@ -199,9 +197,9 @@
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
_sourceSystems = await db.SourceSystemDefinitions.OrderBy(x => x.Code).ToListAsync();
|
||||
_rules = await db.FieldTransformationRules.OrderBy(r => r.SortOrder).ThenBy(r => r.Id).ToListAsync();
|
||||
var state = await TransformationsPageActions.LoadAsync();
|
||||
_sourceSystems = state.SourceSystems;
|
||||
_rules = state.Rules;
|
||||
|
||||
foreach (var rule in _rules)
|
||||
{
|
||||
@@ -235,12 +233,7 @@
|
||||
|
||||
private async Task SaveAllAsync()
|
||||
{
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
db.FieldTransformationRules.RemoveRange(db.FieldTransformationRules);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
db.FieldTransformationRules.AddRange(_rules);
|
||||
await db.SaveChangesAsync();
|
||||
_rules = await TransformationsPageActions.SaveAllAsync(_rules);
|
||||
|
||||
Snackbar.Add(T("Transformationsregeln gespeichert.", "Transformation rules saved."), Severity.Success);
|
||||
await LoadAsync();
|
||||
|
||||
Reference in New Issue
Block a user