@page "/settings" @rendermode @(Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer) @attribute [Authorize(Policy = TrafagSalesExporter.Security.SecurityPolicies.AdminOnly)] @using TrafagSalesExporter.Models @using TrafagSalesExporter.Services @inject ISettingsPageService SettingsPageActions @inject IJSRuntime JS @inject ISnackbar Snackbar Settings Settings Konfiguration Import/Export Wenn deaktiviert, bleiben Passwörter und Secrets beim Export leer. Beim Import ohne Secrets werden bestehende Secrets auf dem Zielsystem beibehalten. @(_exportingConfig ? "Exportiere..." : "Konfiguration exportieren") @(_importingConfig ? "Importiere..." : "Konfiguration importieren") @* SharePoint Config *@ SharePoint Konfiguration Speichern @if (_testingSp) { @("Teste...") } else { @("SharePoint Verbindung testen") } @if (!string.IsNullOrWhiteSpace(_sharePointTestPreview)) {
Test Preview
@_sharePointTestPreview
}
Quellsysteme Diese Zugangsdaten werden pro Quellsystem als Standard verwendet. Ein Standort kann sie bei Bedarf mit eigenen Overrides überschreiben. Quellsystem hinzufuegen Code Name Anschlussart Zentrale URL User Aktiv Test @context.Code @context.DisplayName @GetConnectionKindLabel(context.ConnectionKind) @GetServiceUrlSummary(context) @GetUsernameSummary(context) @if (context.IsActive) { } else { } @if (!UsesManualImport(context)) { @(_testingSystems.Contains(context.Code) ? "Teste..." : "Testen") } Quellsysteme speichern @(_editingSourceSystem.Id == 0 ? "Quellsystem hinzufuegen" : "Quellsystem bearbeiten") @foreach (var kind in SourceSystemConnectionKinds.All) { @GetConnectionKindLabel(kind) } @if (UsesSapGateway(_editingSourceSystem)) { } Abbrechen Uebernehmen Wechselkurse Diese Kurstabelle wird von der Transformation ConvertCurrency verwendet. Gleiche Waehrung rechnet automatisch mit Faktor 1. Kurs hinzufuegen @(_refreshingExchangeRates ? "Aktualisiere ECB-Kurse..." : "Refresh Kurse") Kurse speichern Von Nach Kurs Gueltig ab Gueltig bis Notiz Aktiv @* Export Settings *@ Export Einstellungen PostingDate / Buchungsdatum InvoiceDate / Rechnungsdatum ExtractionDate / Extraktionsdatum Schreibt zusätzliche technische Fortschrittsmeldungen für HANA- und SAP-Lesevorgänge ins Dashboard und in die Logs.
Audit-CSV / nachvollziehbarer Datenfluss Fuer Finance und Wirtschaftspruefung: lesbare Standort-CSV nach Mapping und Konvertierung, optional als Quelle fuer zentrale Auswertungen.
Schreibt beim Laenderexport je Standort eine Sales_*.csv mit den transformierten Daten. Dashboard, zentrale Excel-Datei und Finance-Auswertungen lesen die neuesten Standort-CSV-Dateien statt CentralSalesRecords. Audit-CSV wird immer im gleichen Ordner wie die lokalen Standort-Dateien abgelegt. Der Pfad wird oben bei Lokaler Standardpfad Standort-Dateien gesetzt.
Speichern
@* Filename Preview *@ Dateiname Vorschau Sales_{"{TSC}"}_{DateTime.Now:yyyy-MM-dd}.xlsx Beispiel: Sales_TRFR_@(DateTime.Now.ToString("yyyy-MM-dd")).xlsx @code { private SharePointConfig _spConfig = new(); private ExportSettings _exportSettings = new(); private List _sourceSystems = []; private SourceSystemDefinition _editingSourceSystem = new(); private bool _testingSp; private bool _includeSecretsInExport; private bool _exportingConfig; private bool _importingConfig; private bool _refreshingExchangeRates; private string _sharePointTestPreview = string.Empty; private List _exchangeRates = []; private readonly HashSet _testingSystems = []; private bool _sourceSystemDialogVisible; private readonly DialogOptions _sourceSystemDialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true }; protected override async Task OnInitializedAsync() { var state = await SettingsPageActions.LoadAsync(); _spConfig = state.SharePointConfig; _exportSettings = state.ExportSettings; _sourceSystems = state.SourceSystems; _exchangeRates = state.ExchangeRates; } private async Task SaveSharePoint() { await SettingsPageActions.SaveSharePointAsync(_spConfig); Snackbar.Add("SharePoint Konfiguration gespeichert", Severity.Success); } private async Task TestSharePoint() { _testingSp = true; try { _sharePointTestPreview = await SettingsPageActions.BuildSharePointTestPreviewAsync(_spConfig); Snackbar.Add("SharePoint Verbindung erfolgreich!", Severity.Success); } catch (Exception ex) { Snackbar.Add($"Verbindung fehlgeschlagen: {ex.Message}", Severity.Error); } finally { _testingSp = false; } } private async Task SaveExportSettings() { await SettingsPageActions.SaveExportSettingsAsync(_exportSettings); Snackbar.Add("Export Einstellungen gespeichert", Severity.Success); } private void AddSourceSystem() { _editingSourceSystem = new SourceSystemDefinition { Code = string.Empty, DisplayName = string.Empty, ConnectionKind = SourceSystemConnectionKinds.Hana, IsActive = true }; _sourceSystemDialogVisible = true; } private void EditSourceSystem(SourceSystemDefinition definition) { _editingSourceSystem = new SourceSystemDefinition { Id = definition.Id, Code = definition.Code, DisplayName = definition.DisplayName, ConnectionKind = definition.ConnectionKind, IsActive = definition.IsActive, CentralServiceUrl = definition.CentralServiceUrl, CentralUsername = definition.CentralUsername, CentralPassword = definition.CentralPassword }; _sourceSystemDialogVisible = true; } private void SaveSourceSystemEdit() { _editingSourceSystem.Code = NormalizeSourceSystemCode(_editingSourceSystem.Code); _editingSourceSystem.DisplayName = NormalizeConfigValue(_editingSourceSystem.DisplayName); _editingSourceSystem.ConnectionKind = NormalizeConnectionKind(_editingSourceSystem.ConnectionKind); _editingSourceSystem.CentralServiceUrl = NormalizeConfigValue(_editingSourceSystem.CentralServiceUrl); _editingSourceSystem.CentralUsername = NormalizeConfigValue(_editingSourceSystem.CentralUsername); _editingSourceSystem.CentralPassword = _editingSourceSystem.CentralPassword ?? string.Empty; if (string.IsNullOrWhiteSpace(_editingSourceSystem.Code) || string.IsNullOrWhiteSpace(_editingSourceSystem.DisplayName)) { Snackbar.Add("Code und Name fuer das Quellsystem sind Pflicht.", Severity.Warning); return; } if (_sourceSystems.Any(x => x.Id != _editingSourceSystem.Id && x.Code == _editingSourceSystem.Code)) { Snackbar.Add($"Quellsystem-Code doppelt vorhanden: {_editingSourceSystem.Code}", Severity.Warning); return; } if (_editingSourceSystem.Id == 0) { _sourceSystems.Add(_editingSourceSystem); } else { var existing = _sourceSystems.FirstOrDefault(x => x.Id == _editingSourceSystem.Id); if (existing is not null) { existing.Code = _editingSourceSystem.Code; existing.DisplayName = _editingSourceSystem.DisplayName; existing.ConnectionKind = _editingSourceSystem.ConnectionKind; existing.IsActive = _editingSourceSystem.IsActive; existing.CentralServiceUrl = _editingSourceSystem.CentralServiceUrl; existing.CentralUsername = _editingSourceSystem.CentralUsername; existing.CentralPassword = _editingSourceSystem.CentralPassword; } } _sourceSystems = _sourceSystems.OrderBy(x => x.Code).ToList(); _sourceSystemDialogVisible = false; } private void CloseSourceSystemDialog() { _sourceSystemDialogVisible = false; } private void RemoveSourceSystem(SourceSystemDefinition definition) { _sourceSystems.Remove(definition); } private async Task SaveSourceSystems() { try { _sourceSystems = await SettingsPageActions.SaveSourceSystemsAsync(_sourceSystems); Snackbar.Add("Quellsysteme gespeichert", Severity.Success); } catch (Exception ex) { Snackbar.Add(ex.Message, Severity.Warning); } } private void AddExchangeRate() { _exchangeRates.Add(new CurrencyExchangeRate { FromCurrency = "USD", ToCurrency = "EUR", Rate = 1m, ValidFrom = DateTime.Today, IsActive = true }); } private void RemoveExchangeRate(CurrencyExchangeRate rate) { _exchangeRates.Remove(rate); } private async Task SaveExchangeRates() { _exchangeRates = await SettingsPageActions.SaveExchangeRatesAsync(_exchangeRates); Snackbar.Add("Wechselkurse gespeichert", Severity.Success); } private async Task RefreshEcbRates() { if (_refreshingExchangeRates) return; _refreshingExchangeRates = true; try { 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) { Snackbar.Add($"ECB-Kursimport fehlgeschlagen: {ex.Message}", Severity.Error); } finally { _refreshingExchangeRates = false; } } private async Task ExportConfiguration() { if (_exportingConfig) return; _exportingConfig = true; try { 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"); Snackbar.Add("Konfiguration exportiert", Severity.Success); } catch (Exception ex) { Snackbar.Add($"Export fehlgeschlagen: {ex.Message}", Severity.Error); } finally { _exportingConfig = false; } } private async Task ImportConfiguration(InputFileChangeEventArgs args) { if (_importingConfig) return; _importingConfig = true; try { var file = args.File; await using var stream = file.OpenReadStream(5 * 1024 * 1024); using var reader = new StreamReader(stream); var json = await reader.ReadToEndAsync(); 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) { Snackbar.Add($"Import fehlgeschlagen: {ex.Message}", Severity.Error); } finally { _importingConfig = false; } } private async Task TestCentralCredentials(string sourceSystem) { var definition = _sourceSystems.FirstOrDefault(x => string.Equals(x.Code, sourceSystem, StringComparison.OrdinalIgnoreCase)); if (definition is null) { Snackbar.Add($"Quellsystem '{sourceSystem}' nicht gefunden.", Severity.Warning); return; } if (!_testingSystems.Add(sourceSystem)) return; try { var result = await SettingsPageActions.TestCentralCredentialsAsync(definition); Snackbar.Add(result.Message, result.Success ? Severity.Success : result.Warning ? Severity.Warning : Severity.Error); } finally { _testingSystems.Remove(sourceSystem); } } private static string NormalizeSourceSystemCode(string? code) => Services.SettingsPageService.NormalizeSourceSystemCode(code); private static string NormalizeConnectionKind(string? connectionKind) => Services.SettingsPageService.NormalizeConnectionKind(connectionKind); private static string GetConnectionKindLabel(string connectionKind) => connectionKind switch { SourceSystemConnectionKinds.Hana => "HANA", SourceSystemConnectionKinds.SapGateway => "SAP Gateway", SourceSystemConnectionKinds.ManualExcel => "Manual Excel", _ => connectionKind }; private static bool UsesManualImport(SourceSystemDefinition definition) => string.Equals(definition.ConnectionKind, SourceSystemConnectionKinds.ManualExcel, StringComparison.OrdinalIgnoreCase); private static bool UsesSapGateway(SourceSystemDefinition definition) => string.Equals(definition.ConnectionKind, SourceSystemConnectionKinds.SapGateway, StringComparison.OrdinalIgnoreCase); private static string GetServiceUrlSummary(SourceSystemDefinition definition) => string.IsNullOrWhiteSpace(definition.CentralServiceUrl) ? "-" : definition.CentralServiceUrl; private static string GetUsernameSummary(SourceSystemDefinition definition) => string.IsNullOrWhiteSpace(definition.CentralUsername) ? "-" : definition.CentralUsername; private static string NormalizeConfigValue(string? value) => Services.SettingsPageService.NormalizeConfigValue(value); }