344 lines
14 KiB
Plaintext
344 lines
14 KiB
Plaintext
@page "/management-cockpit"
|
|
@using TrafagSalesExporter.Models
|
|
@using TrafagSalesExporter.Services
|
|
@inject IManagementCockpitService CockpitService
|
|
@inject ISnackbar Snackbar
|
|
|
|
<PageTitle>Management Cockpit</PageTitle>
|
|
|
|
<MudText Typo="Typo.h4" Class="mb-4">Management Cockpit</MudText>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudGrid>
|
|
<MudItem xs="12" md="8">
|
|
<MudSelect T="string" @bind-Value="_selectedFilePath" Label="Vorhandene Excel-Datei" Dense>
|
|
@foreach (var file in _files)
|
|
{
|
|
<MudSelectItem Value="@file.Path">@file.DisplayName</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudStack Row Spacing="2">
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Info" OnClick="ReloadFiles"
|
|
StartIcon="@Icons.Material.Filled.Refresh" Disabled="_loadingFiles">
|
|
Dateien laden
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="Analyze"
|
|
StartIcon="@Icons.Material.Filled.Analytics" Disabled="_analyzing || string.IsNullOrWhiteSpace(_selectedFilePath)">
|
|
@(_analyzing ? "Analysiere..." : "Cockpit erzeugen")
|
|
</MudButton>
|
|
</MudStack>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-3">Zentrale Roh-Auswertung</MudText>
|
|
<MudAlert Severity="Severity.Info" Dense Variant="Variant.Outlined" Class="mb-3">
|
|
Diese Sicht arbeitet direkt auf `CentralSalesRecords` und zeigt nur fachlich neutrale Rohkennzahlen. Kein Intercompany-Filter, keine CHF-Umrechnung, kein Budget, keine Spartenlogik.
|
|
</MudAlert>
|
|
<MudGrid>
|
|
<MudItem xs="12" md="4">
|
|
<MudSelect T="int" @bind-Value="_selectedCentralYear" Label="Jahr" Dense>
|
|
@foreach (var year in _centralYears)
|
|
{
|
|
<MudSelectItem Value="@year">@year</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudSelect T="int?" @bind-Value="_selectedCentralMonth" Label="Monat (optional)" Dense Clearable>
|
|
@foreach (var month in Enumerable.Range(1, 12))
|
|
{
|
|
<MudSelectItem Value="@((int?)month)">@($"{month:D2}")</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Secondary" OnClick="AnalyzeCentral"
|
|
StartIcon="@Icons.Material.Filled.QueryStats" Disabled="_analyzingCentral || _selectedCentralYear == 0">
|
|
@(_analyzingCentral ? "Analysiere..." : "Zentrale Auswertung laden")
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
|
|
@if (_result is not null)
|
|
{
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" md="3"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Land</MudText><MudText Typo="Typo.h6">@_result.Summary.Land</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="3"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">TSC</MudText><MudText Typo="Typo.h6">@_result.Summary.Tsc</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="3"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Umsatz</MudText><MudText Typo="Typo.h6">@_result.Summary.SalesValueTotal.ToString("N2")</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="3"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Geschätzte Marge</MudText><MudText Typo="Typo.h6">@($"{_result.Summary.EstimatedMarginPercent:F1}%")</MudText></MudPaper></MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Management Aussagen</MudText>
|
|
@foreach (var finding in _result.Findings)
|
|
{
|
|
<MudAlert Severity="@MapSeverity(finding.Severity)" Dense Variant="Variant.Outlined" Class="mb-2">
|
|
<b>@finding.Title:</b> @finding.Detail
|
|
</MudAlert>
|
|
}
|
|
</MudPaper>
|
|
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" md="4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Top Kunden</MudText>
|
|
@foreach (var item in _result.TopCustomers)
|
|
{
|
|
<MudText Typo="Typo.body2">@($"{item.Label}: {item.Value:N2} ({item.SharePercent:F1}%)")</MudText>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Top Produktgruppen</MudText>
|
|
@foreach (var item in _result.TopProductGroups)
|
|
{
|
|
<MudText Typo="Typo.body2">@($"{item.Label}: {item.Value:N2} ({item.SharePercent:F1}%)")</MudText>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Top Sales Owner</MudText>
|
|
@foreach (var item in _result.TopSalesEmployees)
|
|
{
|
|
<MudText Typo="Typo.body2">@($"{item.Label}: {item.Value:N2} ({item.SharePercent:F1}%)")</MudText>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Datenqualität</MudText>
|
|
@foreach (var entry in _result.DataQualityCounts.OrderByDescending(x => x.Value))
|
|
{
|
|
<MudText Typo="Typo.body2">@($"{entry.Key}: {entry.Value}")</MudText>
|
|
}
|
|
</MudPaper>
|
|
}
|
|
|
|
@if (_centralResult is not null)
|
|
{
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Zeilen</MudText><MudText Typo="Typo.h6">@_centralResult.Summary.RowCount.ToString("N0")</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Rechnungen</MudText><MudText Typo="Typo.h6">@_centralResult.Summary.InvoiceCount.ToString("N0")</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Standorte</MudText><MudText Typo="Typo.h6">@_centralResult.Summary.SiteCount.ToString("N0")</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Länder</MudText><MudText Typo="Typo.h6">@_centralResult.Summary.CountryCount.ToString("N0")</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Währungen</MudText><MudText Typo="Typo.h6">@_centralResult.Summary.CurrencyCount.ToString("N0")</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">Periode</MudText><MudText Typo="Typo.h6">@BuildPeriodLabel(_centralResult)</MudText></MudPaper></MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Hinweise</MudText>
|
|
@foreach (var notice in _centralResult.Notices)
|
|
{
|
|
<MudAlert Severity="Severity.Info" Dense Variant="Variant.Outlined" Class="mb-2">@notice</MudAlert>
|
|
}
|
|
</MudPaper>
|
|
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" md="6">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Jahresumsatz 2025/2026</MudText>
|
|
<MudTable Items="_centralResult.YearlyTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>Jahr</MudTh>
|
|
<MudTh>Währung</MudTh>
|
|
<MudTh>Umsatz</MudTh>
|
|
<MudTh>Zeilen</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Year</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@context.SalesValue.ToString("N2")</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="6">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Monatsumsatz</MudText>
|
|
<MudTable Items="_centralResult.MonthlyTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>Monat</MudTh>
|
|
<MudTh>Währung</MudTh>
|
|
<MudTh>Umsatz</MudTh>
|
|
<MudTh>Zeilen</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@context.SalesValue.ToString("N2")</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" md="6">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Tagesumsatz im ausgewählten Monat</MudText>
|
|
<MudTable Items="_centralResult.DailyTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>Tag</MudTh>
|
|
<MudTh>Währung</MudTh>
|
|
<MudTh>Umsatz</MudTh>
|
|
<MudTh>Zeilen</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@context.SalesValue.ToString("N2")</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.caption">Für die Tagessicht bitte zusätzlich einen Monat wählen.</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="6">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Umsatz nach Quelle</MudText>
|
|
<MudTable Items="_centralResult.SourceSystemTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>Quelle</MudTh>
|
|
<MudTh>Währung</MudTh>
|
|
<MudTh>Umsatz</MudTh>
|
|
<MudTh>Rechnungen</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@context.SalesValue.ToString("N2")</MudTd>
|
|
<MudTd>@context.InvoiceCount.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Umsatz nach Land</MudText>
|
|
<MudTable Items="_centralResult.CountryTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>Land</MudTh>
|
|
<MudTh>Währung</MudTh>
|
|
<MudTh>Umsatz</MudTh>
|
|
<MudTh>Rechnungen</MudTh>
|
|
<MudTh>Zeilen</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@context.SalesValue.ToString("N2")</MudTd>
|
|
<MudTd>@context.InvoiceCount.ToString("N0")</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
}
|
|
|
|
@code {
|
|
private List<ManagementCockpitFileOption> _files = [];
|
|
private List<int> _centralYears = [];
|
|
private string? _selectedFilePath;
|
|
private ManagementCockpitResult? _result;
|
|
private ManagementCockpitCentralResult? _centralResult;
|
|
private int _selectedCentralYear;
|
|
private int? _selectedCentralMonth;
|
|
private bool _loadingFiles;
|
|
private bool _analyzing;
|
|
private bool _analyzingCentral;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await ReloadFiles();
|
|
await ReloadCentralYears();
|
|
}
|
|
|
|
private async Task ReloadFiles()
|
|
{
|
|
_loadingFiles = true;
|
|
try
|
|
{
|
|
_files = await CockpitService.GetAvailableFilesAsync();
|
|
_selectedFilePath ??= _files.FirstOrDefault()?.Path;
|
|
}
|
|
finally
|
|
{
|
|
_loadingFiles = false;
|
|
}
|
|
}
|
|
|
|
private async Task ReloadCentralYears()
|
|
{
|
|
_centralYears = await CockpitService.GetAvailableCentralYearsAsync();
|
|
if (_selectedCentralYear == 0)
|
|
_selectedCentralYear = _centralYears.LastOrDefault();
|
|
}
|
|
|
|
private async Task Analyze()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_selectedFilePath))
|
|
return;
|
|
|
|
_analyzing = true;
|
|
try
|
|
{
|
|
_result = await CockpitService.AnalyzeAsync(_selectedFilePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"Cockpit konnte nicht erzeugt werden: {ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
_analyzing = false;
|
|
}
|
|
}
|
|
|
|
private async Task AnalyzeCentral()
|
|
{
|
|
if (_selectedCentralYear == 0)
|
|
return;
|
|
|
|
_analyzingCentral = true;
|
|
try
|
|
{
|
|
_centralResult = await CockpitService.AnalyzeCentralAsync(_selectedCentralYear, _selectedCentralMonth);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"Zentrale Auswertung konnte nicht erzeugt werden: {ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
_analyzingCentral = false;
|
|
}
|
|
}
|
|
|
|
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}";
|
|
}
|
|
}
|