Document AI role and HR exclusions
This commit is contained in:
@@ -8,6 +8,15 @@ namespace TrafagSalesExporter.Services;
|
|||||||
internal sealed class HrKpiDashboardBuilder
|
internal sealed class HrKpiDashboardBuilder
|
||||||
{
|
{
|
||||||
private readonly HrKpiDataSourceOptions _dataSources;
|
private readonly HrKpiDataSourceOptions _dataSources;
|
||||||
|
private static readonly HashSet<string> ExcludedPersonNameKeys = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
NormalizePersonExclusionKey("Angelina Jolie"),
|
||||||
|
NormalizePersonExclusionKey("Brad Pitt"),
|
||||||
|
NormalizePersonExclusionKey("Peter Muster"),
|
||||||
|
NormalizePersonExclusionKey("ICT Trafag"),
|
||||||
|
NormalizePersonExclusionKey("Empfanger Reminder"),
|
||||||
|
NormalizePersonExclusionKey("Empfänger Reminder")
|
||||||
|
};
|
||||||
|
|
||||||
public HrKpiDashboardBuilder(HrKpiDataSourceOptions dataSources)
|
public HrKpiDashboardBuilder(HrKpiDataSourceOptions dataSources)
|
||||||
{
|
{
|
||||||
@@ -40,6 +49,12 @@ internal sealed class HrKpiDashboardBuilder
|
|||||||
var employees = LoadEmployees(context, timeRows, sapRows);
|
var employees = LoadEmployees(context, timeRows, sapRows);
|
||||||
var absences = LoadAbsences(context);
|
var absences = LoadAbsences(context);
|
||||||
var leavers = LoadLeavers(context);
|
var leavers = LoadLeavers(context);
|
||||||
|
var excludedRows =
|
||||||
|
employees.RemoveAll(x => IsExcludedTestPerson(x.NameVoll)) +
|
||||||
|
absences.RemoveAll(x => IsExcludedTestPerson(x.Name)) +
|
||||||
|
leavers.RemoveAll(x => IsExcludedTestPerson(x.NameVoll));
|
||||||
|
if (excludedRows > 0)
|
||||||
|
result.Notices.Add($"{excludedRows:N0} Testpersonen-Zeilen wurden aus dem HR-KPI-Dashboard ausgeschlossen.");
|
||||||
|
|
||||||
result.OrganisationOptions = employees
|
result.OrganisationOptions = employees
|
||||||
.Select(x => x.Organisationseinheit)
|
.Select(x => x.Organisationseinheit)
|
||||||
@@ -939,6 +954,18 @@ internal sealed class HrKpiDashboardBuilder
|
|||||||
private static string NormalizeKey(string value)
|
private static string NormalizeKey(string value)
|
||||||
=> value.Trim().ToUpperInvariant();
|
=> value.Trim().ToUpperInvariant();
|
||||||
|
|
||||||
|
private static bool IsExcludedTestPerson(string? name)
|
||||||
|
=> !string.IsNullOrWhiteSpace(name) &&
|
||||||
|
ExcludedPersonNameKeys.Contains(NormalizePersonExclusionKey(name));
|
||||||
|
|
||||||
|
private static string NormalizePersonExclusionKey(string value)
|
||||||
|
{
|
||||||
|
var normalized = NormalizeReason(value)
|
||||||
|
.Replace(",", " ", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var parts = normalized.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
return string.Join(" ", parts.OrderBy(x => x, StringComparer.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
private static int? ParseCostCenter(string value)
|
private static int? ParseCostCenter(string value)
|
||||||
{
|
{
|
||||||
var raw = value.Split('/')[0].Trim();
|
var raw = value.Split('/')[0].Trim();
|
||||||
|
|||||||
@@ -211,6 +211,45 @@ public sealed class HrKpiServiceTests : IDisposable
|
|||||||
Assert.Contains(result.Leavers, row => row.Austrittsart == "Ruhestand" && row.FluktuationAusschlussgrund == "Pensionierung");
|
Assert.Contains(result.Leavers, row => row.Austrittsart == "Ruhestand" && row.FluktuationAusschlussgrund == "Pensionierung");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BuildAsync_Excludes_Configured_Test_Persons_From_All_Hr_Kpi_Views()
|
||||||
|
{
|
||||||
|
RewriteEmployeeRows(
|
||||||
|
[
|
||||||
|
[1001, "Alpha, Anna", "Org A", "100 / Org A", "Engineer", "n", new DateTime(2020, 1, 1), "Aktiv", "0:00", 25, 0, 0, 100000, "CHF"],
|
||||||
|
[9001, "Jolie, Angelina", "Test", "999 / Test", "Engineer", "n", new DateTime(2020, 1, 1), "Aktiv", "0:00", 25, 0, 0, 100000, "CHF"],
|
||||||
|
[9002, "Brad Pitt", "Test", "999 / Test", "Engineer", "n", new DateTime(2020, 1, 1), "Aktiv", "0:00", 25, 0, 0, 100000, "CHF"],
|
||||||
|
[9003, "Peter Muster", "Test", "999 / Test", "Engineer", "n", new DateTime(2020, 1, 1), "Aktiv", "0:00", 25, 0, 0, 100000, "CHF"]
|
||||||
|
]);
|
||||||
|
WriteWorkbook(Path.Combine(_folder, "Abwesenheitinstunden.xlsx"),
|
||||||
|
[
|
||||||
|
"Personalnummer", "Nachname, Vorname (Link Personal)", "Organisation", "Stelle", "Personal Status",
|
||||||
|
"Krankheit angetreten (Stunden Ind.)", "Krank nicht buchbar angetreten (Stunden Ind.)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[1001, "Alpha, Anna", "Org A", "Engineer", "Aktiv", 8.4, 0],
|
||||||
|
[9004, "ICT Trafag", "Test", "Engineer", "Aktiv", 8.4, 0]
|
||||||
|
]);
|
||||||
|
RewriteLeaverRows(
|
||||||
|
[
|
||||||
|
[1001, "Alpha, Anna", "Org A", "Engineer", "Inaktiv", new DateTime(2025, 3, 10), new DateTime(2020, 1, 1), "Arbeitnehmer Kuendigung"],
|
||||||
|
[9005, "Empfänger Reminder", "Test", "Engineer", "Inaktiv", new DateTime(2025, 3, 10), new DateTime(2020, 1, 1), "Arbeitnehmer Kuendigung"]
|
||||||
|
]);
|
||||||
|
|
||||||
|
var result = await _service.BuildAsync(new HrKpiOptions
|
||||||
|
{
|
||||||
|
DataFolder = _folder,
|
||||||
|
Year = 2025
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.DoesNotContain(result.Employees, row => row.NameVoll.Contains("Angelina", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.DoesNotContain(result.Employees, row => row.NameVoll.Contains("Brad Pitt", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.DoesNotContain(result.Employees, row => row.NameVoll.Contains("Peter Muster", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.DoesNotContain(result.Absences, row => row.Name.Contains("ICT Trafag", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.DoesNotContain(result.Leavers, row => row.NameVoll.Contains("Reminder", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Contains(result.Notices, notice => notice.Contains("Testpersonen", StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
private static void WriteFixtureFiles(string folder)
|
private static void WriteFixtureFiles(string folder)
|
||||||
{
|
{
|
||||||
WriteWorkbook(Path.Combine(folder, "Saldiperstichdatum.xlsx"),
|
WriteWorkbook(Path.Combine(folder, "Saldiperstichdatum.xlsx"),
|
||||||
|
|||||||
Binary file not shown.
@@ -11,6 +11,7 @@ Ziel: Aufbau eines modernen, uebersichtlichen Intranet-Dashboards fuer das Group
|
|||||||
| 1 | Fuehrendes CFO-Dokument verwenden: `FINANCE_CHEF_SUMMARY_2026-05-15.docx` | Offen |
|
| 1 | Fuehrendes CFO-Dokument verwenden: `FINANCE_CHEF_SUMMARY_2026-05-15.docx` | Offen |
|
||||||
| 1 | Offene Finance-Entscheide mit Andreas/Finance durchgehen | Offen |
|
| 1 | Offene Finance-Entscheide mit Andreas/Finance durchgehen | Offen |
|
||||||
| 1 | Italien-Abweichung klaeren: Berechnungsart, Deduplizierung, Intercompany | Offen |
|
| 1 | Italien-Abweichung klaeren: Berechnungsart, Deduplizierung, Intercompany | Offen |
|
||||||
|
| 1 | Italien IC-Diagnose besprechen: Trafag, Magnetic Sense/Magnets Sense und Gesellschaft fuer Sensorik erklaeren einen grossen Teil, aber nicht die ganze Abweichung | Offen |
|
||||||
| 1 | Deutschland: finalen Jahresfile 2025 beschaffen | Offen |
|
| 1 | Deutschland: finalen Jahresfile 2025 beschaffen | Offen |
|
||||||
| 2 | UK/England: Jahresvollstaendigkeit und Restdifferenz pruefen | Offen |
|
| 2 | UK/England: Jahresvollstaendigkeit und Restdifferenz pruefen | Offen |
|
||||||
| 2 | CH/AT: Sollzuordnung und Trennung final bestaetigen | Offen |
|
| 2 | CH/AT: Sollzuordnung und Trennung final bestaetigen | Offen |
|
||||||
@@ -29,3 +30,29 @@ Ziel im Termin:
|
|||||||
- Offene Laenderabweichungen priorisieren.
|
- Offene Laenderabweichungen priorisieren.
|
||||||
- Pro Land festlegen, welche Datenquelle und Berechnungslogik final gilt.
|
- Pro Land festlegen, welche Datenquelle und Berechnungslogik final gilt.
|
||||||
|
|
||||||
|
## IT / Intercompany Diagnose
|
||||||
|
|
||||||
|
Aktuelle Diagnose fuer Italien:
|
||||||
|
|
||||||
|
| Kennzahl | Wert |
|
||||||
|
| --- | ---: |
|
||||||
|
| IT Ist vor IC-Abzug | 14.704.336,29 EUR |
|
||||||
|
| Rhino/check.xlsx Soll | 7.669.840,00 EUR |
|
||||||
|
| Abweichung vor IC | +7.034.496,29 EUR |
|
||||||
|
| Erkannter IC-/2nd-party-Abzug | 4.397.746,90 EUR |
|
||||||
|
| IT Ist exkl. IC | 10.306.589,39 EUR |
|
||||||
|
| Restabweichung nach IC | +2.636.749,39 EUR |
|
||||||
|
|
||||||
|
Verwendete IC-/2nd-party-Marker:
|
||||||
|
|
||||||
|
- `TRAFAG`
|
||||||
|
- `MAGNETIC SENSE`
|
||||||
|
- `MAGNETS SENSE`
|
||||||
|
- `GESELLSCHAFT FUER SENSORIK`
|
||||||
|
- `GESELLSCHAFT FUR SENSORIK`
|
||||||
|
|
||||||
|
Bewertung:
|
||||||
|
|
||||||
|
- Intercompany/2nd-party erklaert einen grossen Teil der IT-Abweichung.
|
||||||
|
- Die Summe passt dadurch deutlich besser, aber noch nicht vollstaendig.
|
||||||
|
- Restabweichung weiter pruefen: Summenlogik, Beleg/Position-Deduplizierung, Gutschriften/Storno und weitere lokale IC-Kunden oder Schreibweisen.
|
||||||
|
|||||||
@@ -1349,6 +1349,66 @@ Ergebnis:
|
|||||||
- Build erfolgreich.
|
- Build erfolgreich.
|
||||||
- 3 bestehende MudBlazor-Analyzer-Warnungen in `Logs.razor`, `Transformations.razor` und `Standorte.razor`.
|
- 3 bestehende MudBlazor-Analyzer-Warnungen in `Logs.razor`, `Transformations.razor` und `Standorte.razor`.
|
||||||
|
|
||||||
|
## CFO-Bericht IT/Intercompany Diagnose 2026-05-15
|
||||||
|
|
||||||
|
Ergaenzt:
|
||||||
|
|
||||||
|
- `docs/CFO_Kurzbericht_270515.docx`
|
||||||
|
- `docs/FINANCE_DASHBOARD_TODO_2026-05-15.md`
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
- IT/Intercompany-Diagnose fuer die grosse Italien-Abweichung.
|
||||||
|
- Marker dokumentiert: `TRAFAG`, `MAGNETIC SENSE`, `MAGNETS SENSE`, `GESELLSCHAFT FUER SENSORIK`, `GESELLSCHAFT FUR SENSORIK`.
|
||||||
|
- Zahlen:
|
||||||
|
- IT Ist vor IC-Abzug: `14.704.336,29 EUR`
|
||||||
|
- IC-/2nd-party-Abzug: `4.397.746,90 EUR`
|
||||||
|
- IT Ist exkl. IC: `10.306.589,39 EUR`
|
||||||
|
- Rhino/check.xlsx Soll: `7.669.840,00 EUR`
|
||||||
|
- Restabweichung nach IC: `+2.636.749,39 EUR`
|
||||||
|
|
||||||
|
Bewertung:
|
||||||
|
|
||||||
|
- Intercompany/2nd-party erklaert einen grossen Teil der IT-Abweichung.
|
||||||
|
- Restabweichung bleibt offen und muss ueber Summenlogik, Beleg/Position-Deduplizierung, Gutschriften/Storno und weitere lokale IC-Kunden oder Schreibweisen geprueft werden.
|
||||||
|
|
||||||
|
## HR KPI Testpersonen-Ausschluss 2026-05-15
|
||||||
|
|
||||||
|
Geaendert:
|
||||||
|
|
||||||
|
- Folgende Testpersonen werden zentral aus dem HR-KPI-Dashboard ausgeschlossen:
|
||||||
|
- Angelina Jolie
|
||||||
|
- Brad Pitt
|
||||||
|
- Peter Muster
|
||||||
|
- ICT Trafag
|
||||||
|
- Empfanger Reminder / Empfaenger Reminder
|
||||||
|
- Der Ausschluss erfolgt vor KPI-, Filter- und Tabellenberechnung.
|
||||||
|
- Betroffen sind aktive Mitarbeitende, Absenzen und Austritte.
|
||||||
|
- Im Dashboard erscheint eine Notice, wie viele Testpersonen-Zeilen ausgeschlossen wurden.
|
||||||
|
|
||||||
|
Verifikation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
dotnet test .\TrafagSalesExporter.Tests\TrafagSalesExporter.Tests.csproj --no-restore -p:UseAppHost=false -p:OutDir=.\obj\verify_hr_exclusions\ --verbosity minimal
|
||||||
|
```
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- 70/70 Tests erfolgreich.
|
||||||
|
- 3 bestehende MudBlazor-Analyzer-Warnungen in `Logs.razor`, `Transformations.razor` und `Standorte.razor`.
|
||||||
|
|
||||||
|
## KI-Arbeitsanweisung 2026-05-15
|
||||||
|
|
||||||
|
Erstellt:
|
||||||
|
|
||||||
|
- `persona.md`
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
- Rolle der KI als Entwicklungs-, Analyse- und Dokumentationswerkzeug.
|
||||||
|
- Grenzen der KI bei fachlicher Verantwortung, Finance, HR, Datenschutz und Freigaben.
|
||||||
|
- Arbeitsprinzipien fuer dieses Projekt: bestehende Architektur nutzen, kritisch testen, sauber dokumentieren und offene fachliche Punkte als Pruefpunkte markieren.
|
||||||
|
|
||||||
## Navigation in Finance/HR/Admin gegliedert 2026-05-15
|
## Navigation in Finance/HR/Admin gegliedert 2026-05-15
|
||||||
|
|
||||||
Geaendert:
|
Geaendert:
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# KI-Arbeitsanweisung fuer dieses Projekt
|
||||||
|
|
||||||
|
Stand: 2026-05-15
|
||||||
|
|
||||||
|
## Rolle
|
||||||
|
|
||||||
|
Die KI unterstuetzt in diesem Projekt als Entwicklungs-, Analyse- und Dokumentationswerkzeug.
|
||||||
|
|
||||||
|
Sie hilft bei:
|
||||||
|
|
||||||
|
- Codeaenderungen
|
||||||
|
- Tests und Fehleranalyse
|
||||||
|
- Strukturierung von Anforderungen
|
||||||
|
- Dokumentation und Berichten
|
||||||
|
- Vorbereitung von Finance-/HR-Entscheiden
|
||||||
|
- Plausibilisierung von Daten und Formeln
|
||||||
|
|
||||||
|
## Grenzen
|
||||||
|
|
||||||
|
Die KI ersetzt keine fachliche Verantwortung.
|
||||||
|
|
||||||
|
Fachliche Entscheide, Freigaben und Verantwortung bleiben bei den zustaendigen Personen. Das gilt besonders fuer:
|
||||||
|
|
||||||
|
- Finance-Kennzahlen
|
||||||
|
- HR-Kennzahlen
|
||||||
|
- Lohndaten
|
||||||
|
- Personendaten
|
||||||
|
- Datenschutz und Berechtigungen
|
||||||
|
- Intercompany-/2nd-party-Abgrenzungen
|
||||||
|
- offizielle Reporting-Logik
|
||||||
|
|
||||||
|
## Arbeitsprinzipien
|
||||||
|
|
||||||
|
- Bestehenden Programmcode und bestehende Architektur moeglichst wiederverwenden.
|
||||||
|
- Aenderungen nachvollziehbar dokumentieren.
|
||||||
|
- Kritische Berechnungen mit Tests absichern.
|
||||||
|
- Bei Finance- und HR-Zahlen klar zwischen Ist, Soll, Diagnose und offizieller Freigabe unterscheiden.
|
||||||
|
- Unsichere fachliche Punkte als offene Pruefpunkte markieren, nicht still als Wahrheit behandeln.
|
||||||
|
- Keine sensiblen Daten unnoetig ausgeben oder duplizieren.
|
||||||
|
- HR-Bereiche mit separater Zugriffskontrolle behandeln.
|
||||||
|
|
||||||
|
## Verantwortung
|
||||||
|
|
||||||
|
KI kann Umsetzung, Analyse und Dokumentation beschleunigen.
|
||||||
|
|
||||||
|
Die finale fachliche Entscheidung liegt beim Menschen.
|
||||||
|
|
||||||
Reference in New Issue
Block a user