cockpit vorbereitung
This commit is contained in:
@@ -33,6 +33,37 @@
|
|||||||
</MudGrid>
|
</MudGrid>
|
||||||
</MudPaper>
|
</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)
|
@if (_result is not null)
|
||||||
{
|
{
|
||||||
<MudGrid Class="mb-4">
|
<MudGrid Class="mb-4">
|
||||||
@@ -91,16 +122,147 @@
|
|||||||
</MudPaper>
|
</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 {
|
@code {
|
||||||
private List<ManagementCockpitFileOption> _files = [];
|
private List<ManagementCockpitFileOption> _files = [];
|
||||||
|
private List<int> _centralYears = [];
|
||||||
private string? _selectedFilePath;
|
private string? _selectedFilePath;
|
||||||
private ManagementCockpitResult? _result;
|
private ManagementCockpitResult? _result;
|
||||||
|
private ManagementCockpitCentralResult? _centralResult;
|
||||||
|
private int _selectedCentralYear;
|
||||||
|
private int? _selectedCentralMonth;
|
||||||
private bool _loadingFiles;
|
private bool _loadingFiles;
|
||||||
private bool _analyzing;
|
private bool _analyzing;
|
||||||
|
private bool _analyzingCentral;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
await ReloadFiles();
|
await ReloadFiles();
|
||||||
|
await ReloadCentralYears();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReloadFiles()
|
private async Task ReloadFiles()
|
||||||
@@ -117,6 +279,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ReloadCentralYears()
|
||||||
|
{
|
||||||
|
_centralYears = await CockpitService.GetAvailableCentralYearsAsync();
|
||||||
|
if (_selectedCentralYear == 0)
|
||||||
|
_selectedCentralYear = _centralYears.LastOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Analyze()
|
private async Task Analyze()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_selectedFilePath))
|
if (string.IsNullOrWhiteSpace(_selectedFilePath))
|
||||||
@@ -137,10 +306,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
private static Severity MapSeverity(string severity) => severity switch
|
||||||
{
|
{
|
||||||
"Warning" => Severity.Warning,
|
"Warning" => Severity.Warning,
|
||||||
"Error" => Severity.Error,
|
"Error" => Severity.Error,
|
||||||
_ => Severity.Info
|
_ => 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}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -497,12 +497,34 @@
|
|||||||
|
|
||||||
if (result != true) return;
|
if (result != true) return;
|
||||||
|
|
||||||
using var db = await DbFactory.CreateDbContextAsync();
|
try
|
||||||
var entity = await db.HanaServers.FindAsync(server.Id);
|
|
||||||
if (entity is not null)
|
|
||||||
{
|
{
|
||||||
db.HanaServers.Remove(entity);
|
using var db = await DbFactory.CreateDbContextAsync();
|
||||||
await db.SaveChangesAsync();
|
var linkedSites = await db.Sites
|
||||||
|
.Where(s => s.HanaServerId == server.Id)
|
||||||
|
.OrderBy(s => s.Land)
|
||||||
|
.Select(s => $"{s.Land} ({s.TSC})")
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (linkedSites.Count > 0)
|
||||||
|
{
|
||||||
|
Snackbar.Add(
|
||||||
|
$"Server kann nicht gelöscht werden. Noch verknüpfte Standorte: {string.Join(", ", linkedSites)}",
|
||||||
|
Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entity = await db.HanaServers.FindAsync(server.Id);
|
||||||
|
if (entity is not null)
|
||||||
|
{
|
||||||
|
db.HanaServers.Remove(entity);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"Server konnte nicht gelöscht werden: {ex.Message}", Severity.Error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await LoadDataAsync();
|
await LoadDataAsync();
|
||||||
|
|||||||
@@ -48,3 +48,52 @@ public class ManagementCockpitResult
|
|||||||
public List<ManagementCockpitTopItem> TopSalesEmployees { get; set; } = [];
|
public List<ManagementCockpitTopItem> TopSalesEmployees { get; set; } = [];
|
||||||
public Dictionary<string, int> DataQualityCounts { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
public Dictionary<string, int> DataQualityCounts { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ManagementCockpitCentralFilter
|
||||||
|
{
|
||||||
|
public int Year { get; set; }
|
||||||
|
public int? Month { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ManagementCockpitCentralSummary
|
||||||
|
{
|
||||||
|
public int RowCount { get; set; }
|
||||||
|
public int InvoiceCount { get; set; }
|
||||||
|
public int SiteCount { get; set; }
|
||||||
|
public int CountryCount { get; set; }
|
||||||
|
public int CurrencyCount { get; set; }
|
||||||
|
public DateTime? PeriodStart { get; set; }
|
||||||
|
public DateTime? PeriodEnd { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ManagementCockpitTimeValueRow
|
||||||
|
{
|
||||||
|
public string Label { get; set; } = string.Empty;
|
||||||
|
public int? Year { get; set; }
|
||||||
|
public int? Month { get; set; }
|
||||||
|
public int? Day { get; set; }
|
||||||
|
public string Currency { get; set; } = string.Empty;
|
||||||
|
public decimal SalesValue { get; set; }
|
||||||
|
public int RowCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ManagementCockpitDimensionValueRow
|
||||||
|
{
|
||||||
|
public string Label { get; set; } = string.Empty;
|
||||||
|
public string Currency { get; set; } = string.Empty;
|
||||||
|
public decimal SalesValue { get; set; }
|
||||||
|
public int RowCount { get; set; }
|
||||||
|
public int InvoiceCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ManagementCockpitCentralResult
|
||||||
|
{
|
||||||
|
public ManagementCockpitCentralFilter Filter { get; set; } = new();
|
||||||
|
public ManagementCockpitCentralSummary Summary { get; set; } = new();
|
||||||
|
public List<string> Notices { get; set; } = [];
|
||||||
|
public List<ManagementCockpitTimeValueRow> YearlyTotals { get; set; } = [];
|
||||||
|
public List<ManagementCockpitTimeValueRow> MonthlyTotals { get; set; } = [];
|
||||||
|
public List<ManagementCockpitTimeValueRow> DailyTotals { get; set; } = [];
|
||||||
|
public List<ManagementCockpitDimensionValueRow> SourceSystemTotals { get; set; } = [];
|
||||||
|
public List<ManagementCockpitDimensionValueRow> CountryTotals { get; set; } = [];
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ public interface IManagementCockpitService
|
|||||||
{
|
{
|
||||||
Task<List<ManagementCockpitFileOption>> GetAvailableFilesAsync();
|
Task<List<ManagementCockpitFileOption>> GetAvailableFilesAsync();
|
||||||
Task<ManagementCockpitResult> AnalyzeAsync(string filePath);
|
Task<ManagementCockpitResult> AnalyzeAsync(string filePath);
|
||||||
|
Task<List<int>> GetAvailableCentralYearsAsync();
|
||||||
|
Task<ManagementCockpitCentralResult> AnalyzeCentralAsync(int year, int? month);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,152 @@ public class ManagementCockpitService : IManagementCockpitService
|
|||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<int>> GetAvailableCentralYearsAsync()
|
||||||
|
{
|
||||||
|
using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
var years = await db.CentralSalesRecords
|
||||||
|
.Select(r => r.InvoiceDate.HasValue ? r.InvoiceDate.Value.Year : r.ExtractionDate.Year)
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(x => x)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return years;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ManagementCockpitCentralResult> AnalyzeCentralAsync(int year, int? month)
|
||||||
|
{
|
||||||
|
using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
var baseRows = await db.CentralSalesRecords
|
||||||
|
.Select(r => new CentralCockpitRow
|
||||||
|
{
|
||||||
|
SourceSystem = r.SourceSystem,
|
||||||
|
Land = r.Land,
|
||||||
|
Tsc = r.Tsc,
|
||||||
|
InvoiceNumber = r.InvoiceNumber,
|
||||||
|
SalesCurrency = string.IsNullOrWhiteSpace(r.SalesCurrency) ? "-" : r.SalesCurrency,
|
||||||
|
SalesValue = r.SalesPriceValue,
|
||||||
|
PeriodDate = r.InvoiceDate ?? r.ExtractionDate
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (baseRows.Count == 0)
|
||||||
|
throw new InvalidOperationException("Die zentrale Tabelle enthält noch keine Datensätze.");
|
||||||
|
|
||||||
|
var selectedRows = baseRows
|
||||||
|
.Where(r => r.PeriodDate.Year == year && (!month.HasValue || r.PeriodDate.Month == month.Value))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (selectedRows.Count == 0)
|
||||||
|
throw new InvalidOperationException("Für den gewählten Zeitraum gibt es keine Datensätze in der zentralen Tabelle.");
|
||||||
|
|
||||||
|
var yearlyRows = baseRows
|
||||||
|
.Where(r => r.PeriodDate.Year == 2025 || r.PeriodDate.Year == 2026)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var dailyBaseRows = selectedRows
|
||||||
|
.Where(r => month.HasValue)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return new ManagementCockpitCentralResult
|
||||||
|
{
|
||||||
|
Filter = new ManagementCockpitCentralFilter
|
||||||
|
{
|
||||||
|
Year = year,
|
||||||
|
Month = month
|
||||||
|
},
|
||||||
|
Summary = new ManagementCockpitCentralSummary
|
||||||
|
{
|
||||||
|
RowCount = selectedRows.Count,
|
||||||
|
InvoiceCount = selectedRows.Select(x => x.InvoiceNumber).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).Count(),
|
||||||
|
SiteCount = selectedRows.Select(x => x.Tsc).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).Count(),
|
||||||
|
CountryCount = selectedRows.Select(x => x.Land).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).Count(),
|
||||||
|
CurrencyCount = selectedRows.Select(x => x.SalesCurrency).Distinct(StringComparer.OrdinalIgnoreCase).Count(),
|
||||||
|
PeriodStart = selectedRows.Min(x => x.PeriodDate),
|
||||||
|
PeriodEnd = selectedRows.Max(x => x.PeriodDate)
|
||||||
|
},
|
||||||
|
Notices =
|
||||||
|
[
|
||||||
|
"Roh-Auswertung aus CentralSalesRecords.",
|
||||||
|
"Keine Intercompany-Bereinigung angewendet.",
|
||||||
|
"Keine CHF-Umrechnung angewendet. Umsatz bleibt in Sales Currency.",
|
||||||
|
"Kein Budget- und kein Spartemapping angewendet.",
|
||||||
|
"Periodenlogik basiert auf Invoice Date, falls vorhanden, sonst auf Extraction Date."
|
||||||
|
],
|
||||||
|
YearlyTotals = yearlyRows
|
||||||
|
.GroupBy(x => new { x.PeriodDate.Year, x.SalesCurrency })
|
||||||
|
.OrderBy(g => g.Key.Year)
|
||||||
|
.ThenBy(g => g.Key.SalesCurrency, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(g => new ManagementCockpitTimeValueRow
|
||||||
|
{
|
||||||
|
Label = g.Key.Year.ToString(),
|
||||||
|
Year = g.Key.Year,
|
||||||
|
Currency = g.Key.SalesCurrency,
|
||||||
|
SalesValue = g.Sum(x => x.SalesValue),
|
||||||
|
RowCount = g.Count()
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
MonthlyTotals = selectedRows
|
||||||
|
.GroupBy(x => new { x.PeriodDate.Year, x.PeriodDate.Month, x.SalesCurrency })
|
||||||
|
.OrderBy(g => g.Key.Year)
|
||||||
|
.ThenBy(g => g.Key.Month)
|
||||||
|
.ThenBy(g => g.Key.SalesCurrency, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(g => new ManagementCockpitTimeValueRow
|
||||||
|
{
|
||||||
|
Label = $"{g.Key.Year:D4}-{g.Key.Month:D2}",
|
||||||
|
Year = g.Key.Year,
|
||||||
|
Month = g.Key.Month,
|
||||||
|
Currency = g.Key.SalesCurrency,
|
||||||
|
SalesValue = g.Sum(x => x.SalesValue),
|
||||||
|
RowCount = g.Count()
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
DailyTotals = dailyBaseRows
|
||||||
|
.GroupBy(x => new { x.PeriodDate.Year, x.PeriodDate.Month, x.PeriodDate.Day, x.SalesCurrency })
|
||||||
|
.OrderBy(g => g.Key.Year)
|
||||||
|
.ThenBy(g => g.Key.Month)
|
||||||
|
.ThenBy(g => g.Key.Day)
|
||||||
|
.ThenBy(g => g.Key.SalesCurrency, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(g => new ManagementCockpitTimeValueRow
|
||||||
|
{
|
||||||
|
Label = $"{g.Key.Year:D4}-{g.Key.Month:D2}-{g.Key.Day:D2}",
|
||||||
|
Year = g.Key.Year,
|
||||||
|
Month = g.Key.Month,
|
||||||
|
Day = g.Key.Day,
|
||||||
|
Currency = g.Key.SalesCurrency,
|
||||||
|
SalesValue = g.Sum(x => x.SalesValue),
|
||||||
|
RowCount = g.Count()
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
SourceSystemTotals = selectedRows
|
||||||
|
.GroupBy(x => new { x.SourceSystem, x.SalesCurrency })
|
||||||
|
.OrderBy(g => g.Key.SourceSystem, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ThenBy(g => g.Key.SalesCurrency, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(g => new ManagementCockpitDimensionValueRow
|
||||||
|
{
|
||||||
|
Label = g.Key.SourceSystem,
|
||||||
|
Currency = g.Key.SalesCurrency,
|
||||||
|
SalesValue = g.Sum(x => x.SalesValue),
|
||||||
|
RowCount = g.Count(),
|
||||||
|
InvoiceCount = g.Select(x => x.InvoiceNumber).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).Count()
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
CountryTotals = selectedRows
|
||||||
|
.GroupBy(x => new { x.Land, x.SalesCurrency })
|
||||||
|
.OrderByDescending(g => g.Sum(x => x.SalesValue))
|
||||||
|
.ThenBy(g => g.Key.Land, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ThenBy(g => g.Key.SalesCurrency, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(g => new ManagementCockpitDimensionValueRow
|
||||||
|
{
|
||||||
|
Label = g.Key.Land,
|
||||||
|
Currency = g.Key.SalesCurrency,
|
||||||
|
SalesValue = g.Sum(x => x.SalesValue),
|
||||||
|
RowCount = g.Count(),
|
||||||
|
InvoiceCount = g.Select(x => x.InvoiceNumber).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).Count()
|
||||||
|
})
|
||||||
|
.ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable<string> GetCandidateDirectories(ExportSettings settings)
|
private static IEnumerable<string> GetCandidateDirectories(ExportSettings settings)
|
||||||
{
|
{
|
||||||
yield return Path.Combine(AppContext.BaseDirectory, "output");
|
yield return Path.Combine(AppContext.BaseDirectory, "output");
|
||||||
@@ -384,4 +530,15 @@ public class ManagementCockpitService : IManagementCockpitService
|
|||||||
public decimal EstimatedCostTotal { get; set; }
|
public decimal EstimatedCostTotal { get; set; }
|
||||||
public decimal EstimatedMarginTotal { get; set; }
|
public decimal EstimatedMarginTotal { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CentralCockpitRow
|
||||||
|
{
|
||||||
|
public string SourceSystem { get; set; } = string.Empty;
|
||||||
|
public string Land { get; set; } = string.Empty;
|
||||||
|
public string Tsc { get; set; } = string.Empty;
|
||||||
|
public string InvoiceNumber { get; set; } = string.Empty;
|
||||||
|
public string SalesCurrency { get; set; } = string.Empty;
|
||||||
|
public decimal SalesValue { get; set; }
|
||||||
|
public DateTime PeriodDate { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user