Expand purchasing dashboard from PBIX

This commit is contained in:
2026-06-05 07:22:05 +02:00
parent 6f094fcac6
commit 9b287c15ef
6 changed files with 346 additions and 15 deletions
@@ -1,23 +1,221 @@
@page "/einkauf"
@using TrafagSalesExporter.Models
@inject TrafagSalesExporter.Services.IUiTextService UiText
<PageTitle>@T("Einkauf", "Purchasing")</PageTitle>
<MudText Typo="Typo.h4" Class="mb-4">@T("Einkauf", "Purchasing")</MudText>
<MudPaper Class="pa-4" Outlined="true">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.ShoppingCart" Color="Color.Primary" Size="Size.Large" />
<div>
<MudText Typo="Typo.h6">@T("Einkauf Dashboard", "Purchasing dashboard")</MudText>
<MudText Typo="Typo.body2">
@T("Dieser Bereich ist als Einstiegspunkt fuer Einkaufsauswertungen vorbereitet.",
"This area is prepared as the entry point for purchasing analytics.")
<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>
</MudTabs>
@code {
private readonly List<PurchasingKpiCard> KpiCards =
[
new("Spend total", "Total spend", "-", "Netwr CHF historisch", "Historic Netwr CHF", Icons.Material.Filled.Payments, Color.Primary),
new("Offene Bestellungen", "Open orders", "-", "Wert und Menge offen", "Open value and quantity", Icons.Material.Filled.PendingActions, Color.Warning),
new("Kontrakte", "Contracts", "-", "Restverpflichtungen", "Remaining commitments", Icons.Material.Filled.Assignment, Color.Info),
new("Lieferantenperformance", "Supplier performance", "-", "Bewertung / Liefertermintreue", "Rating / delivery reliability", 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 string T(string german, string english) => UiText.Text(german, english);
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);
}
<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;
}
</style>
@@ -0,0 +1,42 @@
@inject TrafagSalesExporter.Services.IUiTextService UiText
@using TrafagSalesExporter.Models
<MudPaper Class="pa-3" Outlined="true">
<MudText Typo="Typo.h6">@T(TitleDe, TitleEn)</MudText>
<MudText Typo="Typo.body2" Class="mb-3 purchasing-section-muted">@T(DescriptionDe, DescriptionEn)</MudText>
<MudTable Items="@Rows" Dense="true" Hover="true">
<HeaderContent>
<MudTh>@T("Analyse", "Analysis")</MudTh>
<MudTh>@T("Kennzahl", "Measure")</MudTh>
<MudTh>@T("Dimension", "Dimension")</MudTh>
<MudTh>@T("Quelle", "Source")</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@T(context.TitleDe, context.TitleEn)</MudTd>
<MudTd><code>@context.Measure</code></MudTd>
<MudTd>@context.Dimension</MudTd>
<MudTd>
<MudChip T="string" Size="Size.Small" Variant="Variant.Outlined" Color="@(context.Source == "PBIX" ? Color.Primary : Color.Secondary)">
@context.Source
</MudChip>
</MudTd>
</RowTemplate>
</MudTable>
</MudPaper>
@code {
[Parameter, EditorRequired] public string TitleDe { get; set; } = string.Empty;
[Parameter, EditorRequired] public string TitleEn { get; set; } = string.Empty;
[Parameter, EditorRequired] public string DescriptionDe { get; set; } = string.Empty;
[Parameter, EditorRequired] public string DescriptionEn { get; set; } = string.Empty;
[Parameter, EditorRequired] public IReadOnlyList<PurchasingAnalysisRow> Rows { get; set; } = [];
private string T(string german, string english) => UiText.Text(german, english);
}
<style>
.purchasing-section-muted {
color: var(--mud-palette-text-secondary);
}
</style>
@@ -0,0 +1,3 @@
namespace TrafagSalesExporter.Models;
public sealed record PurchasingAnalysisRow(string TitleDe, string TitleEn, string Measure, string Dimension, string Source);
@@ -0,0 +1,62 @@
# Einkaufsdashboard 2026-06-05
## Ziel
Der neue Bereich `Einkauf` soll die vorhandene Power-BI-Vorlage `x.pbix` aufnehmen und um weitere SAP-Einkaufsanalysen ergaenzen.
## Aus `x.pbix` uebernommene Struktur
Analysierte PBIX-Seiten:
- Beschaffungsvolumen CHF je Lieferant.
- Einkaufsvolumen CHF je Lieferant als Kuchenansicht.
- Balkenansicht Volumen je Lieferant und Warengruppe.
- Diagramm Volumen je Warengruppe.
- Einkaufsvolumen CHF je Region.
- Preisentwicklung CHF.
- Matrix Volumen je Warengruppe.
Sichtbare PBIX-Felder:
- `EKPOSet.Netwr CHF`
- `EKPOSet.Netwr CHF/Stk`
- `EKKOSet.Bedat`
- `Data.Name`
- `Data (2).WG komplett`
- `EKPOSet.Matnr`
- `EKPOSet.Txz01`
## Zusaetzlich aufgenommene SAP-Themen
Das Dashboard wurde fachlich um diese Bereiche erweitert:
- Spend total vergangen nach Jahr, Lieferant, Warengruppe und Artikel.
- Offene Bestellwerte und offene Mengen nach Lieferant, Warengruppe und Artikel.
- Offene Verpflichtungen / Mengenkontrakte nach Lieferant, Warengruppe und Artikel.
- Lieferantenbewertungen und Performance nach Lieferant, Warengruppe und Artikel.
## Aktueller Implementierungsstand
- Route: `/einkauf`.
- Hauptnavigation: eigener Punkt `Einkauf` mit Einkaufswagen-Icon.
- Tabs im Einkaufsdashboard:
- `Uebersicht`
- `Spend`
- `Offene Bestellungen`
- `Kontrakte`
- `Lieferanten`
- `PBIX Vorlage`
- Die Seite ist als Cockpit-Struktur umgesetzt und zweisprachig ueber den vorhandenen UI-Sprachservice vorbereitet.
- Die Kennzahlen sind noch nicht live an SAP gebunden.
## Naechster Schritt fuer Live-Daten
Fuer echte Werte muessen die Einkaufsquellen sauber gemappt werden:
- Bestellkopf, z. B. `EKKOSet`.
- Bestellpositionen, z. B. `EKPOSet`.
- Offene Liefer-/Terminmengen, voraussichtlich Termin-/Schedule-Daten.
- Kontrakte und offene Verpflichtungen.
- Lieferantenbewertung / Performance, falls im SAP-System als OData- oder HANA-Quelle verfuegbar.
Danach koennen Filter, Aggregationen und Delta-/Refresh-Prozess analog zu Finance/Spain umgesetzt werden.
+3 -1
View File
@@ -10,7 +10,8 @@ Stand: 2026-06-05
- Letzter dokumentierter Finance-Deploy: 2026-06-05 auf `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\`.
- Neu im Finance/Management-Cockpit: einfache Schnelluebersicht links sichtbar; tiefere Funktionen bleiben unter `Experten`.
- Neu in der Navigation: Menuebaum wird aus `NavigationMenuItems` gerendert; Admins koennen bestehende Punkte unter `Admin > Menuestruktur` umhaengen, sortieren und aus-/einblenden.
- Neu als Hauptbereich: `Einkauf` mit Einkaufswagen-Icon und Einstieg `Einkauf Dashboard`.
- Neu als Hauptbereich: `Einkauf` mit Einkaufswagen-Icon und erweitertem `Einkauf Dashboard`.
- Einkauf: `x.pbix` wurde als Vorlage analysiert; `/einkauf` enthaelt jetzt Struktur fuer Spend, offene Bestellungen, Mengenkontrakte, Lieferantenperformance und die PBIX-Reportseiten. Live-SAP-Anbindung ist noch offen.
- Neu im Expertenbereich: `3D Datenanalyse` mit drehbarer 3D-Grafik, Achsen, Diagrammarten, Indikatorauswahl, Labelgroesse und Simulation per Schieberegler.
- Spanien: `Run-SpainRangeExportAndUpload-AllInOne.ps1` exportiert Sage-Range direkt und laedt CSV/Summary via rclone nach SharePoint `trafag-bi:Import/Finance/Spanien`.
- Spanien: Default-Range ist heute minus 7 Tage bis heute; `ToDate` ist exklusiv.
@@ -27,6 +28,7 @@ Stand: 2026-06-05
- HR KPI: `docs/rag/HR_KPI.md`
- Deployment/IIS: `docs/rag/DEPLOYMENT.md`
- Admin/Startseite: `docs/rag/ADMIN.md`
- Einkauf: `docs/PURCHASING_DASHBOARD_2026-06-05.md`
## Rohquellen Nur Bei Bedarf
+25 -1
View File
@@ -50,7 +50,31 @@ Diese Datei ist fuer tokenarme RAG-Nutzung komprimiert.
- Spanien-Delta-Sync im Dashboard-Import wurde am 2026-06-05 publiziert. Publish brauchte kurz `app_offline.htm`, weil `BiDashboard.dll` gesperrt war; danach wurde `app_offline.htm` wieder entfernt.
- Neu umgesetzt: Linker Menuebaum ist datengetrieben und ueber `Admin > Menuestruktur` umhaengbar/sortierbar; bestehende Punkte koennen in andere Untermenues verschoben werden.
- Neu umgesetzt: Neuer Hauptpunkt `Einkauf` mit Einkaufswagen-Icon und vorbereiteter Einstiegseite `Einkauf Dashboard`.
- Letzte Validierung: `dotnet test TrafagSalesExporter.sln --verbosity minimal --artifacts-path C:\TMP\trafag-test-artifacts-finance-session-proof` mit `82/82` Tests gruen.
- Neu umgesetzt: `x.pbix` als Einkaufs-/SAP-Vorlage analysiert und `Einkauf Dashboard` auf Spend, offene Bestellungen, Kontrakte, Lieferantenperformance und PBIX-Vorlagenstruktur erweitert.
- Wichtig Einkauf: Aktuell ist die Seite fachlich strukturiert, aber noch nicht live an SAP/OData angebunden; fuer Echtwerte muessen Einkaufsquellen wie `EKKOSet`, `EKPOSet`, ggf. Termin-/Kontrakt- und Lieferantenbewertungsdaten gemappt werden.
- Letzte Validierung: `dotnet test TrafagSalesExporter.sln --verbosity minimal` mit `83/83` Tests gruen.
## Nachtrag 2026-06-05 Einkauf / PBIX
Quelle:
- Lokale Vorlage `x.pbix` wurde geoeffnet und die Report-Struktur ausgewertet.
- PBIX-Reportseiten: Beschaffungsvolumen CHF je Lieferant, Einkaufsvolumen als Lieferanten-Kuchen, Balken Lieferant/Warengruppe, Diagramm Warengruppe, Einkaufsvolumen CHF je Region, Preisentwicklung CHF und Matrix Volumen/Warengruppe.
- In der PBIX sichtbare Felder: `EKPOSet.Netwr CHF`, `EKKOSet.Bedat`, `Data.Name`, `Data (2).WG komplett`, `EKPOSet.Matnr`, `EKPOSet.Txz01` und `EKPOSet.Netwr CHF/Stk`.
Umgesetzt:
- Einkaufsseite `/einkauf` von Platzhalter zu fachlichem Cockpit erweitert.
- Tabs: `Uebersicht`, `Spend`, `Offene Bestellungen`, `Kontrakte`, `Lieferanten`, `PBIX Vorlage`.
- Zusaetzlich zu den PBIX-Sichten wurden die vom Einkauf genannten SAP-Themen aufgenommen:
- Spend total vergangen nach Jahr, Lieferant, Warengruppe, Artikel.
- Offene Bestellwerte und Mengen nach Lieferant, Warengruppe, Artikel.
- Offene Verpflichtungen / Mengenkontrakte nach Lieferant, Warengruppe, Artikel.
- Lieferantenbewertung / Performance nach Lieferant, Warengruppe, Artikel.
Naechster technischer Schritt:
- Live-Anbindung der Einkaufsdatenquellen definieren und implementieren. Die UI ist vorbereitet; echte Kennzahlen brauchen noch SAP/OData-Feldmapping, Filterlogik und Aktualisierungsprozess.
## Nachtrag 2026-06-05 Spanien Sage / rclone Upload