@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);
}