@page "/management-cockpit" @using TrafagSalesExporter.Models @using TrafagSalesExporter.Services @inject IManagementCockpitPageService CockpitPageService @inject ISnackbar Snackbar @inject IUiTextService UiText @T("Management Analyse", "Management analysis") @T("Management Analyse", "Management analysis") @foreach (var file in _files) { @file.DisplayName } @foreach (var option in _valueFieldOptions) { @option.Label } @foreach (var option in _currencyOptions) { @option.Label } @T("Dateien laden", "Load files") @(_analyzing ? T("Analysiere...", "Analyzing...") : T("Cockpit erzeugen", "Build cockpit")) @T("Zentrale Roh-Auswertung", "Central raw analysis") @T("Diese Sicht arbeitet direkt auf `CentralSalesRecords`. Summenfeld und Anzeige-Waehrung koennen gewaehlt werden; fachliche Filter wie Intercompany, Budget und Spartenlogik sind weiterhin nicht enthalten.", "This view works directly on `CentralSalesRecords`. Value field and display currency can be selected; business filters such as intercompany, budget and divisional logic are still not included.") @foreach (var year in _centralYears) { @year } @foreach (var month in Enumerable.Range(1, 12)) { @($"{month:D2}") } @foreach (var option in _valueFieldOptions) { @option.Label } @foreach (var option in _valueFieldOptions) { @option.Label } @foreach (var option in _currencyOptions) { @option.Label } @(_analyzingCentral ? T("Analysiere...", "Analyzing...") : T("Zentrale Auswertung laden", "Load central analysis")) @T("Global", "Global") @if (!string.IsNullOrWhiteSpace(_centralLandFilter) || !string.IsNullOrWhiteSpace(_centralTscFilter)) { @T("Gefiltert", "Filtered"): @($"{(_centralLandFilter ?? "-")} / {(_centralTscFilter ?? "-")}") } @if (_result is not null) { @T("Land", "Country")@_result.Summary.Land TSC@_result.Summary.Tsc @_result.Summary.ValueFieldLabel@FormatValue(_result.Summary.AggregatedValueTotal, _result.Summary.DisplayCurrency) @T("Nicht umgerechnet", "Not converted")@_result.Summary.MissingExchangeRateCount.ToString("N0") @T("Management Aussagen", "Management statements") @foreach (var finding in _result.Findings) { @finding.Title: @finding.Detail } @T("Top Kunden", "Top customers") @foreach (var item in _result.TopCustomers) { @($"{item.Label}: {FormatValue(item.Value, _result.Summary.DisplayCurrency)} ({item.SharePercent:F1}%)") } @T("Top Produktgruppen", "Top product groups") @foreach (var item in _result.TopProductGroups) { @($"{item.Label}: {FormatValue(item.Value, _result.Summary.DisplayCurrency)} ({item.SharePercent:F1}%)") } @T("Top Sales Owner", "Top sales owner") @foreach (var item in _result.TopSalesEmployees) { @($"{item.Label}: {FormatValue(item.Value, _result.Summary.DisplayCurrency)} ({item.SharePercent:F1}%)") } @T("Datenqualitaet", "Data quality") @foreach (var entry in _result.DataQualityCounts.OrderByDescending(x => x.Value)) { @($"{entry.Key}: {entry.Value}") } } @if (_centralResult is not null) { @T("Zeilen", "Rows")@_centralResult.Summary.RowCount.ToString("N0") @T("Rechnungen", "Invoices")@_centralResult.Summary.InvoiceCount.ToString("N0") @T("Standorte", "Sites")@_centralResult.Summary.SiteCount.ToString("N0") @T("Laender", "Countries")@_centralResult.Summary.CountryCount.ToString("N0") @_centralResult.Summary.ValueFieldLabel@FormatValue(_centralResult.Summary.ValueTotal, _centralResult.Summary.DisplayCurrency) @T("Nicht umgerechnet", "Not converted")@_centralResult.Summary.MissingExchangeRateCount.ToString("N0") @T("Hinweise", "Notes") @foreach (var notice in _centralResult.Notices) { @notice } @T("Jahreswerte", "Yearly values") @T("Jahr", "Year") @T("Waehrung", "Currency") @_centralResult.Summary.ValueFieldLabel @foreach (var field in _centralResult.AdditionalValueFields) { @field.Label } @T("Zeilen", "Rows") @context.Year @context.Currency @FormatValue(context.SalesValue, context.Currency) @foreach (var field in _centralResult.AdditionalValueFields) { @FormatAdditionalValue(context, field.Key) } @context.RowCount.ToString("N0") @T("Monatswerte", "Monthly values") @T("Monat", "Month") @T("Waehrung", "Currency") @_centralResult.Summary.ValueFieldLabel @foreach (var field in _centralResult.AdditionalValueFields) { @field.Label } @T("Zeilen", "Rows") @context.Label @context.Currency @FormatValue(context.SalesValue, context.Currency) @foreach (var field in _centralResult.AdditionalValueFields) { @FormatAdditionalValue(context, field.Key) } @context.RowCount.ToString("N0") @T("Tageswerte im ausgewaehlten Monat", "Daily values in selected month") @T("Tag", "Day") @T("Waehrung", "Currency") @_centralResult.Summary.ValueFieldLabel @foreach (var field in _centralResult.AdditionalValueFields) { @field.Label } @T("Zeilen", "Rows") @context.Label @context.Currency @FormatValue(context.SalesValue, context.Currency) @foreach (var field in _centralResult.AdditionalValueFields) { @FormatAdditionalValue(context, field.Key) } @context.RowCount.ToString("N0") @T("Fuer die Tagessicht bitte zusaetzlich einen Monat waehlen.", "Please select a month as well for the daily view.") @T("Werte nach Quelle", "Values by source") @T("Quelle", "Source") @T("Waehrung", "Currency") @_centralResult.Summary.ValueFieldLabel @T("Rechnungen", "Invoices") @context.Label @context.Currency @FormatValue(context.SalesValue, context.Currency) @context.InvoiceCount.ToString("N0") @T("Werte nach Land", "Values by country") @T("Land", "Country") @T("Waehrung", "Currency") @_centralResult.Summary.ValueFieldLabel @T("Rechnungen", "Invoices") @T("Zeilen", "Rows") @context.Label @context.Currency @FormatValue(context.SalesValue, context.Currency) @context.InvoiceCount.ToString("N0") @context.RowCount.ToString("N0") } @code { private List _files = []; private List _centralYears = []; private List _valueFieldOptions = []; private readonly List _currencyOptions = [ new(ManagementCockpitCurrencyOptions.Eur, "EUR"), new(ManagementCockpitCurrencyOptions.Usd, "USD"), new(ManagementCockpitCurrencyOptions.Native, "Original") ]; private string? _selectedFilePath; private ManagementCockpitResult? _result; private ManagementCockpitCentralResult? _centralResult; private int _selectedCentralYear; private int? _selectedCentralMonth; private string? _centralLandFilter; private string? _centralTscFilter; private string _selectedFileValueField = ManagementCockpitValueFieldKeys.SalesPriceValue; private string _selectedCentralValueField = ManagementCockpitValueFieldKeys.SalesPriceValue; private IEnumerable _selectedCentralAdditionalValueFields = []; private string _selectedFileTargetCurrency = ManagementCockpitCurrencyOptions.Eur; private string _selectedCentralTargetCurrency = ManagementCockpitCurrencyOptions.Eur; private bool _loadingFiles; private bool _analyzing; private bool _analyzingCentral; protected override async Task OnInitializedAsync() { var state = await CockpitPageService.InitializeAsync(_selectedFilePath, _selectedCentralYear); _files = state.Files; _valueFieldOptions = state.ValueFieldOptions; _centralYears = state.CentralYears; _selectedFilePath = state.SelectedFilePath; _selectedCentralYear = state.SelectedCentralYear; } private async Task ReloadFiles() { _loadingFiles = true; try { _files = await CockpitPageService.LoadFilesAsync(); _selectedFilePath ??= _files.FirstOrDefault()?.Path; } finally { _loadingFiles = false; } } private async Task ReloadCentralYears() { _centralYears = await CockpitPageService.LoadCentralYearsAsync(); if (_selectedCentralYear == 0) _selectedCentralYear = _centralYears.LastOrDefault(); } private async Task Analyze() { if (string.IsNullOrWhiteSpace(_selectedFilePath)) return; _analyzing = true; try { _result = await CockpitPageService.AnalyzeAsync(_selectedFilePath, new ManagementCockpitAnalysisOptions { ValueField = _selectedFileValueField, TargetCurrency = _selectedFileTargetCurrency }); _centralLandFilter = _result.Summary.Land; _centralTscFilter = _result.Summary.Tsc; } catch (Exception ex) { Snackbar.Add(string.Format(T("Cockpit konnte nicht erzeugt werden: {0}", "Could not build cockpit: {0}"), ex.Message), Severity.Error); } finally { _analyzing = false; } } private async Task AnalyzeCentral() { if (_selectedCentralYear == 0) return; _analyzingCentral = true; try { _centralResult = await CockpitPageService.AnalyzeCentralAsync(_selectedCentralYear, _selectedCentralMonth, new ManagementCockpitAnalysisOptions { ValueField = _selectedCentralValueField, AdditionalValueFields = _selectedCentralAdditionalValueFields.ToList(), TargetCurrency = _selectedCentralTargetCurrency, LandFilter = _centralLandFilter, TscFilter = _centralTscFilter }); } catch (Exception ex) { Snackbar.Add(string.Format(T("Zentrale Auswertung konnte nicht erzeugt werden: {0}", "Could not build central analysis: {0}"), ex.Message), Severity.Error); } finally { _analyzingCentral = false; } } private void ClearCentralScope() { _centralLandFilter = null; _centralTscFilter = null; } private static Severity MapSeverity(string severity) => severity switch { "Warning" => Severity.Warning, "Error" => Severity.Error, _ => Severity.Info }; private static string BuildPeriodLabel(ManagementCockpitCentralResult result) { if (result.Summary.PeriodStart is null || result.Summary.PeriodEnd is null) return "-"; return $"{result.Summary.PeriodStart.Value:dd.MM.yyyy} - {result.Summary.PeriodEnd.Value:dd.MM.yyyy}"; } private static string FormatValue(decimal value, string currency) => string.IsNullOrWhiteSpace(currency) || currency == "-" ? value.ToString("N2") : $"{value:N2} {currency}"; private void SetSelectedCentralAdditionalValueFields(IEnumerable values) { _selectedCentralAdditionalValueFields = values .Where(value => !string.IsNullOrWhiteSpace(value)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } private static string FormatAdditionalValue(ManagementCockpitTimeValueRow row, string fieldKey) { if (!row.AdditionalValues.TryGetValue(fieldKey, out var value)) return "-"; var formattedValue = FormatValue(value.Value, value.Currency); return value.MissingExchangeRateCount == 0 ? formattedValue : $"{formattedValue} / {value.MissingExchangeRateCount} ohne Kurs"; } private sealed record CurrencySelectOption(string Key, string Label); } @code { private string T(string german, string english) => UiText.Text(german, english); }