Expand finance 3D indicators

This commit is contained in:
2026-06-04 14:22:22 +02:00
parent 1049216049
commit e33a2fd9e6
4 changed files with 124 additions and 36 deletions
@@ -1086,10 +1086,21 @@
];
private readonly List<Finance3dIndicatorOption> _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<Finance3dChartTypeOption> _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<ManagementFinanceCountryStatusRow>? 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<decimal?>? 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
@@ -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; }
@@ -1045,14 +1045,31 @@ public class ManagementCockpitService : IManagementCockpitService
IEnumerable<FinanceAggregationRow> 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
};
}
+5 -7
View File
@@ -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;
}
}