diff --git a/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor b/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor index cd1452f..7abb689 100644 --- a/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor +++ b/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor @@ -1086,10 +1086,21 @@ ]; private readonly List _finance3dIndicatorOptions = [ - new(Finance3dIndicators.Actual, "Net Sales Actual", "Net sales actual"), + new(Finance3dIndicators.Actual, "Ist Umsatz inkl. IC", "Actual sales incl. IC"), + new(Finance3dIndicators.ActualExcludingIntercompany, "Ist Umsatz ohne IC", "Actual sales excl. IC"), + new(Finance3dIndicators.IntercompanyValue, "Intercompany Wert", "Intercompany value"), + new(Finance3dIndicators.IntercompanyShare, "Intercompany Anteil %", "Intercompany share %"), + new(Finance3dIndicators.Quantity, "Menge", "Quantity"), + new(Finance3dIndicators.CreditValue, "Gutschriften Wert", "Credit-note value"), + new(Finance3dIndicators.CreditRows, "Gutschriften Zeilen", "Credit-note rows"), + new(Finance3dIndicators.TotalRows, "Alle Zeilen", "All rows"), 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") + new(Finance3dIndicators.IncludeRate, "Include Quote %", "Include rate %"), + new(Finance3dIndicators.ExcludeRate, "Exclude Quote %", "Exclude rate %"), + new(Finance3dIndicators.ReferenceValue, "Sollwert Filterjahr", "Reference value filter year"), + new(Finance3dIndicators.Deviation, "Soll/Ist Differenz Filterjahr", "Actual/reference difference filter year"), + new(Finance3dIndicators.DeviationPercent, "Soll/Ist Abweichung % Filterjahr", "Actual/reference deviation % filter year") ]; private readonly List _finance3dChartTypeOptions = [ @@ -1130,7 +1141,13 @@ private bool _finance3dNeedsRender; private bool ShowProductFamilyColumn => _productFinanceGroupLevel != ProductFinanceGroupLevels.Division; - private bool Finance3dScenarioAffectsValue => _finance3dIndicator is Finance3dIndicators.Actual or Finance3dIndicators.Deviation; + private bool Finance3dScenarioAffectsValue => _finance3dIndicator is + Finance3dIndicators.Actual or + Finance3dIndicators.ActualExcludingIntercompany or + Finance3dIndicators.IntercompanyValue or + Finance3dIndicators.CreditValue or + Finance3dIndicators.ReferenceValue or + Finance3dIndicators.Deviation; private decimal Finance3dBaseTotal => CalculateFinance3dBaseTotal(); private decimal Finance3dScenarioTotal => Finance3dScenarioAffectsValue ? Finance3dBaseTotal * (decimal)_finance3dScenarioFactor @@ -1366,14 +1383,14 @@ if (_financeResult is null) return []; - var deviationsByKey = _financeResult.CountryRows - .Where(row => row.Difference.HasValue) + var countryRowsByKey = _financeResult.CountryRows .GroupBy(row => $"{row.Year}|{row.CountryKey}", StringComparer.OrdinalIgnoreCase) .ToDictionary( group => group.Key, - group => group.Sum(row => Math.Abs(row.Difference!.Value))); + group => group.ToList(), + StringComparer.OrdinalIgnoreCase); - var sourceRows = _finance3dIndicator == Finance3dIndicators.Deviation + var sourceRows = IsFinance3dReferenceYearIndicator(_finance3dIndicator) ? _financeResult.Rows : (_financeResult.YearCountryRows.Count > 0 ? _financeResult.YearCountryRows : _financeResult.Rows); @@ -1382,14 +1399,8 @@ .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) - }; + countryRowsByKey.TryGetValue($"{row.Year}|{row.CountryKey}", out var countryRows); + var value = ResolveFinance3dRowValue(row, countryRows); return new { country = row.CountryKey, @@ -1407,23 +1418,67 @@ if (_financeResult is null) return 0m; - var sourceRows = _finance3dIndicator == Finance3dIndicators.Deviation + var countryRowsByKey = _financeResult.CountryRows + .GroupBy(row => $"{row.Year}|{row.CountryKey}", StringComparer.OrdinalIgnoreCase) + .ToDictionary( + group => group.Key, + group => group.ToList(), + StringComparer.OrdinalIgnoreCase); + + var sourceRows = IsFinance3dReferenceYearIndicator(_finance3dIndicator) ? _financeResult.Rows : (_financeResult.YearCountryRows.Count > 0 ? _financeResult.YearCountryRows : _financeResult.Rows); - if (_finance3dIndicator == Finance3dIndicators.Deviation) + var values = sourceRows + .Select(row => + { + countryRowsByKey.TryGetValue($"{row.Year}|{row.CountryKey}", out var countryRows); + return ResolveFinance3dRowValue(row, countryRows); + }) + .ToList(); + + if (IsFinance3dPercentIndicator(_finance3dIndicator)) { - return _financeResult.CountryRows - .Where(row => row.Difference.HasValue) - .Sum(row => Math.Abs(row.Difference!.Value)); + var nonZeroValues = values.Where(value => value != 0m).ToList(); + return nonZeroValues.Count == 0 ? 0m : nonZeroValues.Average(); } - return _finance3dIndicator switch + return values.Sum(); + } + + private decimal ResolveFinance3dRowValue(ManagementFinanceSummaryRow row, IReadOnlyCollection? countryRows) + => _finance3dIndicator switch { - Finance3dIndicators.IncludedRows => sourceRows.Sum(row => row.IncludedRows), - Finance3dIndicators.ExcludedRows => sourceRows.Sum(row => row.ExcludedRows), - _ => sourceRows.Sum(row => Math.Abs(row.NetSalesActual)) + Finance3dIndicators.ActualExcludingIntercompany => Math.Abs(row.NetSalesActualExcludingIntercompany), + Finance3dIndicators.IntercompanyValue => Math.Abs(row.IntercompanyValue), + Finance3dIndicators.IntercompanyShare => Math.Abs(row.IntercompanySharePercent), + Finance3dIndicators.Quantity => Math.Abs(row.Quantity), + Finance3dIndicators.CreditValue => Math.Abs(row.CreditValue), + Finance3dIndicators.CreditRows => row.CreditRows, + Finance3dIndicators.TotalRows => row.TotalRows, + Finance3dIndicators.IncludedRows => row.IncludedRows, + Finance3dIndicators.ExcludedRows => row.ExcludedRows, + Finance3dIndicators.IncludeRate => row.IncludeRatePercent, + Finance3dIndicators.ExcludeRate => row.ExcludeRatePercent, + Finance3dIndicators.ReferenceValue => Math.Abs(countryRows?.Sum(item => item.ReferenceValue ?? 0m) ?? 0m), + Finance3dIndicators.Deviation => Math.Abs(countryRows?.Where(item => item.Difference.HasValue).Sum(item => item.Difference!.Value) ?? 0m), + Finance3dIndicators.DeviationPercent => Math.Abs(AverageNullablePercent(countryRows?.Select(item => item.DifferencePercent))), + _ => Math.Abs(row.NetSalesActual) }; + + private static bool IsFinance3dReferenceYearIndicator(string indicator) + => indicator is Finance3dIndicators.ReferenceValue or Finance3dIndicators.Deviation or Finance3dIndicators.DeviationPercent; + + private static bool IsFinance3dPercentIndicator(string indicator) + => indicator is Finance3dIndicators.IntercompanyShare or Finance3dIndicators.IncludeRate or Finance3dIndicators.ExcludeRate or Finance3dIndicators.DeviationPercent; + + private static decimal AverageNullablePercent(IEnumerable? values) + { + if (values is null) + return 0m; + + var actualValues = values.Where(value => value.HasValue).Select(value => value!.Value).ToList(); + return actualValues.Count == 0 ? 0m : actualValues.Average(); } private string FormatFinance3dValue(decimal value) @@ -1740,9 +1795,20 @@ private static class Finance3dIndicators { public const string Actual = "actual"; + public const string ActualExcludingIntercompany = "actualExcludingIntercompany"; + public const string IntercompanyValue = "intercompanyValue"; + public const string IntercompanyShare = "intercompanyShare"; + public const string Quantity = "quantity"; + public const string CreditValue = "creditValue"; + public const string CreditRows = "creditRows"; + public const string TotalRows = "totalRows"; public const string IncludedRows = "includedRows"; public const string ExcludedRows = "excludedRows"; + public const string IncludeRate = "includeRate"; + public const string ExcludeRate = "excludeRate"; + public const string ReferenceValue = "referenceValue"; public const string Deviation = "deviation"; + public const string DeviationPercent = "deviationPercent"; } private static class Finance3dChartTypes diff --git a/TrafagSalesExporter/Models/ManagementCockpitModels.cs b/TrafagSalesExporter/Models/ManagementCockpitModels.cs index cffb7ad..305e655 100644 --- a/TrafagSalesExporter/Models/ManagementCockpitModels.cs +++ b/TrafagSalesExporter/Models/ManagementCockpitModels.cs @@ -171,15 +171,22 @@ public class ManagementFinanceSummaryRow public string Currency { get; set; } = string.Empty; public int IncludedRows { get; set; } public int ExcludedRows { get; set; } + public int TotalRows { get; set; } public decimal NetSalesActual { get; set; } + public decimal NetSalesActualExcludingIntercompany { get; set; } + public decimal IntercompanyValue { get; set; } + public decimal IntercompanySharePercent { get; set; } + public decimal Quantity { get; set; } + public decimal CreditValue { get; set; } + public int CreditRows { get; set; } + public decimal IncludeRatePercent { get; set; } + public decimal ExcludeRatePercent { get; set; } } public class ManagementFinanceCountryStatusRow : ManagementFinanceSummaryRow { public string SourceSystems { get; set; } = string.Empty; public string Tscs { get; set; } = string.Empty; - public decimal IntercompanyValue { get; set; } - public decimal NetSalesActualExcludingIntercompany { get; set; } public decimal? ReferenceValue { get; set; } public decimal? Difference { get; set; } public decimal? DifferencePercent { get; set; } diff --git a/TrafagSalesExporter/Services/ManagementCockpitService.cs b/TrafagSalesExporter/Services/ManagementCockpitService.cs index c1cb337..b502ef9 100644 --- a/TrafagSalesExporter/Services/ManagementCockpitService.cs +++ b/TrafagSalesExporter/Services/ManagementCockpitService.cs @@ -1045,14 +1045,31 @@ public class ManagementCockpitService : IManagementCockpitService IEnumerable rows) { var rowList = rows.ToList(); + var includedRows = rowList.Count(row => row.Include); + var excludedRows = rowList.Count(row => !row.Include); + var actual = rowList.Sum(row => row.Value); + var intercompanyValue = rowList.Where(row => row.IsIntercompany).Sum(row => row.Value); + var creditRows = rowList.Count(row => row.Value < 0m || row.RawSalesValue < 0m || LooksLikeCreditDocument(row.DocumentType, row.InvoiceNumber)); + var creditValue = rowList + .Where(row => row.Value < 0m || row.RawSalesValue < 0m || LooksLikeCreditDocument(row.DocumentType, row.InvoiceNumber)) + .Sum(row => Math.Abs(row.Value)); return new ManagementFinanceSummaryRow { Year = year, CountryKey = countryKey, Currency = currency, - IncludedRows = rowList.Count(row => row.Include), - ExcludedRows = rowList.Count(row => !row.Include), - NetSalesActual = rowList.Sum(row => row.Value) + IncludedRows = includedRows, + ExcludedRows = excludedRows, + TotalRows = rowList.Count, + NetSalesActual = actual, + NetSalesActualExcludingIntercompany = actual - intercompanyValue, + IntercompanyValue = intercompanyValue, + IntercompanySharePercent = actual == 0m ? 0m : intercompanyValue / actual * 100m, + Quantity = rowList.Sum(row => row.Quantity), + CreditValue = creditValue, + CreditRows = creditRows, + IncludeRatePercent = rowList.Count == 0 ? 0m : includedRows * 100m / rowList.Count, + ExcludeRatePercent = rowList.Count == 0 ? 0m : excludedRows * 100m / rowList.Count }; } diff --git a/TrafagSalesExporter/wwwroot/css/app.css b/TrafagSalesExporter/wwwroot/css/app.css index 785e81d..726ecb3 100644 --- a/TrafagSalesExporter/wwwroot/css/app.css +++ b/TrafagSalesExporter/wwwroot/css/app.css @@ -42,7 +42,8 @@ body, .finance-3d-surface { width: 100%; - min-height: 560px; + height: calc(100vh - 260px); + min-height: 680px; overflow: hidden; background: #f7f9fb; } @@ -50,7 +51,7 @@ body, .finance-3d-canvas { display: block; width: 100%; - height: 560px; + height: 100%; touch-action: none; } @@ -73,10 +74,7 @@ body, @media (max-width: 700px) { .finance-3d-surface { - min-height: 420px; - } - - .finance-3d-canvas { - height: 420px; + height: calc(100vh - 300px); + min-height: 480px; } }