Add purchasing ideas tab and SAP table test

This commit is contained in:
2026-06-05 10:45:48 +02:00
parent 989ff66102
commit e7e408fc20
2 changed files with 361 additions and 0 deletions
@@ -185,6 +185,88 @@
DetailRows="@SupplierDetailRows" />
</MudTabPanel>
<MudTabPanel Text="@T("Ideen", "Ideas")" Icon="@Icons.Material.Filled.Lightbulb">
<MudGrid Spacing="2">
<MudItem xs="12" lg="8">
<MudPaper Class="pa-3 purchasing-overview-panel" Outlined="true">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="mb-3">
<div>
<MudText Typo="Typo.h6">@T("Weitere Einkaufsanalysen", "Additional purchasing analytics")</MudText>
<MudText Typo="Typo.body2" Class="purchasing-muted">
@T("Analysen, die dem Einkauf neben PowerBI mehr Steuerung, Risiko- und Sparpotenzial zeigen.",
"Analytics that give purchasing more steering, risk and savings potential beyond Power BI.")
</MudText>
</div>
<MudChip T="string" Color="Color.Info" Variant="Variant.Outlined">
@T("Roadmap", "Roadmap")
</MudChip>
</MudStack>
<div class="purchasing-idea-grid">
@foreach (var idea in PurchasingIdeas)
{
<div class="purchasing-idea-card">
<div class="purchasing-idea-icon">
<MudIcon Icon="@idea.Icon" Color="@idea.Color" />
</div>
<div class="purchasing-idea-content">
<div class="purchasing-idea-title">
<strong>@T(idea.TitleDe, idea.TitleEn)</strong>
<MudChip T="string" Size="Size.Small" Color="@idea.Color" Variant="Variant.Outlined">@T(idea.StatusDe, idea.StatusEn)</MudChip>
</div>
<span>@T(idea.DescriptionDe, idea.DescriptionEn)</span>
<div class="purchasing-idea-meta">
<code>@idea.RequiredData</code>
<span>@T("Nutzen", "Value"): @T(idea.ValueDe, idea.ValueEn)</span>
</div>
</div>
</div>
}
</div>
</MudPaper>
</MudItem>
<MudItem xs="12" lg="4">
<MudPaper Class="pa-3 purchasing-overview-panel" Outlined="true">
<MudText Typo="Typo.h6" Class="mb-2">@T("Prioritaet", "Priority")</MudText>
<div class="purchasing-priority-stack">
@foreach (var priority in PurchasingIdeaPriorities)
{
<div class="purchasing-priority-row">
<MudIcon Icon="@priority.Icon" Color="@priority.Color" Size="Size.Small" />
<div>
<strong>@T(priority.TitleDe, priority.TitleEn)</strong>
<span>@T(priority.DetailDe, priority.DetailEn)</span>
</div>
</div>
}
</div>
</MudPaper>
</MudItem>
<MudItem xs="12">
<MudPaper Class="pa-3 purchasing-overview-panel" Outlined="true">
<MudText Typo="Typo.h6" Class="mb-2">@T("Kennzahlen-Katalog fuer den naechsten Ausbau", "KPI catalogue for the next build-out")</MudText>
<MudTable Items="@PurchasingIdeaKpis" Dense="true" Hover="true" Striped="true">
<HeaderContent>
<MudTh>@T("Analyse", "Analysis")</MudTh>
<MudTh>@T("Kennzahl", "KPI")</MudTh>
<MudTh>@T("Dimension", "Dimension")</MudTh>
<MudTh>@T("Datenbasis", "Data basis")</MudTh>
<MudTh>@T("Status", "Status")</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@T(context.AnalysisDe, context.AnalysisEn)</MudTd>
<MudTd><strong>@T(context.KpiDe, context.KpiEn)</strong></MudTd>
<MudTd>@context.Dimension</MudTd>
<MudTd><code>@context.Source</code></MudTd>
<MudTd>
<MudChip T="string" Size="Size.Small" Color="@context.Color" Variant="Variant.Outlined">@T(context.StatusDe, context.StatusEn)</MudChip>
</MudTd>
</RowTemplate>
</MudTable>
</MudPaper>
</MudItem>
</MudGrid>
</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>
@@ -391,6 +473,34 @@
new("Matrix Vol./WG", "Pivot, Slicer", "Sum(EKPOSet.Netwr CHF)", "Warengruppe, Lieferant, Artikel")
];
private IReadOnlyList<PurchasingIdea> PurchasingIdeas =>
[
new("Lieferantenrisiko", "Supplier risk", "Kombiniert Abhaengigkeit, Single-Source-Anteil, offene Bestellungen und Lieferperformance zu einem Risiko-Score.", "Combines dependency, single-source share, open orders and delivery performance into one risk score.", "EKKO, EKPO, EKET, LFA1", "Engpaesse und Lieferantenabhaengigkeit frueh sehen", "see shortages and supplier dependency early", _liveState.EkpoLoaded && _liveState.EketLoaded ? "berechenbar" : "wartet auf EKPO/EKET", _liveState.EkpoLoaded && _liveState.EketLoaded ? "calculable" : "waiting for EKPO/EKET", Icons.Material.Filled.WarningAmber, _liveState.EkpoLoaded && _liveState.EketLoaded ? Color.Success : Color.Warning),
new("Preisabweichung", "Price variance", "Zeigt Preissteigerungen pro Artikel/Lieferant gegen Vorjahr, Budget oder letzten Einkaufspreis.", "Shows price increases by article/supplier against prior year, budget or last purchase price.", "EKPO, EKKO, FX", "Sparpotenziale und Ausreisser direkt sichtbar", "savings potential and outliers visible immediately", _liveState.EkpoLoaded ? "berechenbar" : "wartet auf EKPO", _liveState.EkpoLoaded ? "calculable" : "waiting for EKPO", Icons.Material.Filled.TrendingUp, _liveState.EkpoLoaded ? Color.Success : Color.Warning),
new("Maverick Buying", "Maverick buying", "Findet Bestellungen ausserhalb bevorzugter Lieferanten, Rahmenvertraege oder Warengruppenregeln.", "Finds orders outside preferred suppliers, contracts or material-group rules.", "EKKO, EKPO, Kontrakte", "Compliance und Buendelung verbessern", "improve compliance and bundling", "Konzept", "concept", Icons.Material.Filled.Policy, Color.Info),
new("Rahmenvertragsnutzung", "Contract utilisation", "Zeigt Kontraktmenge, Abrufmenge, Restmenge, Laufzeit und drohenden Verfall.", "Shows contract quantity, call-off quantity, remaining quantity, term and expiry risk.", "EKKO, EKPO, EKET", "Restverpflichtungen aktiv steuern", "actively manage remaining commitments", _liveState.EketLoaded ? "teilweise" : "wartet auf EKET", _liveState.EketLoaded ? "partial" : "waiting for EKET", Icons.Material.Filled.AssignmentTurnedIn, _liveState.EketLoaded ? Color.Success : Color.Warning),
new("Working Capital", "Working capital", "Verbindet offene Bestellungen, Liefertermine und Zahlungs-/Bestandswirkung zu Cash-Ausblick.", "Connects open orders, delivery dates and payment/inventory impact into a cash outlook.", "EKPO, EKET, FI/AP", "Cashbedarf aus Einkauf vorhersagen", "forecast purchasing cash needs", "Konzept", "concept", Icons.Material.Filled.AccountBalanceWallet, Color.Info),
new("Datenqualitaet", "Data quality", "Prueft fehlende Lieferanten, Warengruppen, Artikeltexte, Waehrung, Preisbasis und Dubletten.", "Checks missing suppliers, material groups, article texts, currency, price basis and duplicates.", "EKKO, EKPO, Mapping", "Vertrauen in Kennzahlen sichern", "secure trust in KPIs", _liveState.EkkoLoaded ? "startklar" : "wartet", _liveState.EkkoLoaded ? "ready" : "waiting", Icons.Material.Filled.FactCheck, _liveState.EkkoLoaded ? Color.Success : Color.Warning)
];
private IReadOnlyList<PurchasingIdeaPriority> PurchasingIdeaPriorities =>
[
new("1. EKPO/EKET Daten reparieren", "1. Repair EKPO/EKET data", "Ohne Positionen fehlen echte Spend-, Artikel-, Warengruppen- und Termindaten.", "Without item rows, real spend, article, material-group and schedule data are missing.", Icons.Material.Filled.BuildCircle, Color.Warning),
new("2. Preisabweichung aktivieren", "2. Activate price variance", "Sehr hoher Managementnutzen, sobald EKPO Werte liefert.", "Very high management value as soon as EKPO provides values.", Icons.Material.Filled.TrendingUp, Color.Info),
new("3. Lieferantenrisiko aufbauen", "3. Build supplier risk", "Kombiniert Performance, offene Werte und Abhaengigkeit.", "Combines performance, open values and dependency.", Icons.Material.Filled.Security, Color.Info),
new("4. Contract Cockpit ausbauen", "4. Extend contract cockpit", "Mengenkontrakte und Restverpflichtungen brauchen EKET/EKPO.", "Quantity contracts and remaining commitments need EKET/EKPO.", Icons.Material.Filled.Assignment, Color.Info)
];
private IReadOnlyList<PurchasingIdeaKpi> PurchasingIdeaKpis =>
[
new("Lieferantenrisiko", "Supplier risk", "Risiko-Score 0-100", "Risk score 0-100", "Lieferant / Warengruppe / Artikel", "EKKO+EKPO+EKET", _liveState.EkpoLoaded && _liveState.EketLoaded ? "bereit" : "wartet auf Tabellen", _liveState.EkpoLoaded && _liveState.EketLoaded ? "ready" : "waiting for tables", _liveState.EkpoLoaded && _liveState.EketLoaded ? Color.Success : Color.Warning),
new("Preisabweichung", "Price variance", "Preisdelta % / CHF", "price delta % / CHF", "Artikel / Lieferant / Jahr", "EKPO", _liveState.EkpoLoaded ? "bereit" : "wartet auf EKPO", _liveState.EkpoLoaded ? "ready" : "waiting for EKPO", _liveState.EkpoLoaded ? Color.Success : Color.Warning),
new("Maverick Buying", "Maverick buying", "Anteil ausserhalb Vertrag", "share outside contract", "Einkaeufer / Lieferant / Warengruppe", "EKKO+EKPO+Kontrakt", "Konzept", "concept", Color.Info),
new("Rahmenvertragsnutzung", "Contract utilisation", "Abrufquote %", "consumption rate %", "Kontrakt / Lieferant / Artikel", "EKPO+EKET", _liveState.EketLoaded ? "teilweise" : "wartet auf EKET", _liveState.EketLoaded ? "partial" : "waiting for EKET", _liveState.EketLoaded ? Color.Success : Color.Warning),
new("Working Capital", "Working capital", "Cash Forecast CHF", "cash forecast CHF", "Monat / Lieferant / Warengruppe", "EKPO+EKET+FI", "Konzept", "concept", Color.Info),
new("Datenqualitaet", "Data quality", "Mapping-Abdeckung %", "mapping coverage %", "Tabelle / Feld / Land", "EKKO+EKPO+Mapping", _liveState.EkkoLoaded ? "startklar" : "wartet", _liveState.EkkoLoaded ? "ready" : "waiting", _liveState.EkkoLoaded ? Color.Success : Color.Warning)
];
private readonly List<Purchasing3dIndicator> Purchasing3dIndicators =
[
new("spend", "Spend CHF", "Spend CHF", "CHF"),
@@ -661,6 +771,9 @@
private sealed record PurchasingSource(string Name, string Description);
private sealed record PurchasingPipelineStep(string TitleDe, string TitleEn, string Value, string DetailDe, string DetailEn, string Icon, bool IsReady, Color Color);
private sealed record PowerBiPageInfo(string Page, string Visuals, string Measure, string Dimensions);
private sealed record PurchasingIdea(string TitleDe, string TitleEn, string DescriptionDe, string DescriptionEn, string RequiredData, string ValueDe, string ValueEn, string StatusDe, string StatusEn, string Icon, Color Color);
private sealed record PurchasingIdeaPriority(string TitleDe, string TitleEn, string DetailDe, string DetailEn, string Icon, Color Color);
private sealed record PurchasingIdeaKpi(string AnalysisDe, string AnalysisEn, string KpiDe, string KpiEn, string Dimension, string Source, string StatusDe, string StatusEn, Color Color);
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);
}
@@ -933,6 +1046,87 @@
word-break: break-word;
}
.purchasing-idea-grid {
display: grid;
grid-template-columns: repeat(2, minmax(260px, 1fr));
gap: 12px;
}
.purchasing-idea-card {
display: grid;
grid-template-columns: 46px minmax(0, 1fr);
gap: 12px;
padding: 14px;
border: 1px solid var(--mud-palette-lines-default);
border-radius: 8px;
background: var(--mud-palette-surface);
min-height: 170px;
}
.purchasing-idea-icon {
width: 42px;
height: 42px;
display: grid;
place-items: center;
border-radius: 8px;
background: rgba(21,101,192,.1);
}
.purchasing-idea-content {
display: flex;
flex-direction: column;
gap: 9px;
min-width: 0;
}
.purchasing-idea-title {
display: flex;
justify-content: space-between;
align-items: start;
gap: 10px;
}
.purchasing-idea-content > span,
.purchasing-idea-meta span,
.purchasing-priority-row span {
color: var(--mud-palette-text-secondary);
font-size: .88rem;
}
.purchasing-idea-meta {
display: grid;
gap: 6px;
margin-top: auto;
}
.purchasing-idea-meta code {
white-space: normal;
word-break: break-word;
}
.purchasing-priority-stack {
display: grid;
gap: 10px;
}
.purchasing-priority-row {
display: grid;
grid-template-columns: 28px minmax(0, 1fr);
gap: 10px;
align-items: start;
padding: 11px 0;
border-bottom: 1px solid var(--mud-palette-lines-default);
}
.purchasing-priority-row:last-child {
border-bottom: 0;
}
.purchasing-priority-row strong {
display: block;
margin-bottom: 4px;
}
.purchasing-source-row {
display: grid;
grid-template-columns: 26px minmax(0, 1fr);
@@ -965,6 +1159,7 @@
.purchasing-hero-metrics,
.purchasing-pipeline,
.purchasing-axis-grid,
.purchasing-idea-grid,
.purchasing-mini-donut-wrap {
grid-template-columns: 1fr;
}