Document AI role and HR exclusions

This commit is contained in:
2026-05-15 11:55:40 +02:00
parent 83e556e89e
commit 9daf54b8d9
6 changed files with 200 additions and 0 deletions
@@ -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.
+60
View File
@@ -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:
+47
View File
@@ -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.