1767 lines
92 KiB
Plaintext
1767 lines
92 KiB
Plaintext
@page "/management-cockpit"
|
|
@rendermode @(Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer)
|
|
@using System.Globalization
|
|
@using Microsoft.AspNetCore.Components
|
|
@using Microsoft.JSInterop
|
|
@using TrafagSalesExporter.Models
|
|
@using TrafagSalesExporter.Services
|
|
@inject IManagementCockpitPageService CockpitPageService
|
|
@inject ISnackbar Snackbar
|
|
@inject IUiTextService UiText
|
|
@inject IJSRuntime JsRuntime
|
|
|
|
<PageTitle>@T("Management Analyse", "Management analysis")</PageTitle>
|
|
|
|
<MudText Typo="Typo.h4" Class="mb-4">@T("Management Analyse", "Management analysis")</MudText>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudGrid>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="int" @bind-Value="_selectedFinanceYear" Label="@T("Finance-Jahr", "Finance year")" Dense>
|
|
@foreach (var year in _financeYearOptions)
|
|
{
|
|
<MudSelectItem Value="@year">@year</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string" @bind-Value="_selectedFinanceCountryKey" Label="@T("Land", "Country")" Dense Clearable>
|
|
@foreach (var option in _financeCountryOptions)
|
|
{
|
|
<MudSelectItem Value="@option">@option</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string" @bind-Value="_selectedFinanceCurrency" Label="@T("Waehrung", "Currency")" Dense Clearable>
|
|
@foreach (var option in _financeCurrencyOptions)
|
|
{
|
|
<MudSelectItem Value="@option">@option</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="AnalyzeFinanceSummary"
|
|
StartIcon="@Icons.Material.Filled.FactCheck" Disabled="_analyzingFinance" FullWidth>
|
|
@(_analyzingFinance ? T("Lade...", "Loading...") : T("Finance Summary laden", "Load finance summary"))
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
|
|
@if (_financeResult is not null)
|
|
{
|
|
<MudTabs Elevation="1" Rounded="false" PanelClass="pt-4" @bind-ActivePanelIndex="_activeOverviewTabIndex">
|
|
<MudTabPanel Text="@T("Schnelluebersicht", "Quick overview")" Icon="@Icons.Material.Filled.Speed">
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Net Sales Actual", "Net sales actual")</MudText>
|
|
<MudText Typo="Typo.h5">@FormatValue(_financeResult.NetSalesActual, _financeResult.DisplayCurrency)</MudText>
|
|
<MudText Typo="Typo.body2">@($"{_financeResult.Filter.Year}")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Laender OK", "Countries OK")</MudText>
|
|
<MudText Typo="Typo.h5">@_financeResult.CountryRows.Count(row => row.Status == "OK").ToString("N0")</MudText>
|
|
<MudText Typo="Typo.body2">@T("Soll/Ist ohne Abweichung", "Actual/reference without deviation")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Zu pruefen", "To check")</MudText>
|
|
<MudText Typo="Typo.h5">@_financeResult.CountryRows.Count(row => row.Status == "Pruefen").ToString("N0")</MudText>
|
|
<MudText Typo="Typo.body2">@T("Abweichung oder offene Regel", "Deviation or open rule")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Datenstandorte", "Data sites")</MudText>
|
|
<MudText Typo="Typo.h5">@_financeResult.DataStatusRows.Count(row => row.IsActive).ToString("N0")</MudText>
|
|
<MudText Typo="Typo.body2">@T("aktive Quellen", "active sources")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudTabs Elevation="0" Rounded="false" PanelClass="pt-4">
|
|
<MudTabPanel Text="@T("Freigabe", "Approval")" Icon="@Icons.Material.Filled.FactCheck">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Finance-Freigabe je Land", "Finance approval by country")</MudText>
|
|
<MudTable Items="_financeResult.CountryRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Status", "Status")</MudTh>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>@T("Ist", "Actual")</MudTh>
|
|
<MudTh>@T("Soll", "Reference")</MudTh>
|
|
<MudTh>@T("Differenz", "Difference")</MudTh>
|
|
<MudTh>@T("Datenstand", "Data status")</MudTh>
|
|
<MudTh>@T("Hinweis", "Note")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd><MudChip T="string" Size="Size.Small" Color="@StatusColor(context.Status)" Variant="Variant.Filled">@context.Status</MudChip></MudTd>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
<MudTd>@FormatNullableValue(context.ReferenceValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatNullableValue(context.Difference, context.Currency)</MudTd>
|
|
<MudTd>@BuildDataStatusText(context)</MudTd>
|
|
<MudTd>@BuildQuickFinanceNote(context)</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Datenstand", "Data status")" Icon="@Icons.Material.Filled.Storage">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Letzter Datenstand je Standort", "Latest data status by site")</MudText>
|
|
<MudTable Items="_financeResult.DataStatusRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Aktiv", "Active")</MudTh>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Quelle", "Source")</MudTh>
|
|
<MudTh>@T("Zentrale Zeilen", "Central rows")</MudTh>
|
|
<MudTh>@T("Letzter Export", "Latest export")</MudTh>
|
|
<MudTh>@T("Status", "Status")</MudTh>
|
|
<MudTh>@T("Manual Import", "Manual import")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>
|
|
<MudIcon Icon="@(context.IsActive ? Icons.Material.Filled.CheckCircle : Icons.Material.Filled.Cancel)"
|
|
Color="@(context.IsActive ? Color.Success : Color.Default)" Size="Size.Small" />
|
|
</MudTd>
|
|
<MudTd>@context.Land</MudTd>
|
|
<MudTd>@context.Tsc</MudTd>
|
|
<MudTd>@context.SourceSystem</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
<MudTd>@FormatDateTime(context.LatestExportAt)</MudTd>
|
|
<MudTd>@(string.IsNullOrWhiteSpace(context.LatestExportStatus) ? "-" : context.LatestExportStatus)</MudTd>
|
|
<MudTd>@FormatManualImportStatus(context)</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Sparten", "Divisions")" Icon="@Icons.Material.Filled.AccountTree">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Sparten-Abdeckung nach Land", "Division coverage by country")</MudText>
|
|
<MudTable Items="_financeResult.ProductFinanceCountryRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Gesamtumsatz", "Total sales")</MudTh>
|
|
<MudTh>@T("Zugeordnet", "Assigned")</MudTh>
|
|
<MudTh>@T("Nicht im Stamm", "Not in master")</MudTh>
|
|
<MudTh>@T("Abdeckung", "Coverage")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Tsc</MudTd>
|
|
<MudTd>@FormatValue(context.TotalValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.AssignedValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.MissingReferenceValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatPercent(context.AssignedValuePercent)</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
</MudTabPanel>
|
|
|
|
<MudTabPanel Text="@T("Experten", "Experts")" Icon="@Icons.Material.Filled.Tune">
|
|
<MudTabs Elevation="0" Rounded="false" PanelClass="pt-4" @bind-ActivePanelIndex="_activeFinanceTabIndex">
|
|
<MudTabPanel Text="@T("Finance Summary", "Finance summary")" Icon="@Icons.Material.Filled.Dashboard">
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Net Sales Actual", "Net sales actual")</MudText>
|
|
<MudText Typo="Typo.h5">@FormatValue(_financeResult.NetSalesActual, _financeResult.DisplayCurrency)</MudText>
|
|
<MudText Typo="Typo.body2">@T("gefiltertes Endergebnis", "filtered final result")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Enthaltene Zeilen", "Included rows")</MudText>
|
|
<MudText Typo="Typo.h5">@_financeResult.IncludedRows.ToString("N0")</MudText>
|
|
<MudText Typo="Typo.body2">@T("Finance Include = TRUE", "Finance Include = TRUE")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Ausgeschlossen", "Excluded")</MudText>
|
|
<MudText Typo="Typo.h5">@_financeResult.ExcludedRows.ToString("N0")</MudText>
|
|
<MudText Typo="Typo.body2">@T("Finance-Regeln", "Finance rules")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Laender / Waehrungen", "Countries / currencies")</MudText>
|
|
<MudText Typo="Typo.h5">@($"{_financeResult.CountryCount:N0} / {_financeResult.CurrencyCount:N0}")</MudText>
|
|
<MudText Typo="Typo.body2">@($"{_financeResult.Filter.Year}")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" md="8">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Summen wie im Excel-Blatt Finance Summary", "Totals matching the Finance Summary Excel sheet")</MudText>
|
|
<MudTable Items="_financeResult.Rows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Jahr", "Year")</MudTh>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@T("Net Sales Actual", "Net sales actual")</MudTh>
|
|
<MudTh>@T("Enthalten", "Included")</MudTh>
|
|
<MudTh>@T("Ausgeschlossen", "Excluded")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Year</MudTd>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
<MudTd>@context.IncludedRows.ToString("N0")</MudTd>
|
|
<MudTd>@context.ExcludedRows.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">
|
|
@T("Keine Finance-Summary-Daten fuer diese Filter.", "No finance summary data for these filters.")
|
|
</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Hinweise", "Notes")</MudText>
|
|
@foreach (var notice in _financeResult.Notices)
|
|
{
|
|
<MudAlert Severity="Severity.Info" Dense Variant="Variant.Outlined" Class="mb-2">@notice</MudAlert>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Jahresvergleich mit aktuellem Filter", "Year comparison with current filter")</MudText>
|
|
<MudTable Items="_financeResult.YearRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Jahr", "Year")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@T("Net Sales Actual", "Net sales actual")</MudTh>
|
|
<MudTh>@T("Enthalten", "Included")</MudTh>
|
|
<MudTh>@T("Ausgeschlossen", "Excluded")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Year</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
<MudTd>@context.IncludedRows.ToString("N0")</MudTd>
|
|
<MudTd>@context.ExcludedRows.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Laender", "Countries")" Icon="@Icons.Material.Filled.Public">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Finance-Status nach Land", "Finance status by country")</MudText>
|
|
<MudTable Items="_financeResult.CountryRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Status", "Status")</MudTh>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Quelle", "Source")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@T("Ist", "Actual")</MudTh>
|
|
<MudTh>@T("IC/2nd-party", "IC/2nd-party")</MudTh>
|
|
<MudTh>@T("Ist ohne IC", "Actual excl. IC")</MudTh>
|
|
<MudTh>@T("Soll", "Reference")</MudTh>
|
|
<MudTh>@T("Differenz", "Difference")</MudTh>
|
|
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd><MudChip T="string" Size="Size.Small" Color="@StatusColor(context.Status)" Variant="Variant.Outlined">@context.Status</MudChip></MudTd>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Tscs</MudTd>
|
|
<MudTd>@context.SourceSystems</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.IntercompanyValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActualExcludingIntercompany, context.Currency)</MudTd>
|
|
<MudTd>@FormatNullableValue(context.ReferenceValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatNullableValue(context.Difference, context.Currency)</MudTd>
|
|
<MudTd>@context.IncludedRows.ToString("N0") / @context.ExcludedRows.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine Laenderdaten fuer diese Filter.", "No country data for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Datenstatus", "Data status")" Icon="@Icons.Material.Filled.FactCheck">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Datenbestand nach Standort", "Data inventory by site")</MudText>
|
|
<MudTable Items="_financeResult.DataStatusRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Aktiv", "Active")</MudTh>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Quelle", "Source")</MudTh>
|
|
<MudTh>@T("Zentrale Zeilen", "Central rows")</MudTh>
|
|
<MudTh>@T("Letzter Export", "Latest export")</MudTh>
|
|
<MudTh>@T("Exportstatus", "Export status")</MudTh>
|
|
<MudTh>@T("Letzte Speicherung", "Latest stored")</MudTh>
|
|
<MudTh>@T("Manual Import", "Manual import")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>
|
|
<MudIcon Icon="@(context.IsActive ? Icons.Material.Filled.CheckCircle : Icons.Material.Filled.Cancel)"
|
|
Color="@(context.IsActive ? Color.Success : Color.Default)" Size="Size.Small" />
|
|
</MudTd>
|
|
<MudTd>@context.Land</MudTd>
|
|
<MudTd>@context.Tsc</MudTd>
|
|
<MudTd>@context.SourceSystem</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
<MudTd>@FormatDateTime(context.LatestExportAt)</MudTd>
|
|
<MudTd>@(string.IsNullOrWhiteSpace(context.LatestExportStatus) ? "-" : context.LatestExportStatus)</MudTd>
|
|
<MudTd>@FormatDateTime(context.LatestStoredAtUtc)</MudTd>
|
|
<MudTd>@FormatManualImportStatus(context)</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Abweichungen", "Deviations")" Icon="@Icons.Material.Filled.WarningAmber">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Soll/Ist-Abweichungen", "Actual/reference deviations")</MudText>
|
|
<MudTable Items="_financeResult.DeviationRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Status", "Status")</MudTh>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@T("Ist", "Actual")</MudTh>
|
|
<MudTh>@T("Soll", "Reference")</MudTh>
|
|
<MudTh>@T("Differenz", "Difference")</MudTh>
|
|
<MudTh>%</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd><MudChip T="string" Size="Size.Small" Color="@StatusColor(context.Status)" Variant="Variant.Outlined">@context.Status</MudChip></MudTd>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
<MudTd>@FormatNullableValue(context.ReferenceValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatNullableValue(context.Difference, context.Currency)</MudTd>
|
|
<MudTd>@FormatPercent(context.DifferencePercent)</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine Sollwerte oder keine Abweichungen fuer diese Filter.", "No reference values or deviations for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Gutschriften", "Credit notes")" Icon="@Icons.Material.Filled.AssignmentReturn">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Gutschriften-Kandidaten", "Credit-note candidates")</MudText>
|
|
<MudAlert Severity="Severity.Info" Dense Variant="Variant.Outlined" Class="mb-3">
|
|
@T("Diese Sicht zeigt technische Kandidaten anhand negativer Werte und erkennbarer Belegtypen/-nummern. Sie ersetzt keine landesspezifische Fachfreigabe.",
|
|
"This view shows technical candidates based on negative values and recognizable document types/numbers. It does not replace country-specific business approval.")
|
|
</MudAlert>
|
|
<MudTable Items="_financeResult.CreditCandidates" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Rechnung", "Invoice")</MudTh>
|
|
<MudTh>@T("Typ", "Type")</MudTh>
|
|
<MudTh>@T("Wert", "Value")</MudTh>
|
|
<MudTh>@T("Menge", "Quantity")</MudTh>
|
|
<MudTh>@T("Grund", "Reason")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Tsc</MudTd>
|
|
<MudTd>@context.InvoiceNumber</MudTd>
|
|
<MudTd>@context.DocumentType</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
<MudTd>@context.Quantity.ToString("N2")</MudTd>
|
|
<MudTd>@context.Reason</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine Gutschriften-Kandidaten fuer diese Filter.", "No credit-note candidates for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Datenqualitaet", "Data quality")" Icon="@Icons.Material.Filled.Rule">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Pruefpunkte", "Checkpoints")</MudText>
|
|
<MudTable Items="_financeResult.DataQualityRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Status", "Status")</MudTh>
|
|
<MudTh>@T("Pruefpunkt", "Checkpoint")</MudTh>
|
|
<MudTh>@T("Anzahl", "Count")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd><MudChip T="string" Size="Size.Small" Color="@SeverityColor(context.Severity)" Variant="Variant.Outlined">@context.Severity</MudChip></MudTd>
|
|
<MudTd>@context.Issue</MudTd>
|
|
<MudTd>@context.Count.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine Datenqualitaetsauffaelligkeiten fuer diese Filter.", "No data-quality findings for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Spartenanalyse", "Division analysis")" Icon="@Icons.Material.Filled.AccountTree">
|
|
<MudTabs Elevation="0" Rounded="false" PanelClass="pt-4" @bind-ActivePanelIndex="_activeDivisionTabIndex">
|
|
<MudTabPanel Text="@T("Finanzanalyse", "Finance analysis")" Icon="@Icons.Material.Filled.PieChart">
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Gesamtumsatz", "Total sales")</MudText>
|
|
<MudText Typo="Typo.h6">@FormatValue(_financeResult.ProductFinanceSummary.TotalValue, _financeResult.ProductFinanceSummary.DisplayCurrency)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Zugeordneter Umsatz", "Assigned sales")</MudText>
|
|
<MudText Typo="Typo.h6">@FormatValue(_financeResult.ProductFinanceSummary.AssignedValue, _financeResult.ProductFinanceSummary.DisplayCurrency)</MudText>
|
|
<MudText Typo="Typo.caption">@FormatPercent(_financeResult.ProductFinanceSummary.AssignedValuePercent)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Nicht zugeordnet", "Unassigned")</MudText>
|
|
<MudText Typo="Typo.h6">@FormatValue(_financeResult.ProductFinanceSummary.UnassignedValue, _financeResult.ProductFinanceSummary.DisplayCurrency)</MudText>
|
|
<MudText Typo="Typo.caption">@FormatPercent(_financeResult.ProductFinanceSummary.UnassignedValuePercent)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Nicht im Stamm", "Not in master")</MudText>
|
|
<MudText Typo="Typo.h6">@FormatValue(_financeResult.ProductFinanceSummary.MissingReferenceValue, _financeResult.ProductFinanceSummary.DisplayCurrency)</MudText>
|
|
<MudText Typo="Typo.caption">@FormatPercent(_financeResult.ProductFinanceSummary.MissingReferenceValuePercent)</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudGrid Class="mb-2">
|
|
<MudItem xs="12" md="6">
|
|
<MudText Typo="Typo.h6">@T("Umsatz nach Produktsparte", "Sales by product division")</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudSelect T="string" @bind-Value="_productFinanceGroupLevel" Label="@T("Gruppierung", "Grouping")" Dense>
|
|
@foreach (var option in _productFinanceGroupingOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@T(option.GermanLabel, option.EnglishLabel)</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudButton Variant="@(_limitProductFinanceTop10 ? Variant.Filled : Variant.Outlined)"
|
|
Color="Color.Primary"
|
|
StartIcon="@Icons.Material.Filled.FilterAlt"
|
|
OnClick="ToggleProductFinanceTop10"
|
|
FullWidth>
|
|
@T("Top 10 anzeigen", "Show top 10")
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
<MudTable Items="BuildProductFinanceRows()" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Produktsparte", "Product division")</MudTh>
|
|
@if (ShowProductFamilyColumn)
|
|
{
|
|
<MudTh>@T("Produktfamilie", "Product family")</MudTh>
|
|
}
|
|
@if (ShowProductHierarchyColumn)
|
|
{
|
|
<MudTh>PAPH1</MudTh>
|
|
}
|
|
<MudTh>@T("Umsatz", "Sales")</MudTh>
|
|
<MudTh>@T("Anteil", "Share")</MudTh>
|
|
<MudTh>@T("Materialien", "Materials")</MudTh>
|
|
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
|
<MudTh>@T("Laender", "Countries")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>
|
|
<MudIcon Icon="@ResolveProductDivisionIcon(context.ProductDivisionCode, context.ProductDivisionText, context.ProductFamilyText, context.ProductHierarchyText)"
|
|
Size="Size.Small" Class="mr-1" />
|
|
@BuildCodeText(context.ProductDivisionCode, context.ProductDivisionText)
|
|
</MudTd>
|
|
@if (ShowProductFamilyColumn)
|
|
{
|
|
<MudTd>@BuildCodeText(context.ProductFamilyCode, context.ProductFamilyText)</MudTd>
|
|
}
|
|
@if (ShowProductHierarchyColumn)
|
|
{
|
|
<MudTd>@BuildCodeText(context.ProductHierarchyCode, context.ProductHierarchyText)</MudTd>
|
|
}
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
<MudTd>@FormatPercent(context.SharePercent)</MudTd>
|
|
<MudTd>@context.MaterialCount.ToString("N0")</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
<MudTd>@FormatCountriesWithFlags(context.Countries)</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine zugeordneten Spartenumsaetze fuer diese Filter.", "No assigned division sales for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Umsatzabdeckung nach Land", "Sales coverage by country")</MudText>
|
|
<MudTable Items="_financeResult.ProductFinanceCountryRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Gesamt", "Total")</MudTh>
|
|
<MudTh>@T("Zugeordnet", "Assigned")</MudTh>
|
|
<MudTh>@T("Nicht zugeordnet", "Unassigned")</MudTh>
|
|
<MudTh>@T("Nicht im Stamm", "Not in master")</MudTh>
|
|
<MudTh>@T("Material fehlt", "Material missing")</MudTh>
|
|
<MudTh>@T("Abdeckung", "Coverage")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Tsc</MudTd>
|
|
<MudTd>@FormatValue(context.TotalValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.AssignedValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.UnassignedValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.MissingReferenceValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatValue(context.MissingMaterialValue, context.Currency)</MudTd>
|
|
<MudTd>@FormatPercent(context.AssignedValuePercent)</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine Umsatzabdeckung fuer diese Filter.", "No sales coverage for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Zentrale Zuordnung", "Central mapping")" Icon="@Icons.Material.Filled.AccountTree">
|
|
<MudGrid Class="mb-4">
|
|
<MudItem xs="12" sm="6" md="2">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Materialien", "Materials")</MudText>
|
|
<MudText Typo="Typo.h6">@_financeResult.ProductAssignmentSummary.DistinctMaterialCount.ToString("N0")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="2">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Zugeordnet", "Assigned")</MudText>
|
|
<MudText Typo="Typo.h6">@_financeResult.ProductAssignmentSummary.MatchedMaterialCount.ToString("N0")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="2">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Nicht zugeordnet", "Unassigned")</MudText>
|
|
<MudText Typo="Typo.h6">@_financeResult.ProductAssignmentSummary.UnassignedMaterialCount.ToString("N0")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="2">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Nicht im Stamm", "Not in master")</MudText>
|
|
<MudText Typo="Typo.h6">@_financeResult.ProductAssignmentSummary.MissingReferenceMaterialCount.ToString("N0")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="2">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("Material fehlt", "Material missing")</MudText>
|
|
<MudText Typo="Typo.h6">@_financeResult.ProductAssignmentSummary.MissingMaterialNumberCount.ToString("N0")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="2">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.caption">@T("TR-AG Referenz", "TR AG reference")</MudText>
|
|
<MudText Typo="Typo.h6">@_financeResult.ProductAssignmentSummary.ReferenceMaterialCount.ToString("N0")</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudAlert Severity="Severity.Info" Dense Variant="Variant.Outlined" Class="mb-4">
|
|
@T("Diese Sicht prueft Materialnummern aller gefilterten Laender gegen die fuehrende TR-AG-Referenz aus `ProductDivisionRefSet`. Die Produktsparten der lokalen ERPs werden nicht verwendet.",
|
|
"This view checks material numbers from all filtered countries against the leading TR AG reference from `ProductDivisionRefSet`. Local ERP product divisions are not used.")
|
|
</MudAlert>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Abdeckung nach Land", "Coverage by country")</MudText>
|
|
<MudTable Items="_financeResult.ProductAssignmentCountryRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Materialien", "Materials")</MudTh>
|
|
<MudTh>@T("Zugeordnet", "Assigned")</MudTh>
|
|
<MudTh>@T("Nicht zugeordnet", "Unassigned")</MudTh>
|
|
<MudTh>@T("Nicht im Stamm", "Not in master")</MudTh>
|
|
<MudTh>@T("Material fehlt", "Material missing")</MudTh>
|
|
<MudTh>@T("Trefferquote", "Match rate")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Tsc</MudTd>
|
|
<MudTd>@context.DistinctMaterialCount.ToString("N0")</MudTd>
|
|
<MudTd>@context.MatchedMaterialCount.ToString("N0")</MudTd>
|
|
<MudTd>@context.UnassignedMaterialCount.ToString("N0")</MudTd>
|
|
<MudTd>@context.MissingReferenceMaterialCount.ToString("N0")</MudTd>
|
|
<MudTd>@context.MissingMaterialNumberCount.ToString("N0")</MudTd>
|
|
<MudTd>@FormatPercent(context.MatchPercent)</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine Materialdaten fuer diese Filter.", "No material data for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Materialpruefung gegen TR-AG-Referenz", "Material check against TR AG reference")</MudText>
|
|
<MudTable Items="_financeResult.ProductAssignmentRows" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Status", "Status")</MudTh>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>TSC</MudTh>
|
|
<MudTh>@T("Land-Material", "Local material")</MudTh>
|
|
<MudTh>@T("Land-Text", "Local text")</MudTh>
|
|
<MudTh>@T("TR-AG-MATNR", "TR AG MATNR")</MudTh>
|
|
<MudTh>PAPH1</MudTh>
|
|
<MudTh>@T("Produktfamilie", "Product family")</MudTh>
|
|
<MudTh>@T("Produktsparte", "Product division")</MudTh>
|
|
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
|
<MudTh>@T("Finance-Wert", "Finance value")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd><MudChip T="string" Size="Size.Small" Color="@ProductAssignmentColor(context.Status)" Variant="Variant.Outlined">@context.Status</MudChip></MudTd>
|
|
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
|
|
<MudTd>@context.Tsc</MudTd>
|
|
<MudTd>@context.Material</MudTd>
|
|
<MudTd>@context.ArticleName</MudTd>
|
|
<MudTd>@context.ReferenceMaterial</MudTd>
|
|
<MudTd>@BuildCodeText(context.ProductHierarchyCode, context.ProductHierarchyText)</MudTd>
|
|
<MudTd>@BuildCodeText(context.ProductFamilyCode, context.ProductFamilyText)</MudTd>
|
|
<MudTd>
|
|
<MudIcon Icon="@ResolveProductDivisionIcon(context.ProductDivisionCode, context.ProductDivisionText, context.ProductFamilyText, context.ProductHierarchyText)"
|
|
Size="Size.Small" Class="mr-1" />
|
|
@BuildCodeText(context.ProductDivisionCode, context.ProductDivisionText)
|
|
</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.body2">@T("Keine Materialpruefung fuer diese Filter.", "No material check for these filters.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("3D Datenanalyse", "3D data analysis")" Icon="@Icons.Material.Filled.ViewInAr">
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudGrid>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string" Value="_finance3dIndicator" ValueChanged="SetFinance3dIndicator" Label="@T("Indikator", "Indicator")" Dense>
|
|
@foreach (var option in _finance3dIndicatorOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@T(option.GermanLabel, option.EnglishLabel)</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudSelect T="string" Value="_finance3dChartType" ValueChanged="SetFinance3dChartType" Label="@T("Grafik", "Chart")" Dense>
|
|
@foreach (var option in _finance3dChartTypeOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@T(option.GermanLabel, option.EnglishLabel)</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudText Typo="Typo.caption">@T("Szenario-Faktor / Wechselkurs", "Scenario factor / exchange rate")</MudText>
|
|
<div class="finance-3d-range-row">
|
|
<MudButton Variant="Variant.Outlined" Size="Size.Small" OnClick="@(() => SetFinance3dScenarioFactorPreset(0.9d))">-10%</MudButton>
|
|
<input class="finance-3d-range"
|
|
type="range"
|
|
min="0.5"
|
|
max="1.5"
|
|
step="0.01"
|
|
value="@_finance3dScenarioFactor.ToString("0.00", CultureInfo.InvariantCulture)"
|
|
@oninput="SetFinance3dScenarioFactor" />
|
|
<MudText Typo="Typo.body2" Class="finance-3d-factor">@_finance3dScenarioFactor.ToString("0.00", CultureInfo.InvariantCulture)x</MudText>
|
|
<MudIconButton Icon="@Icons.Material.Filled.RestartAlt"
|
|
Size="Size.Small"
|
|
Color="Color.Default"
|
|
OnClick="ResetFinance3dScenarioFactor" />
|
|
<MudButton Variant="Variant.Outlined" Size="Size.Small" OnClick="@(() => SetFinance3dScenarioFactorPreset(1.1d))">+10%</MudButton>
|
|
</div>
|
|
@if (Finance3dScenarioAffectsValue)
|
|
{
|
|
<MudText Typo="Typo.caption">
|
|
@T("Basis", "Base"): @FormatFinance3dValue(Finance3dBaseTotal) |
|
|
@T("Szenario", "Scenario"): @FormatFinance3dValue(Finance3dScenarioTotal) |
|
|
@T("Delta", "Delta"): @FormatFinance3dValue(Finance3dScenarioDelta)
|
|
</MudText>
|
|
}
|
|
else
|
|
{
|
|
<MudText Typo="Typo.caption">
|
|
@T("Der Faktor wirkt nur auf Wertindikatoren, nicht auf Zeilenanzahlen.",
|
|
"The factor only affects value indicators, not row counts.")
|
|
</MudText>
|
|
}
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudText Typo="Typo.body2">
|
|
@T("Linke Maustaste drehen, Mausrad zoomen, Shift+Ziehen oder rechte Maustaste verschieben. Balken/Linie/Flaeche zeigen Land-Jahr-Verlauf, Kreis zeigt Laenderanteile.",
|
|
"Left mouse button rotates, mouse wheel zooms, Shift+drag or right mouse button pans. Bar/line/surface show country-year trend, pie shows country shares.")
|
|
</MudText>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-0 finance-3d-surface" Elevation="1">
|
|
<canvas @ref="_finance3dCanvas" class="finance-3d-canvas"></canvas>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
<MudTabPanel Text="@T("Rohdaten Diagnose", "Raw-data diagnostics")" Icon="@Icons.Material.Filled.QueryStats">
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudGrid>
|
|
<MudItem xs="12" md="6">
|
|
<MudSelect T="string" @bind-Value="_selectedFilePath" Label="@T("Vorhandene Excel-Datei", "Available Excel file")" Dense>
|
|
@foreach (var file in _files)
|
|
{
|
|
<MudSelectItem Value="@file.Path">@file.DisplayName</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string" @bind-Value="_selectedFileValueField" Label="@T("Summenfeld", "Value field")" Dense>
|
|
@foreach (var option in _valueFieldOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@option.Label</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string" @bind-Value="_selectedFileTargetCurrency" Label="@T("Anzeige-Waehrung", "Display currency")" Dense>
|
|
@foreach (var option in _currencyOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@option.Label</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudStack Row Spacing="2">
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Info" OnClick="ReloadFiles"
|
|
StartIcon="@Icons.Material.Filled.Refresh" Disabled="_loadingFiles">
|
|
@T("Dateien laden", "Load files")
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="Analyze"
|
|
StartIcon="@Icons.Material.Filled.Analytics" Disabled="_analyzing || string.IsNullOrWhiteSpace(_selectedFilePath)">
|
|
@(_analyzing ? T("Analysiere...", "Analyzing...") : T("Cockpit erzeugen", "Build cockpit"))
|
|
</MudButton>
|
|
</MudStack>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-3">@T("Zentrale Roh-Auswertung", "Central raw analysis")</MudText>
|
|
<MudAlert Severity="Severity.Info" Dense Variant="Variant.Outlined" Class="mb-3">
|
|
@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.")
|
|
</MudAlert>
|
|
<MudAlert Severity="Severity.Warning" Dense Variant="Variant.Outlined" Class="mb-3">
|
|
@T("Diese Analyse ist eine Plausibilitaets- und Rohdatensicht. Fuer den verbindlichen Finance-Abgleich bitte `Soll/Ist Vergleich` oder im Endexcel die `Finance | ...`-Spalten verwenden.",
|
|
"This analysis is a plausibility/raw-data view. For the authoritative finance reconciliation, use `Actual/reference comparison` or the `Finance | ...` columns in the final Excel.")
|
|
</MudAlert>
|
|
<MudGrid>
|
|
<MudItem xs="12" md="2">
|
|
<MudSelect T="int" @bind-Value="_selectedCentralYear" Label='@T("Jahr", "Year")' Dense>
|
|
@foreach (var year in _centralYears)
|
|
{
|
|
<MudSelectItem Value="@year">@year</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudTextField @bind-Value="_centralLandFilter" Label="@T("Landfilter", "Country filter")" />
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudTextField @bind-Value="_centralTscFilter" Label="TSC" />
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudSelect T="int?" @bind-Value="_selectedCentralMonth" Label='@T("Monat (optional)", "Month (optional)")' Dense Clearable>
|
|
@foreach (var month in Enumerable.Range(1, 12))
|
|
{
|
|
<MudSelectItem Value="@((int?)month)">@($"{month:D2}")</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string" @bind-Value="_selectedCentralValueField" Label="@T("Summenfeld", "Value field")" Dense>
|
|
@foreach (var option in _valueFieldOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@option.Label</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string"
|
|
SelectedValues="_selectedCentralAdditionalValueFields"
|
|
SelectedValuesChanged="SetSelectedCentralAdditionalValueFields"
|
|
MultiSelection="true"
|
|
Label="@T("Weitere Summenfelder", "Additional value fields")"
|
|
Dense>
|
|
@foreach (var option in _valueFieldOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@option.Label</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudSelect T="string" @bind-Value="_selectedCentralTargetCurrency" Label="@T("Anzeige-Waehrung", "Display currency")" Dense>
|
|
@foreach (var option in _currencyOptions)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@option.Label</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12">
|
|
<MudStack Row Spacing="2" AlignItems="AlignItems.Center">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Secondary" OnClick="AnalyzeCentral"
|
|
StartIcon="@Icons.Material.Filled.QueryStats" Disabled="_analyzingCentral || _selectedCentralYear == 0">
|
|
@(_analyzingCentral ? T("Analysiere...", "Analyzing...") : T("Zentrale Auswertung laden", "Load central analysis"))
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Default" OnClick="ClearCentralScope"
|
|
StartIcon="@Icons.Material.Filled.FilterAltOff">
|
|
@T("Global", "Global")
|
|
</MudButton>
|
|
@if (!string.IsNullOrWhiteSpace(_centralLandFilter) || !string.IsNullOrWhiteSpace(_centralTscFilter))
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Info" Variant="Variant.Outlined">
|
|
@T("Gefiltert", "Filtered"): @($"{(_centralLandFilter ?? "-")} / {(_centralTscFilter ?? "-")}")
|
|
</MudChip>
|
|
}
|
|
</MudStack>
|
|
</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">@T("Land", "Country")</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">@_result.Summary.ValueFieldLabel</MudText><MudText Typo="Typo.h6">@FormatValue(_result.Summary.AggregatedValueTotal, _result.Summary.DisplayCurrency)</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="3"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">@T("Nicht umgerechnet", "Not converted")</MudText><MudText Typo="Typo.h6">@_result.Summary.MissingExchangeRateCount.ToString("N0")</MudText></MudPaper></MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Management Aussagen", "Management statements")</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">@T("Top Kunden", "Top customers")</MudText>
|
|
@foreach (var item in _result.TopCustomers)
|
|
{
|
|
<MudText Typo="Typo.body2">@($"{item.Label}: {FormatValue(item.Value, _result.Summary.DisplayCurrency)} ({item.SharePercent:F1}%)")</MudText>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Top Produktgruppen", "Top product groups")</MudText>
|
|
@foreach (var item in _result.TopProductGroups)
|
|
{
|
|
<MudText Typo="Typo.body2">@($"{item.Label}: {FormatValue(item.Value, _result.Summary.DisplayCurrency)} ({item.SharePercent:F1}%)")</MudText>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Top Sales Owner", "Top sales owner")</MudText>
|
|
@foreach (var item in _result.TopSalesEmployees)
|
|
{
|
|
<MudText Typo="Typo.body2">@($"{item.Label}: {FormatValue(item.Value, _result.Summary.DisplayCurrency)} ({item.SharePercent:F1}%)")</MudText>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Datenqualitaet", "Data quality")</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">@T("Zeilen", "Rows")</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">@T("Rechnungen", "Invoices")</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">@T("Standorte", "Sites")</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">@T("Laender", "Countries")</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">@_centralResult.Summary.ValueFieldLabel</MudText><MudText Typo="Typo.h6">@FormatValue(_centralResult.Summary.ValueTotal, _centralResult.Summary.DisplayCurrency)</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">@T("Nicht umgerechnet", "Not converted")</MudText><MudText Typo="Typo.h6">@_centralResult.Summary.MissingExchangeRateCount.ToString("N0")</MudText></MudPaper></MudItem>
|
|
<MudItem xs="12" md="2"><MudPaper Class="pa-4"><MudText Typo="Typo.caption">@T("Kursdatum", "Rate date")</MudText><MudText Typo="Typo.body2">@_centralResult.Summary.ExchangeRateDateLabel</MudText></MudPaper></MudItem>
|
|
</MudGrid>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Hinweise", "Notes")</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">@T("Jahreswerte", "Yearly values")</MudText>
|
|
<MudTable Items="_centralResult.YearlyTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Jahr", "Year")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@_centralResult.Summary.ValueFieldLabel</MudTh>
|
|
@foreach (var field in _centralResult.AdditionalValueFields)
|
|
{
|
|
<MudTh>@field.Label</MudTh>
|
|
}
|
|
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Year</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.SalesValue, context.Currency)</MudTd>
|
|
@foreach (var field in _centralResult.AdditionalValueFields)
|
|
{
|
|
<MudTd>@FormatAdditionalValue(context, field.Key)</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">@T("Monatswerte", "Monthly values")</MudText>
|
|
<MudTable Items="_centralResult.MonthlyTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Monat", "Month")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@_centralResult.Summary.ValueFieldLabel</MudTh>
|
|
@foreach (var field in _centralResult.AdditionalValueFields)
|
|
{
|
|
<MudTh>@field.Label</MudTh>
|
|
}
|
|
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.SalesValue, context.Currency)</MudTd>
|
|
@foreach (var field in _centralResult.AdditionalValueFields)
|
|
{
|
|
<MudTd>@FormatAdditionalValue(context, field.Key)</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">@T("Tageswerte im ausgewaehlten Monat", "Daily values in selected month")</MudText>
|
|
<MudTable Items="_centralResult.DailyTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Tag", "Day")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@_centralResult.Summary.ValueFieldLabel</MudTh>
|
|
@foreach (var field in _centralResult.AdditionalValueFields)
|
|
{
|
|
<MudTh>@field.Label</MudTh>
|
|
}
|
|
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.SalesValue, context.Currency)</MudTd>
|
|
@foreach (var field in _centralResult.AdditionalValueFields)
|
|
{
|
|
<MudTd>@FormatAdditionalValue(context, field.Key)</MudTd>
|
|
}
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.caption">@T("Fuer die Tagessicht bitte zusaetzlich einen Monat waehlen.", "Please select a month as well for the daily view.")</MudText>
|
|
</NoRecordsContent>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" md="6">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Werte nach Quelle", "Values by source")</MudText>
|
|
<MudTable Items="_centralResult.SourceSystemTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Quelle", "Source")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@_centralResult.Summary.ValueFieldLabel</MudTh>
|
|
<MudTh>@T("Rechnungen", "Invoices")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.SalesValue, context.Currency)</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">@T("Werte nach Land", "Values by country")</MudText>
|
|
<MudTable Items="_centralResult.CountryTotals" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>@T("Land", "Country")</MudTh>
|
|
<MudTh>@T("Waehrung", "Currency")</MudTh>
|
|
<MudTh>@_centralResult.Summary.ValueFieldLabel</MudTh>
|
|
<MudTh>@T("Rechnungen", "Invoices")</MudTh>
|
|
<MudTh>@T("Zeilen", "Rows")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Label</MudTd>
|
|
<MudTd>@context.Currency</MudTd>
|
|
<MudTd>@FormatValue(context.SalesValue, context.Currency)</MudTd>
|
|
<MudTd>@context.InvoiceCount.ToString("N0")</MudTd>
|
|
<MudTd>@context.RowCount.ToString("N0")</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
}
|
|
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
}
|
|
|
|
@code {
|
|
[Parameter]
|
|
[SupplyParameterFromQuery(Name = "section")]
|
|
public string? Section { get; set; }
|
|
|
|
[Parameter]
|
|
[SupplyParameterFromQuery(Name = "division")]
|
|
public string? Division { get; set; }
|
|
|
|
private List<ManagementCockpitFileOption> _files = [];
|
|
private List<int> _centralYears = [];
|
|
private List<int> _financeYearOptions = [];
|
|
private List<string> _financeCountryOptions = [];
|
|
private List<string> _financeCurrencyOptions = [];
|
|
private List<ManagementCockpitValueFieldOption> _valueFieldOptions = [];
|
|
private readonly List<CurrencySelectOption> _currencyOptions =
|
|
[
|
|
new(ManagementCockpitCurrencyOptions.Chf, "CHF"),
|
|
new(ManagementCockpitCurrencyOptions.Eur, "EUR"),
|
|
new(ManagementCockpitCurrencyOptions.Usd, "USD"),
|
|
new(ManagementCockpitCurrencyOptions.Native, "Original")
|
|
];
|
|
private readonly List<ProductFinanceGroupingOption> _productFinanceGroupingOptions =
|
|
[
|
|
new(ProductFinanceGroupLevels.Hierarchy, "PAPH1 Detail", "PAPH1 detail"),
|
|
new(ProductFinanceGroupLevels.Family, "Produktfamilie", "Product family"),
|
|
new(ProductFinanceGroupLevels.Division, "Produktsparte", "Product division")
|
|
];
|
|
private readonly List<Finance3dIndicatorOption> _finance3dIndicatorOptions =
|
|
[
|
|
new(Finance3dIndicators.Actual, "Net Sales Actual", "Net sales actual"),
|
|
new(Finance3dIndicators.IncludedRows, "Enthaltene Zeilen", "Included rows"),
|
|
new(Finance3dIndicators.ExcludedRows, "Ausgeschlossene Zeilen", "Excluded rows"),
|
|
new(Finance3dIndicators.Deviation, "Soll/Ist Differenz Filterjahr", "Actual/reference difference filter year")
|
|
];
|
|
private readonly List<Finance3dChartTypeOption> _finance3dChartTypeOptions =
|
|
[
|
|
new(Finance3dChartTypes.Bar, "Balken", "Bar"),
|
|
new(Finance3dChartTypes.Line, "Linie", "Line"),
|
|
new(Finance3dChartTypes.Surface, "Flaeche", "Surface"),
|
|
new(Finance3dChartTypes.Pie, "Kreis / Anteil", "Pie / share")
|
|
];
|
|
private string? _selectedFilePath;
|
|
private ManagementCockpitResult? _result;
|
|
private ManagementCockpitCentralResult? _centralResult;
|
|
private ManagementFinanceSummaryResult? _financeResult;
|
|
private int _selectedFinanceYear;
|
|
private string? _selectedFinanceCountryKey;
|
|
private string? _selectedFinanceCurrency;
|
|
private int _selectedCentralYear;
|
|
private int? _selectedCentralMonth;
|
|
private string? _centralLandFilter;
|
|
private string? _centralTscFilter;
|
|
private string _selectedFileValueField = ManagementCockpitValueFieldKeys.SalesPriceValue;
|
|
private string _selectedCentralValueField = ManagementCockpitValueFieldKeys.SalesPriceValue;
|
|
private IEnumerable<string> _selectedCentralAdditionalValueFields = [];
|
|
private string _selectedFileTargetCurrency = ManagementCockpitCurrencyOptions.Eur;
|
|
private string _selectedCentralTargetCurrency = ManagementCockpitCurrencyOptions.Native;
|
|
private bool _loadingFiles;
|
|
private bool _analyzing;
|
|
private bool _analyzingCentral;
|
|
private bool _analyzingFinance;
|
|
private int _activeOverviewTabIndex;
|
|
private int _activeFinanceTabIndex;
|
|
private int _activeDivisionTabIndex;
|
|
private string _productFinanceGroupLevel = ProductFinanceGroupLevels.Hierarchy;
|
|
private bool _limitProductFinanceTop10;
|
|
private string _finance3dIndicator = Finance3dIndicators.Actual;
|
|
private string _finance3dChartType = Finance3dChartTypes.Bar;
|
|
private double _finance3dScenarioFactor = 1d;
|
|
private ElementReference _finance3dCanvas;
|
|
private bool _finance3dNeedsRender;
|
|
|
|
private bool ShowProductFamilyColumn => _productFinanceGroupLevel != ProductFinanceGroupLevels.Division;
|
|
private bool Finance3dScenarioAffectsValue => _finance3dIndicator is Finance3dIndicators.Actual or Finance3dIndicators.Deviation;
|
|
private decimal Finance3dBaseTotal => CalculateFinance3dBaseTotal();
|
|
private decimal Finance3dScenarioTotal => Finance3dScenarioAffectsValue
|
|
? Finance3dBaseTotal * (decimal)_finance3dScenarioFactor
|
|
: Finance3dBaseTotal;
|
|
private decimal Finance3dScenarioDelta => Finance3dScenarioTotal - Finance3dBaseTotal;
|
|
|
|
private bool ShowProductHierarchyColumn => _productFinanceGroupLevel == ProductFinanceGroupLevels.Hierarchy;
|
|
|
|
protected override void OnParametersSet()
|
|
{
|
|
_activeOverviewTabIndex = string.IsNullOrWhiteSpace(Section) ? 0 : 1;
|
|
|
|
if (string.Equals(Section, "division", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_activeFinanceTabIndex = ManagementFinanceTabIndexes.Division;
|
|
_activeDivisionTabIndex = string.Equals(Division, "central", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
|
}
|
|
else if (string.IsNullOrWhiteSpace(Section))
|
|
{
|
|
_activeFinanceTabIndex = ManagementFinanceTabIndexes.Summary;
|
|
_activeDivisionTabIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
_activeFinanceTabIndex = Section.ToLowerInvariant() switch
|
|
{
|
|
"countries" => ManagementFinanceTabIndexes.Countries,
|
|
"status" => ManagementFinanceTabIndexes.Status,
|
|
"deviations" => ManagementFinanceTabIndexes.Deviations,
|
|
"credits" => ManagementFinanceTabIndexes.Credits,
|
|
"quality" => ManagementFinanceTabIndexes.Quality,
|
|
"3d" => ManagementFinanceTabIndexes.ThreeD,
|
|
"raw" => ManagementFinanceTabIndexes.Raw,
|
|
_ => ManagementFinanceTabIndexes.Summary
|
|
};
|
|
}
|
|
}
|
|
|
|
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;
|
|
_selectedFinanceYear = _selectedCentralYear;
|
|
await AnalyzeFinanceSummary();
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (_finance3dNeedsRender && _financeResult is not null)
|
|
{
|
|
_finance3dNeedsRender = false;
|
|
await RenderFinance3dAsync();
|
|
}
|
|
}
|
|
|
|
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 async Task AnalyzeFinanceSummary()
|
|
{
|
|
_analyzingFinance = true;
|
|
try
|
|
{
|
|
_financeResult = await CockpitPageService.AnalyzeFinanceSummaryAsync(
|
|
_selectedFinanceYear,
|
|
_selectedFinanceCountryKey,
|
|
_selectedFinanceCurrency);
|
|
|
|
_financeYearOptions = _financeResult.YearOptions;
|
|
_financeCountryOptions = _financeResult.CountryOptions;
|
|
_financeCurrencyOptions = _financeResult.CurrencyOptions;
|
|
_selectedFinanceYear = _financeResult.Filter.Year;
|
|
_finance3dNeedsRender = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add(string.Format(T("Finance Summary konnte nicht erzeugt werden: {0}", "Could not build finance summary: {0}"), ex.Message), Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
_analyzingFinance = false;
|
|
}
|
|
}
|
|
|
|
private void ClearCentralScope()
|
|
{
|
|
_centralLandFilter = null;
|
|
_centralTscFilter = null;
|
|
}
|
|
|
|
private void ToggleProductFinanceTop10()
|
|
{
|
|
_limitProductFinanceTop10 = !_limitProductFinanceTop10;
|
|
}
|
|
|
|
private async Task SetFinance3dIndicator(string value)
|
|
{
|
|
_finance3dIndicator = string.IsNullOrWhiteSpace(value) ? Finance3dIndicators.Actual : value;
|
|
await RenderFinance3dAsync();
|
|
}
|
|
|
|
private async Task SetFinance3dChartType(string value)
|
|
{
|
|
_finance3dChartType = string.IsNullOrWhiteSpace(value) ? Finance3dChartTypes.Bar : value;
|
|
await RenderFinance3dAsync();
|
|
}
|
|
|
|
private async Task SetFinance3dScenarioFactor(ChangeEventArgs args)
|
|
{
|
|
if (double.TryParse(Convert.ToString(args.Value, CultureInfo.InvariantCulture), NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
|
|
{
|
|
_finance3dScenarioFactor = Math.Clamp(value, 0.5d, 1.5d);
|
|
await UpdateFinance3dScenarioFactorAsync();
|
|
}
|
|
}
|
|
|
|
private async Task ResetFinance3dScenarioFactor()
|
|
{
|
|
_finance3dScenarioFactor = 1d;
|
|
await UpdateFinance3dScenarioFactorAsync();
|
|
}
|
|
|
|
private async Task SetFinance3dScenarioFactorPreset(double value)
|
|
{
|
|
_finance3dScenarioFactor = Math.Clamp(value, 0.5d, 1.5d);
|
|
await UpdateFinance3dScenarioFactorAsync();
|
|
}
|
|
|
|
private async Task RenderFinance3dAsync()
|
|
{
|
|
if (_financeResult is null)
|
|
return;
|
|
|
|
var rows = BuildFinance3dRows();
|
|
await JsRuntime.InvokeVoidAsync("trafagFinance3d.render", _finance3dCanvas, rows, new
|
|
{
|
|
indicator = _finance3dIndicator,
|
|
title = ResolveFinance3dIndicatorLabel(_finance3dIndicator),
|
|
chartType = _finance3dChartType,
|
|
scenarioFactor = Finance3dScenarioAffectsValue ? _finance3dScenarioFactor : 1d
|
|
});
|
|
}
|
|
|
|
private async Task UpdateFinance3dScenarioFactorAsync()
|
|
{
|
|
await JsRuntime.InvokeVoidAsync(
|
|
"trafagFinance3d.updateFactor",
|
|
_finance3dCanvas,
|
|
Finance3dScenarioAffectsValue ? _finance3dScenarioFactor : 1d);
|
|
}
|
|
|
|
private IReadOnlyList<object> BuildFinance3dRows()
|
|
{
|
|
if (_financeResult is null)
|
|
return [];
|
|
|
|
var deviationsByKey = _financeResult.CountryRows
|
|
.Where(row => row.Difference.HasValue)
|
|
.GroupBy(row => $"{row.Year}|{row.CountryKey}", StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(
|
|
group => group.Key,
|
|
group => group.Sum(row => Math.Abs(row.Difference!.Value)));
|
|
|
|
var sourceRows = _finance3dIndicator == Finance3dIndicators.Deviation
|
|
? _financeResult.Rows
|
|
: (_financeResult.YearCountryRows.Count > 0 ? _financeResult.YearCountryRows : _financeResult.Rows);
|
|
|
|
return sourceRows
|
|
.OrderBy(row => row.CountryKey, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(row => row.Year)
|
|
.Select(row =>
|
|
{
|
|
deviationsByKey.TryGetValue($"{row.Year}|{row.CountryKey}", out var deviation);
|
|
var value = _finance3dIndicator switch
|
|
{
|
|
Finance3dIndicators.IncludedRows => row.IncludedRows,
|
|
Finance3dIndicators.ExcludedRows => row.ExcludedRows,
|
|
Finance3dIndicators.Deviation => deviation,
|
|
_ => Math.Abs(row.NetSalesActual)
|
|
};
|
|
return new
|
|
{
|
|
country = row.CountryKey,
|
|
year = row.Year,
|
|
currency = row.Currency,
|
|
value
|
|
};
|
|
})
|
|
.Cast<object>()
|
|
.ToList();
|
|
}
|
|
|
|
private decimal CalculateFinance3dBaseTotal()
|
|
{
|
|
if (_financeResult is null)
|
|
return 0m;
|
|
|
|
var sourceRows = _finance3dIndicator == Finance3dIndicators.Deviation
|
|
? _financeResult.Rows
|
|
: (_financeResult.YearCountryRows.Count > 0 ? _financeResult.YearCountryRows : _financeResult.Rows);
|
|
|
|
if (_finance3dIndicator == Finance3dIndicators.Deviation)
|
|
{
|
|
return _financeResult.CountryRows
|
|
.Where(row => row.Difference.HasValue)
|
|
.Sum(row => Math.Abs(row.Difference!.Value));
|
|
}
|
|
|
|
return _finance3dIndicator switch
|
|
{
|
|
Finance3dIndicators.IncludedRows => sourceRows.Sum(row => row.IncludedRows),
|
|
Finance3dIndicators.ExcludedRows => sourceRows.Sum(row => row.ExcludedRows),
|
|
_ => sourceRows.Sum(row => Math.Abs(row.NetSalesActual))
|
|
};
|
|
}
|
|
|
|
private string FormatFinance3dValue(decimal value)
|
|
=> value.ToString("N0", CultureInfo.CurrentCulture);
|
|
|
|
private string ResolveFinance3dIndicatorLabel(string key)
|
|
=> _finance3dIndicatorOptions.FirstOrDefault(option => option.Key == key) is { } option
|
|
? T(option.GermanLabel, option.EnglishLabel)
|
|
: T("Net Sales Actual", "Net sales actual");
|
|
|
|
private IReadOnlyList<ManagementProductDivisionFinanceRow> BuildProductFinanceRows()
|
|
{
|
|
if (_financeResult is null)
|
|
return [];
|
|
|
|
var sourceRows = _financeResult.ProductDivisionFinanceRows;
|
|
var totalsByCurrency = sourceRows
|
|
.GroupBy(row => row.Currency, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(group => group.Key, group => group.Sum(row => row.NetSalesActual), StringComparer.OrdinalIgnoreCase);
|
|
|
|
var rows = sourceRows
|
|
.GroupBy(row => BuildProductFinanceGroupKey(row))
|
|
.Select(group =>
|
|
{
|
|
var value = group.Sum(row => row.NetSalesActual);
|
|
totalsByCurrency.TryGetValue(group.Key.Currency, out var total);
|
|
|
|
return new ManagementProductDivisionFinanceRow
|
|
{
|
|
ProductDivisionCode = group.Key.ProductDivisionCode,
|
|
ProductDivisionText = group.Key.ProductDivisionText,
|
|
ProductFamilyCode = group.Key.ProductFamilyCode,
|
|
ProductFamilyText = group.Key.ProductFamilyText,
|
|
ProductHierarchyCode = group.Key.ProductHierarchyCode,
|
|
ProductHierarchyText = group.Key.ProductHierarchyText,
|
|
Currency = group.Key.Currency,
|
|
NetSalesActual = value,
|
|
SharePercent = PercentOf(value, total),
|
|
MaterialCount = group.Sum(row => row.MaterialCount),
|
|
RowCount = group.Sum(row => row.RowCount),
|
|
Countries = JoinCountries(group.Select(row => row.Countries))
|
|
};
|
|
})
|
|
.OrderByDescending(row => Math.Abs(row.NetSalesActual))
|
|
.ThenBy(row => row.ProductDivisionCode, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(row => row.ProductFamilyCode, StringComparer.OrdinalIgnoreCase)
|
|
.ThenBy(row => row.ProductHierarchyCode, StringComparer.OrdinalIgnoreCase)
|
|
.ToList();
|
|
|
|
return _limitProductFinanceTop10 ? rows.Take(10).ToList() : rows;
|
|
}
|
|
|
|
private ProductFinanceGroupKey BuildProductFinanceGroupKey(ManagementProductDivisionFinanceRow row)
|
|
{
|
|
return _productFinanceGroupLevel switch
|
|
{
|
|
ProductFinanceGroupLevels.Division => new ProductFinanceGroupKey(
|
|
row.ProductDivisionCode,
|
|
row.ProductDivisionText,
|
|
string.Empty,
|
|
string.Empty,
|
|
string.Empty,
|
|
string.Empty,
|
|
row.Currency),
|
|
ProductFinanceGroupLevels.Family => new ProductFinanceGroupKey(
|
|
row.ProductDivisionCode,
|
|
row.ProductDivisionText,
|
|
row.ProductFamilyCode,
|
|
row.ProductFamilyText,
|
|
string.Empty,
|
|
string.Empty,
|
|
row.Currency),
|
|
_ => new ProductFinanceGroupKey(
|
|
row.ProductDivisionCode,
|
|
row.ProductDivisionText,
|
|
row.ProductFamilyCode,
|
|
row.ProductFamilyText,
|
|
row.ProductHierarchyCode,
|
|
row.ProductHierarchyText,
|
|
row.Currency)
|
|
};
|
|
}
|
|
|
|
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 static string FormatNullableValue(decimal? value, string currency)
|
|
=> value.HasValue ? FormatValue(value.Value, currency) : "-";
|
|
|
|
private static string FormatPercent(decimal? value)
|
|
=> value.HasValue ? $"{value.Value:N1}%" : "-";
|
|
|
|
private static decimal PercentOf(decimal value, decimal total)
|
|
=> total == 0m ? 0m : value * 100m / total;
|
|
|
|
private static string FormatDateTime(DateTime? value)
|
|
=> value.HasValue ? value.Value.ToLocalTime().ToString("dd.MM.yyyy HH:mm") : "-";
|
|
|
|
private static string FormatManualImportStatus(ManagementFinanceDataStatusRow row)
|
|
{
|
|
if (!string.Equals(row.SourceSystem, "MANUAL_EXCEL", StringComparison.OrdinalIgnoreCase))
|
|
return "-";
|
|
|
|
if (!string.IsNullOrWhiteSpace(row.ManualImportFilePath))
|
|
return row.ManualImportLastUploadedAtUtc.HasValue
|
|
? $"{System.IO.Path.GetFileName(row.ManualImportFilePath)} / {FormatDateTime(row.ManualImportLastUploadedAtUtc)}"
|
|
: System.IO.Path.GetFileName(row.ManualImportFilePath);
|
|
|
|
return "kein Pfad";
|
|
}
|
|
|
|
private string BuildDataStatusText(ManagementFinanceCountryStatusRow countryRow)
|
|
{
|
|
if (_financeResult is null)
|
|
return "-";
|
|
|
|
var tscs = countryRow.Tscs
|
|
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
var matchingRows = _financeResult.DataStatusRows
|
|
.Where(row => row.Land.Equals(countryRow.CountryKey, StringComparison.OrdinalIgnoreCase) ||
|
|
tscs.Contains(row.Tsc))
|
|
.OrderByDescending(row => row.LatestExportAt ?? row.LatestStoredAtUtc ?? DateTime.MinValue)
|
|
.ToList();
|
|
|
|
var latest = matchingRows.FirstOrDefault();
|
|
if (latest is null)
|
|
return "-";
|
|
|
|
var date = latest.LatestExportAt ?? latest.LatestStoredAtUtc;
|
|
var status = string.IsNullOrWhiteSpace(latest.LatestExportStatus) ? latest.SourceSystem : latest.LatestExportStatus;
|
|
return $"{status} / {FormatDateTime(date)}";
|
|
}
|
|
|
|
private string BuildQuickFinanceNote(ManagementFinanceCountryStatusRow row)
|
|
{
|
|
if (!row.ReferenceValue.HasValue)
|
|
return T("Kein Sollwert gepflegt.", "No reference value maintained.");
|
|
if (row.Status == "OK")
|
|
return T("Freigabefaehig.", "Ready for approval.");
|
|
if (row.Difference.HasValue)
|
|
return T("Abweichung pruefen.", "Check deviation.");
|
|
|
|
return T("Pruefen.", "Check.");
|
|
}
|
|
|
|
private static Color StatusColor(string status) => status switch
|
|
{
|
|
"OK" => Color.Success,
|
|
"Pruefen" => Color.Warning,
|
|
_ => Color.Default
|
|
};
|
|
|
|
private static Color SeverityColor(string severity) => severity switch
|
|
{
|
|
"Warning" => Color.Warning,
|
|
"Error" => Color.Error,
|
|
_ => Color.Info
|
|
};
|
|
|
|
private static Color ProductAssignmentColor(string status) => status switch
|
|
{
|
|
"Zugeordnet" => Color.Success,
|
|
"Nicht zugeordnet" => Color.Warning,
|
|
"Nicht im TR-AG-Stamm" => Color.Error,
|
|
"Material fehlt" => Color.Default,
|
|
_ => Color.Info
|
|
};
|
|
|
|
private static string BuildCodeText(string code, string text)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(code))
|
|
return string.IsNullOrWhiteSpace(text) ? "-" : text;
|
|
|
|
return string.IsNullOrWhiteSpace(text) ? code : $"{code} - {text}";
|
|
}
|
|
|
|
private static string ResolveProductDivisionIcon(
|
|
string productDivisionCode,
|
|
string productDivisionText,
|
|
string productFamilyText,
|
|
string productHierarchyText)
|
|
{
|
|
var combinedText = string.Join(' ', productDivisionText, productFamilyText, productHierarchyText).ToUpperInvariant();
|
|
|
|
if (string.Equals(productDivisionCode, "UNASS", StringComparison.OrdinalIgnoreCase) ||
|
|
combinedText.Contains("NICHT ZUGEORDNET", StringComparison.OrdinalIgnoreCase) ||
|
|
combinedText.Contains("UNASS", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Icons.Material.Filled.HelpOutline;
|
|
}
|
|
|
|
if (combinedText.Contains("GAS", StringComparison.OrdinalIgnoreCase) ||
|
|
combinedText.Contains("DENSITY", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Icons.Material.Filled.Sensors;
|
|
}
|
|
|
|
if (combinedText.Contains("PRESSURE", StringComparison.OrdinalIgnoreCase) ||
|
|
combinedText.Contains("DRUCK", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Icons.Material.Filled.Compress;
|
|
}
|
|
|
|
if (combinedText.Contains("TEMP", StringComparison.OrdinalIgnoreCase) ||
|
|
combinedText.Contains("THERMOSTAT", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Icons.Material.Filled.DeviceThermostat;
|
|
}
|
|
|
|
if (combinedText.Contains("SWITCH", StringComparison.OrdinalIgnoreCase) ||
|
|
combinedText.Contains("SCHALTER", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Icons.Material.Filled.ToggleOn;
|
|
}
|
|
|
|
if (combinedText.Contains("ACCESS", StringComparison.OrdinalIgnoreCase) ||
|
|
combinedText.Contains("ZUBEH", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Icons.Material.Filled.Extension;
|
|
}
|
|
|
|
return Icons.Material.Filled.Category;
|
|
}
|
|
|
|
private static string JoinCountries(IEnumerable<string> countryValues)
|
|
{
|
|
var countries = countryValues
|
|
.SelectMany(value => value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.OrderBy(value => value, StringComparer.OrdinalIgnoreCase)
|
|
.Select(FormatCountryWithFlag);
|
|
|
|
return string.Join(", ", countries);
|
|
}
|
|
|
|
private static string FormatCountriesWithFlags(string countries)
|
|
=> string.IsNullOrWhiteSpace(countries)
|
|
? "-"
|
|
: JoinCountries([countries]);
|
|
|
|
private static string FormatCountryWithFlag(string country)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(country))
|
|
return "-";
|
|
|
|
var normalized = country.Trim().ToUpperInvariant();
|
|
if (normalized.Length != 2 || normalized.Any(character => character is < 'A' or > 'Z'))
|
|
return country;
|
|
|
|
var flag = string.Concat(normalized.Select(character => char.ConvertFromUtf32(0x1F1E6 + character - 'A')));
|
|
return $"{flag} {normalized}";
|
|
}
|
|
|
|
private void SetSelectedCentralAdditionalValueFields(IEnumerable<string> 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 string T(string german, string english) => UiText.Text(german, english);
|
|
|
|
private static class ProductFinanceGroupLevels
|
|
{
|
|
public const string Hierarchy = "hierarchy";
|
|
public const string Family = "family";
|
|
public const string Division = "division";
|
|
}
|
|
|
|
private static class ManagementFinanceTabIndexes
|
|
{
|
|
public const int Summary = 0;
|
|
public const int Countries = 1;
|
|
public const int Status = 2;
|
|
public const int Deviations = 3;
|
|
public const int Credits = 4;
|
|
public const int Quality = 5;
|
|
public const int Division = 6;
|
|
public const int ThreeD = 7;
|
|
public const int Raw = 8;
|
|
}
|
|
|
|
private static class Finance3dIndicators
|
|
{
|
|
public const string Actual = "actual";
|
|
public const string IncludedRows = "includedRows";
|
|
public const string ExcludedRows = "excludedRows";
|
|
public const string Deviation = "deviation";
|
|
}
|
|
|
|
private static class Finance3dChartTypes
|
|
{
|
|
public const string Bar = "bar";
|
|
public const string Line = "line";
|
|
public const string Surface = "surface";
|
|
public const string Pie = "pie";
|
|
}
|
|
|
|
private sealed record ProductFinanceGroupingOption(string Key, string GermanLabel, string EnglishLabel);
|
|
private sealed record Finance3dIndicatorOption(string Key, string GermanLabel, string EnglishLabel);
|
|
private sealed record Finance3dChartTypeOption(string Key, string GermanLabel, string EnglishLabel);
|
|
|
|
private sealed record ProductFinanceGroupKey(
|
|
string ProductDivisionCode,
|
|
string ProductDivisionText,
|
|
string ProductFamilyCode,
|
|
string ProductFamilyText,
|
|
string ProductHierarchyCode,
|
|
string ProductHierarchyText,
|
|
string Currency);
|
|
|
|
private sealed record CurrencySelectOption(string Key, string Label);
|
|
}
|