420 lines
22 KiB
Plaintext
420 lines
22 KiB
Plaintext
@page "/einkauf"
|
|
@using System.Globalization
|
|
@using TrafagSalesExporter.Models
|
|
@inject TrafagSalesExporter.Services.IUiTextService UiText
|
|
@inject IJSRuntime JsRuntime
|
|
|
|
<PageTitle>@T("Einkauf", "Purchasing")</PageTitle>
|
|
|
|
<MudText Typo="Typo.h4" Class="mb-1">@T("Einkauf", "Purchasing")</MudText>
|
|
<MudText Typo="Typo.body2" Class="mb-4 purchasing-muted">
|
|
@T("SAP-Einkaufsdashboard nach Power-BI-Vorlage x.pbix, vorbereitet fuer Spend, offene Bestellungen, Kontrakte und Lieferantenperformance.",
|
|
"SAP purchasing dashboard based on the Power BI template x.pbix, prepared for spend, open orders, contracts and supplier performance.")
|
|
</MudText>
|
|
|
|
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Class="mb-4">
|
|
@T("Die PBIX-Vorlage liefert Struktur und Kennzahlenlogik. Live-Werte werden sichtbar, sobald die SAP-Einkaufsdatenquelle angebunden ist.",
|
|
"The PBIX template provides structure and KPI logic. Live values will appear once the SAP purchasing data source is connected.")
|
|
</MudAlert>
|
|
|
|
<MudGrid Class="mb-4" Spacing="2">
|
|
@foreach (var card in KpiCards)
|
|
{
|
|
<MudItem xs="12" sm="6" lg="3">
|
|
<MudPaper Class="purchasing-kpi pa-3" Outlined="true">
|
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
|
<MudIcon Icon="@card.Icon" Color="@card.Color" Size="Size.Large" />
|
|
<div>
|
|
<MudText Typo="Typo.caption" Class="purchasing-muted">@T(card.TitleDe, card.TitleEn)</MudText>
|
|
<MudText Typo="Typo.h6">@card.Value</MudText>
|
|
<MudText Typo="Typo.caption">@T(card.DetailDe, card.DetailEn)</MudText>
|
|
</div>
|
|
</MudStack>
|
|
</MudPaper>
|
|
</MudItem>
|
|
}
|
|
</MudGrid>
|
|
|
|
<MudTabs Elevation="1" Rounded="false" PanelClass="pt-4">
|
|
<MudTabPanel Text="@T("Uebersicht", "Overview")" Icon="@Icons.Material.Filled.Dashboard">
|
|
<MudGrid Spacing="2">
|
|
<MudItem xs="12" lg="7">
|
|
<MudPaper Class="pa-3" Outlined="true">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Analyseachsen", "Analysis axes")</MudText>
|
|
<MudTable Items="@AnalysisAxes" Dense="true" Hover="true">
|
|
<HeaderContent>
|
|
<MudTh>@T("Achse", "Axis")</MudTh>
|
|
<MudTh>@T("PBIX-Feld", "PBIX field")</MudTh>
|
|
<MudTh>@T("Verwendung", "Usage")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@T(context.LabelDe, context.LabelEn)</MudTd>
|
|
<MudTd><code>@context.Field</code></MudTd>
|
|
<MudTd>@T(context.UsageDe, context.UsageEn)</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" lg="5">
|
|
<MudPaper Class="pa-3" Outlined="true">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("SAP-Quellen aus PBIX", "SAP sources from PBIX")</MudText>
|
|
@foreach (var source in SapSources)
|
|
{
|
|
<div class="purchasing-source-row">
|
|
<MudIcon Icon="@Icons.Material.Filled.TableChart" Size="Size.Small" />
|
|
<span><strong>@source.Name</strong> @source.Description</span>
|
|
</div>
|
|
}
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudTabPanel>
|
|
|
|
<MudTabPanel Text="@T("Spend", "Spend")" Icon="@Icons.Material.Filled.Payments">
|
|
<PurchasingSection TitleDe="Spend total vergangen"
|
|
TitleEn="Historic total spend"
|
|
DescriptionDe="Beschaffungsvolumen in CHF nach Jahr, Lieferant, Warengruppe und Artikel."
|
|
DescriptionEn="Purchasing volume in CHF by year, supplier, material group and article."
|
|
Rows="@SpendRows" />
|
|
</MudTabPanel>
|
|
|
|
<MudTabPanel Text="@T("Offene Bestellungen", "Open orders")" Icon="@Icons.Material.Filled.PendingActions">
|
|
<PurchasingSection TitleDe="Offene Bestellwerte und Mengen"
|
|
TitleEn="Open order values and quantities"
|
|
DescriptionDe="Vorbereitet fuer offene Werte/Mengen pro Lieferant, Warengruppe und Artikel."
|
|
DescriptionEn="Prepared for open values/quantities by supplier, material group and article."
|
|
Rows="@OpenOrderRows" />
|
|
</MudTabPanel>
|
|
|
|
<MudTabPanel Text="@T("Kontrakte", "Contracts")" Icon="@Icons.Material.Filled.Assignment">
|
|
<PurchasingSection TitleDe="Offene Verpflichtungen"
|
|
TitleEn="Open commitments"
|
|
DescriptionDe="Vorbereitet fuer Mengenkontrakte und Restverpflichtungen pro Lieferant, Warengruppe und Artikel."
|
|
DescriptionEn="Prepared for quantity contracts and remaining commitments by supplier, material group and article."
|
|
Rows="@ContractRows" />
|
|
</MudTabPanel>
|
|
|
|
<MudTabPanel Text="@T("Lieferanten", "Suppliers")" Icon="@Icons.Material.Filled.Verified">
|
|
<PurchasingSection TitleDe="Lieferantenbewertung und Performance"
|
|
TitleEn="Supplier rating and performance"
|
|
DescriptionDe="Vorbereitet fuer Liefertermintreue, Qualitaet, Preisentwicklung und Reklamationen."
|
|
DescriptionEn="Prepared for delivery reliability, quality, price development and claims."
|
|
Rows="@SupplierPerformanceRows" />
|
|
</MudTabPanel>
|
|
|
|
<MudTabPanel Text="@T("PBIX Vorlage", "PBIX template")" Icon="@Icons.Material.Filled.InsertChart">
|
|
<MudPaper Class="pa-3" Outlined="true">
|
|
<MudText Typo="Typo.h6" Class="mb-2">@T("Aus x.pbix uebernommene Seiten", "Pages derived from x.pbix")</MudText>
|
|
<MudTable Items="@PowerBiPages" Dense="true" Hover="true">
|
|
<HeaderContent>
|
|
<MudTh>@T("Power-BI-Seite", "Power BI page")</MudTh>
|
|
<MudTh>@T("Visuals", "Visuals")</MudTh>
|
|
<MudTh>@T("Kennzahl", "Measure")</MudTh>
|
|
<MudTh>@T("Dimensionen", "Dimensions")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd>@context.Page</MudTd>
|
|
<MudTd>@context.Visuals</MudTd>
|
|
<MudTd><code>@context.Measure</code></MudTd>
|
|
<MudTd>@context.Dimensions</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
|
|
<MudTabPanel Text="@T("3D Simulation", "3D simulation")" Icon="@Icons.Material.Filled.ViewInAr">
|
|
<MudPaper Class="pa-3 mb-3" Outlined="true">
|
|
<MudGrid Spacing="2">
|
|
<MudItem xs="12" md="3">
|
|
<MudSelect T="string" Value="_purchasing3dIndicator" ValueChanged="SetPurchasing3dIndicator" Label="@T("Indikator", "Indicator")" Dense="true">
|
|
@foreach (var option in Purchasing3dIndicators)
|
|
{
|
|
<MudSelectItem Value="@option.Key">@T(option.TitleDe, option.TitleEn)</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudSelect T="string" Value="_purchasing3dChartType" ValueChanged="SetPurchasing3dChartType" Label="@T("Grafik", "Chart")" Dense="true">
|
|
<MudSelectItem Value="@("bar")">@T("Balken", "Bars")</MudSelectItem>
|
|
<MudSelectItem Value="@("line")">@T("Linie", "Line")</MudSelectItem>
|
|
<MudSelectItem Value="@("surface")">@T("Flaeche", "Surface")</MudSelectItem>
|
|
<MudSelectItem Value="@("pie")">@T("Kreis", "Pie")</MudSelectItem>
|
|
</MudSelect>
|
|
</MudItem>
|
|
<MudItem xs="12" md="3">
|
|
<MudText Typo="Typo.caption">@T("Preis-/Wechselkurs-Szenario", "Price/exchange-rate scenario")</MudText>
|
|
<div class="finance-3d-range-row">
|
|
<MudButton Variant="Variant.Outlined" Size="Size.Small" OnClick="@(() => SetPurchasing3dFactorPreset(0.9d))">-10%</MudButton>
|
|
<input class="finance-3d-range"
|
|
type="range"
|
|
min="0.5"
|
|
max="1.5"
|
|
step="0.01"
|
|
value="@_purchasing3dFactor.ToString("0.00", CultureInfo.InvariantCulture)"
|
|
@oninput="SetPurchasing3dFactor" />
|
|
<MudText Typo="Typo.body2" Class="finance-3d-factor">@_purchasing3dFactor.ToString("0.00", CultureInfo.InvariantCulture)x</MudText>
|
|
</div>
|
|
<MudText Typo="Typo.caption">
|
|
@T("Delta", "Delta"): @FormatScenarioDelta()
|
|
</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudText Typo="Typo.caption">@T("Beschriftung", "Labels")</MudText>
|
|
<div class="finance-3d-range-row">
|
|
<input class="finance-3d-range"
|
|
type="range"
|
|
min="0.8"
|
|
max="2.5"
|
|
step="0.1"
|
|
value="@_purchasing3dLabelScale.ToString("0.0", CultureInfo.InvariantCulture)"
|
|
@oninput="SetPurchasing3dLabelScale" />
|
|
<MudText Typo="Typo.body2" Class="finance-3d-factor">@_purchasing3dLabelScale.ToString("0.0", CultureInfo.InvariantCulture)x</MudText>
|
|
</div>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Info" StartIcon="@Icons.Material.Filled.Refresh" OnClick="RenderPurchasing3dAsync">
|
|
@T("Neu zeichnen", "Redraw")
|
|
</MudButton>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
<MudPaper Class="pa-0 finance-3d-surface purchasing-3d-surface" Elevation="1">
|
|
<canvas @ref="_purchasing3dCanvas" class="finance-3d-canvas" style="display:block;width:100%;height:100%;touch-action:none;"></canvas>
|
|
</MudPaper>
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
|
|
@code {
|
|
private ElementReference _purchasing3dCanvas;
|
|
private string _purchasing3dIndicator = "spend";
|
|
private string _purchasing3dChartType = "bar";
|
|
private double _purchasing3dFactor = 1d;
|
|
private double _purchasing3dLabelScale = 1.5d;
|
|
|
|
private IReadOnlyList<PurchasingKpiCard> KpiCards =>
|
|
[
|
|
new("Spend total", "Total spend", FormatChf(Purchasing3dBaseRows.Sum(x => x.Spend)), "Simulationsbasis bis SAP-Liveimport", "Simulation basis until SAP live import", Icons.Material.Filled.Payments, Color.Primary),
|
|
new("Offene Bestellungen", "Open orders", FormatChf(Purchasing3dBaseRows.Sum(x => x.OpenValue)), "Wert offen aus Simulationsbasis", "Open value from simulation basis", Icons.Material.Filled.PendingActions, Color.Warning),
|
|
new("Kontrakte", "Contracts", FormatChf(Purchasing3dBaseRows.Sum(x => x.ContractValue)), "Restverpflichtungen aus Simulationsbasis", "Remaining commitments from simulation basis", Icons.Material.Filled.Assignment, Color.Info),
|
|
new("Lieferantenperformance", "Supplier performance", $"{Purchasing3dBaseRows.Average(x => x.SupplierScore):N1}%", "Durchschnitt Score aus Simulationsbasis", "Average score from simulation basis", Icons.Material.Filled.Verified, Color.Success)
|
|
];
|
|
|
|
private readonly List<PurchasingAxis> AnalysisAxes =
|
|
[
|
|
new("Jahr", "Year", "EKKOSet.Bedat", "Zeitfilter und Verlauf", "Time filter and trend"),
|
|
new("Lieferant", "Supplier", "Data.Name / Data.Lieferant", "Spend und Performance pro Lieferant", "Spend and performance by supplier"),
|
|
new("Warengruppe", "Material group", "Data (2).Warengruppe / WG komplett", "Spend nach Warengruppe", "Spend by material group"),
|
|
new("Artikel", "Article", "EKPOSet.Matnr / EKPOSet.Txz01", "Artikel- und Preisentwicklung", "Article and price development"),
|
|
new("Region", "Region", "Data.Laender-/Regionenschluessel", "Regionale Spend-Verteilung", "Regional spend distribution")
|
|
];
|
|
|
|
private readonly List<PurchasingSource> SapSources =
|
|
[
|
|
new("EKKOSet", "Bestellkopf: Datum, Lieferant, Einkaufsbeleg."),
|
|
new("EKPOSet", "Bestellposition: Artikel, Text, Netwr CHF, Preis pro Stueck."),
|
|
new("eketSet", "Einteilungen/Termine: Basis fuer offene Mengen und Liefertermine."),
|
|
new("Data", "Lieferanten-Mapping und Lieferantennamen."),
|
|
new("Data (2)", "Warengruppen-Mapping und Warengruppentexte.")
|
|
];
|
|
|
|
private readonly List<PurchasingAnalysisRow> SpendRows =
|
|
[
|
|
new("Spend nach Jahr", "Spend by year", "Sum(EKPOSet.Netwr CHF)", "EKKOSet.Bedat Jahr", "PBIX"),
|
|
new("Spend nach Lieferant", "Spend by supplier", "Sum(EKPOSet.Netwr CHF)", "Data.Name", "PBIX"),
|
|
new("Spend nach Warengruppe", "Spend by material group", "Sum(EKPOSet.Netwr CHF)", "Data (2).Warengruppe", "PBIX"),
|
|
new("Spend nach Artikel", "Spend by article", "Sum(EKPOSet.Netwr CHF)", "EKPOSet.Matnr / Txz01", "PBIX")
|
|
];
|
|
|
|
private readonly List<PurchasingAnalysisRow> OpenOrderRows =
|
|
[
|
|
new("Offener Bestellwert", "Open order value", "Bestellwert offen CHF", "Lieferant / Warengruppe / Artikel", "Neu"),
|
|
new("Offene Bestellmenge", "Open order quantity", "Menge offen", "Lieferant / Warengruppe / Artikel", "Neu"),
|
|
new("Faelligkeiten", "Due dates", "Termin / Rueckstand", "Lieferant / Artikel", "Neu")
|
|
];
|
|
|
|
private readonly List<PurchasingAnalysisRow> ContractRows =
|
|
[
|
|
new("Mengenkontrakte", "Quantity contracts", "Kontraktmenge / Abrufmenge", "Lieferant / Warengruppe / Artikel", "Neu"),
|
|
new("Restverpflichtung", "Remaining commitment", "Restwert CHF", "Lieferant / Warengruppe / Artikel", "Neu"),
|
|
new("Kontrakt-Ausnutzung", "Contract consumption", "Abrufquote", "Lieferant / Artikel", "Neu")
|
|
];
|
|
|
|
private readonly List<PurchasingAnalysisRow> SupplierPerformanceRows =
|
|
[
|
|
new("Liefertermintreue", "Delivery reliability", "On-time %", "Lieferant / Artikel", "Neu"),
|
|
new("Preisentwicklung", "Price development", "Netwr CHF/Stk", "Lieferant / Artikel / Jahr", "PBIX"),
|
|
new("Qualitaet / Reklamation", "Quality / claims", "Fehlerquote", "Lieferant / Warengruppe", "Neu"),
|
|
new("Lieferantenbewertung", "Supplier rating", "Score", "Lieferant", "Neu")
|
|
];
|
|
|
|
private readonly List<PowerBiPageInfo> PowerBiPages =
|
|
[
|
|
new("Besch.Volumen CHF/Lieferant", "Pivot, Slicer", "Sum(EKPOSet.Netwr CHF)", "Jahr, Lieferant, Warengruppe, Artikel"),
|
|
new("Eink.Vol. CHF / Lieferant Kuchen", "Pie, Slicer", "Sum(EKPOSet.Netwr CHF)", "Lieferant, Warengruppe, Jahr"),
|
|
new("Balken Vol./Lief/WG", "Column, Slicer", "Sum(EKPOSet.Netwr CHF)", "Lieferant, Warengruppe, Artikel"),
|
|
new("Diagramm Vol./WG", "Column, Slicer", "Sum(EKPOSet.Netwr CHF)", "Warengruppe, Jahr"),
|
|
new("Eink.Vol. CHF / Region", "Pie, Slicer", "Sum(EKPOSet.Netwr CHF)", "Region, Warengruppe, Jahr"),
|
|
new("Preisentwicklung CHF", "Pivot, Slicer", "Min(EKPOSet.Netwr CHF/Stk)", "Lieferant, Artikel, Jahr"),
|
|
new("Matrix Vol./WG", "Pivot, Slicer", "Sum(EKPOSet.Netwr CHF)", "Warengruppe, Lieferant, Artikel")
|
|
];
|
|
|
|
private readonly List<Purchasing3dIndicator> Purchasing3dIndicators =
|
|
[
|
|
new("spend", "Spend CHF", "Spend CHF", "CHF"),
|
|
new("openValue", "Offener Bestellwert", "Open order value", "CHF"),
|
|
new("openQuantity", "Offene Menge", "Open quantity", "Qty"),
|
|
new("contractValue", "Kontrakt-Restwert", "Contract remaining value", "CHF"),
|
|
new("supplierScore", "Lieferantenperformance", "Supplier performance", "%")
|
|
];
|
|
|
|
private readonly List<Purchasing3dBaseRow> Purchasing3dBaseRows =
|
|
[
|
|
new("Lieferant A", 2024, 1450000d, 260000d, 11800d, 420000d, 91d),
|
|
new("Lieferant A", 2025, 1680000d, 310000d, 13200d, 460000d, 93d),
|
|
new("Lieferant A", 2026, 1820000d, 335000d, 14100d, 490000d, 92d),
|
|
new("Lieferant B", 2024, 980000d, 190000d, 9300d, 260000d, 86d),
|
|
new("Lieferant B", 2025, 1120000d, 225000d, 10100d, 315000d, 88d),
|
|
new("Lieferant B", 2026, 1240000d, 250000d, 10800d, 350000d, 89d),
|
|
new("Warengruppe Sensorik", 2024, 760000d, 120000d, 6400d, 210000d, 94d),
|
|
new("Warengruppe Sensorik", 2025, 890000d, 145000d, 7100d, 230000d, 95d),
|
|
new("Warengruppe Sensorik", 2026, 940000d, 155000d, 7400d, 245000d, 94d),
|
|
new("Artikel Top 10", 2024, 520000d, 83000d, 2800d, 160000d, 90d),
|
|
new("Artikel Top 10", 2025, 610000d, 97000d, 3100d, 180000d, 91d),
|
|
new("Artikel Top 10", 2026, 680000d, 105000d, 3350d, 195000d, 92d)
|
|
];
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
await RenderPurchasing3dAsync();
|
|
}
|
|
|
|
private string T(string german, string english) => UiText.Text(german, english);
|
|
private static string FormatChf(double value) => $"CHF {value:N0}";
|
|
|
|
private async Task SetPurchasing3dIndicator(string value)
|
|
{
|
|
_purchasing3dIndicator = value;
|
|
await RenderPurchasing3dAsync();
|
|
}
|
|
|
|
private async Task SetPurchasing3dChartType(string value)
|
|
{
|
|
_purchasing3dChartType = value;
|
|
await RenderPurchasing3dAsync();
|
|
}
|
|
|
|
private async Task SetPurchasing3dFactor(ChangeEventArgs args)
|
|
{
|
|
if (double.TryParse(Convert.ToString(args.Value, CultureInfo.InvariantCulture), NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
|
|
{
|
|
_purchasing3dFactor = Math.Clamp(value, 0.5d, 1.5d);
|
|
await JsRuntime.InvokeVoidAsync("trafagFinance3d.updateFactor", _purchasing3dCanvas, ScenarioAffectsPurchasingValue ? _purchasing3dFactor : 1d);
|
|
}
|
|
}
|
|
|
|
private async Task SetPurchasing3dFactorPreset(double value)
|
|
{
|
|
_purchasing3dFactor = Math.Clamp(value, 0.5d, 1.5d);
|
|
await JsRuntime.InvokeVoidAsync("trafagFinance3d.updateFactor", _purchasing3dCanvas, ScenarioAffectsPurchasingValue ? _purchasing3dFactor : 1d);
|
|
}
|
|
|
|
private async Task SetPurchasing3dLabelScale(ChangeEventArgs args)
|
|
{
|
|
if (double.TryParse(Convert.ToString(args.Value, CultureInfo.InvariantCulture), NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
|
|
{
|
|
_purchasing3dLabelScale = Math.Clamp(value, 0.8d, 2.5d);
|
|
await RenderPurchasing3dAsync();
|
|
}
|
|
}
|
|
|
|
private async Task RenderPurchasing3dAsync()
|
|
{
|
|
await JsRuntime.InvokeVoidAsync("trafagFinance3d.render", _purchasing3dCanvas, BuildPurchasing3dRows(), new
|
|
{
|
|
indicator = _purchasing3dIndicator,
|
|
title = ResolvePurchasing3dIndicatorLabel(),
|
|
chartType = _purchasing3dChartType,
|
|
xAxis = T("X: Lieferant / Warengruppe / Artikel", "X: supplier / material group / article"),
|
|
yAxis = T("Y: Wert / Menge / Score", "Y: value / quantity / score"),
|
|
zAxis = T("Z: Jahr / Zeit", "Z: year / time"),
|
|
pieAxis = T("Kreis: Anteil am aktuellen Indikator", "Pie: share of current indicator"),
|
|
labelScale = _purchasing3dLabelScale,
|
|
scenarioFactor = ScenarioAffectsPurchasingValue ? _purchasing3dFactor : 1d
|
|
});
|
|
}
|
|
|
|
private IReadOnlyList<object> BuildPurchasing3dRows()
|
|
=> Purchasing3dBaseRows
|
|
.Select(row => new
|
|
{
|
|
country = row.Axis,
|
|
year = row.Year,
|
|
value = ResolvePurchasing3dValue(row)
|
|
})
|
|
.Cast<object>()
|
|
.ToList();
|
|
|
|
private double ResolvePurchasing3dValue(Purchasing3dBaseRow row) => _purchasing3dIndicator switch
|
|
{
|
|
"openValue" => row.OpenValue,
|
|
"openQuantity" => row.OpenQuantity,
|
|
"contractValue" => row.ContractValue,
|
|
"supplierScore" => row.SupplierScore,
|
|
_ => row.Spend
|
|
};
|
|
|
|
private bool ScenarioAffectsPurchasingValue => _purchasing3dIndicator is "spend" or "openValue" or "contractValue";
|
|
|
|
private string ResolvePurchasing3dIndicatorLabel()
|
|
=> T(
|
|
Purchasing3dIndicators.FirstOrDefault(x => x.Key == _purchasing3dIndicator)?.TitleDe ?? "Spend CHF",
|
|
Purchasing3dIndicators.FirstOrDefault(x => x.Key == _purchasing3dIndicator)?.TitleEn ?? "Spend CHF");
|
|
|
|
private string FormatScenarioDelta()
|
|
{
|
|
if (!ScenarioAffectsPurchasingValue)
|
|
return T("nicht auf diesen Indikator angewendet", "not applied to this indicator");
|
|
|
|
var baseTotal = Purchasing3dBaseRows.Sum(ResolvePurchasing3dValue);
|
|
var delta = baseTotal * _purchasing3dFactor - baseTotal;
|
|
return $"{delta:N0} {Purchasing3dIndicators.First(x => x.Key == _purchasing3dIndicator).Unit}";
|
|
}
|
|
|
|
private sealed record PurchasingKpiCard(string TitleDe, string TitleEn, string Value, string DetailDe, string DetailEn, string Icon, Color Color);
|
|
private sealed record PurchasingAxis(string LabelDe, string LabelEn, string Field, string UsageDe, string UsageEn);
|
|
private sealed record PurchasingSource(string Name, string Description);
|
|
private sealed record PowerBiPageInfo(string Page, string Visuals, string Measure, string Dimensions);
|
|
private sealed record Purchasing3dIndicator(string Key, string TitleDe, string TitleEn, string Unit);
|
|
private sealed record Purchasing3dBaseRow(string Axis, int Year, double Spend, double OpenValue, double OpenQuantity, double ContractValue, double SupplierScore);
|
|
}
|
|
|
|
<style>
|
|
.purchasing-muted {
|
|
color: var(--mud-palette-text-secondary);
|
|
}
|
|
|
|
.purchasing-kpi {
|
|
min-height: 104px;
|
|
}
|
|
|
|
.purchasing-source-row {
|
|
display: grid;
|
|
grid-template-columns: 26px minmax(0, 1fr);
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 7px 0;
|
|
border-bottom: 1px solid var(--mud-palette-lines-default);
|
|
}
|
|
|
|
.purchasing-source-row:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
.purchasing-3d-surface {
|
|
height: calc(100vh - 300px);
|
|
min-height: 620px;
|
|
overflow: hidden;
|
|
}
|
|
</style>
|