Commit pending finance and Power BI work
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
# Fluktuation Nachdokumentation - 2026-05-12
|
||||
|
||||
## Ausgangslage
|
||||
|
||||
Die Fluktuationsformeln aus `formeln.docx` sollten in die Power-BI-Logik uebernommen werden.
|
||||
|
||||
Fachliche Definition laut HR:
|
||||
|
||||
- Zaehler: nur Arbeitnehmerkuendigungen
|
||||
- Nicht zaehlen: befristete Vertraege, Aushilfen, Pensionierungen und Kuendigungen durch Trafag
|
||||
- Nenner: durchschnittlicher Headcount, nicht FTE
|
||||
- Monat: Austritte des Monats / Headcount des Monats
|
||||
- Quartal: Austritte des Quartals / durchschnittlicher Headcount des Quartals
|
||||
- Jahreshochrechnung: aktuelle Quartals-Fluktuation x 4
|
||||
- Effektives Jahr: Austritte des Jahres / durchschnittlicher Headcount des Jahres
|
||||
|
||||
## Geaenderte Dateien
|
||||
|
||||
### `rexx_ausgeschieden.txt`
|
||||
|
||||
Die bestehende Power-Query fuer `C:\temp\Personalausgeschieden.xlsx` wurde erweitert.
|
||||
|
||||
Neu bzw. angepasst:
|
||||
|
||||
- robuste Umwandlung von `Austrittsdatum` und `Eintrittsdatum`
|
||||
- Date
|
||||
- DateTime
|
||||
- Excel-Seriennummer, z.B. `45396.0`
|
||||
- Text im Format `dd.MM.yyyy`
|
||||
- Normalisierung von `Austrittsart`
|
||||
- Kleinbuchstaben
|
||||
- Umlaute nach ASCII, z.B. `kuendigung`
|
||||
- neue fachliche Spalten:
|
||||
- `Austrittsart_Normalisiert`
|
||||
- `Ist_Arbeitnehmerkuendigung`
|
||||
- `Ist_Fluktuation_Ausgeschlossen`
|
||||
- `Ist_Fluktuationsrelevant`
|
||||
- `Fluktuation_Ausschlussgrund`
|
||||
|
||||
Wichtig: `Kündigung AN` aus Rexx wird jetzt als Arbeitnehmerkuendigung erkannt.
|
||||
|
||||
### `fluktuation_measures_dax.txt`
|
||||
|
||||
Neues DAX-File fuer die Fluktuations-Measures.
|
||||
|
||||
Tabellenreferenzen wurden auf `HR_KPI_DATEN_SAP` gesetzt.
|
||||
|
||||
Enthaltene Measures:
|
||||
|
||||
- `Headcount Festangestellt`
|
||||
- `Headcount Aktiv Total`
|
||||
- `Austritte Total Rexx`
|
||||
- `Austritte Arbeitnehmerkuendigung`
|
||||
- `Austritte Fluktuationsrelevant`
|
||||
- `Austritte Nicht Fluktuationsrelevant`
|
||||
- `Fluktuation Monat %`
|
||||
- `Avg Headcount Quartal`
|
||||
- `Austritte Quartal`
|
||||
- `Fluktuation Quartal %`
|
||||
- `Fluktuation Hochrechnung Jahr %`
|
||||
- `Avg Headcount Jahr`
|
||||
- `Austritte Jahr`
|
||||
- `Fluktuation Jahr Effektiv %`
|
||||
- `Fluktuation Ausschlussgrund Anzahl`
|
||||
|
||||
Die Austritts-Measures verwenden `TREATAS` auf `Rexx_Ausgeschieden[Austrittsmonat]`, damit die Filterung ueber `HR_KPI_DATEN_SAP[Periode]` auch ohne direkte Beziehung funktionieren kann.
|
||||
|
||||
## Konsolenpruefung der Rexx-Datei
|
||||
|
||||
Gepruefte Datei:
|
||||
|
||||
```text
|
||||
C:\temp\Personalausgeschieden.xlsx
|
||||
```
|
||||
|
||||
Gefundene Austritte:
|
||||
|
||||
```text
|
||||
104 total
|
||||
42 Kuendigung AN
|
||||
34 Kuendigung AG
|
||||
15 Befristung
|
||||
7 leer
|
||||
5 Ruhestand
|
||||
1 Aufhebungsvertrag
|
||||
```
|
||||
|
||||
Nach der korrigierten Logik:
|
||||
|
||||
```text
|
||||
33 fluktuationsrelevante Austritte
|
||||
```
|
||||
|
||||
Die Differenz zu 42 `Kuendigung AN` entsteht, weil Aushilfen, Praktikanten, Werkstudenten und Lehrlinge nicht in die Fluktuationsberechnung einfliessen.
|
||||
|
||||
## Ursache fuer 0/leere Fluktuation
|
||||
|
||||
Die erste Erkennung suchte nach Begriffen wie:
|
||||
|
||||
```text
|
||||
arbeitnehmer
|
||||
mitarbeiter
|
||||
eigenkuendigung
|
||||
kuendigung ma
|
||||
```
|
||||
|
||||
Rexx liefert aber:
|
||||
|
||||
```text
|
||||
Kündigung AN
|
||||
```
|
||||
|
||||
Dadurch war `Ist_Arbeitnehmerkuendigung` ueberall `false`, und die Fluktuations-Measures hatten keinen Zaehler.
|
||||
|
||||
## Erwartete Kontrollwerte in Power BI
|
||||
|
||||
Nach Aktualisierung der Queries sollten ohne zusaetzliche Filter ungefaehr folgende Werte sichtbar sein:
|
||||
|
||||
```text
|
||||
Austritte Total Rexx = 104
|
||||
Austritte Arbeitnehmerkuendigung = 42
|
||||
Austritte Fluktuationsrelevant = 33
|
||||
```
|
||||
|
||||
Wenn `Fluktuation Monat %`, `Fluktuation Quartal %` oder `Fluktuation Jahr Effektiv %` leer bleiben, zuerst diese Punkte pruefen:
|
||||
|
||||
- ist `Rexx_Ausgeschieden` geladen?
|
||||
- heisst die Haupttabelle wirklich `HR_KPI_DATEN_SAP`?
|
||||
- existieren `HR_KPI_DATEN_SAP[Periode]` und `Rexx_Ausgeschieden[Austrittsmonat]` als Date-Spalten?
|
||||
- liefert `Headcount Festangestellt` einen Wert groesser 0?
|
||||
- gibt es aktive Filter auf Jahr, Monat, Organisation oder Kostenstelle?
|
||||
|
||||
## Nachtrag: Leere Quartals-/Jahres-Measures
|
||||
|
||||
Am 2026-05-12 wurden die DAX-Measures in `fluktuation_measures_dax.txt`
|
||||
nochmals angepasst, weil folgende Kennzahlen in Power BI leer waren:
|
||||
|
||||
- `Austritte Jahr`
|
||||
- `Austritte Quartal`
|
||||
- `Fluktuation Hochrechnung Jahr %`
|
||||
- `Fluktuation Quartal %`
|
||||
- `BU_Tage_Total`
|
||||
|
||||
Wahrscheinliche Ursache:
|
||||
|
||||
`HR_KPI_DATEN_SAP[Periode]` wird in `hr_kpi_daten_query.txt` aktuell als
|
||||
aktueller Monat aus `DateTime.LocalNow()` erzeugt. Dadurch enthalten die
|
||||
Perioden in der Haupttabelle nicht zwingend dieselben Monate wie
|
||||
`Rexx_Ausgeschieden[Austrittsmonat]`. Die bisherigen `DATESQTD`- und
|
||||
`DATESYTD`-Measures konnten deshalb keine passenden Austritte finden und
|
||||
lieferten leere Werte.
|
||||
|
||||
Anpassung in `fluktuation_measures_dax.txt`:
|
||||
|
||||
- `Austritte Quartal` rechnet jetzt ueber Quartalsstart und Quartalsende.
|
||||
- `Austritte Jahr` filtert jetzt ueber das Jahr von `Austrittsmonat`.
|
||||
- Prozent-Measures sind mit `COALESCE(..., 0)` gegen leere Werte abgesichert.
|
||||
- Basis-Measures fuer Headcount und Austritte geben ebenfalls `0` statt leer zurueck.
|
||||
- `BU_Tage_Total`, `NBU_Tage_Total` und `Unfalltage Total` wurden ergaenzt.
|
||||
|
||||
Wichtig:
|
||||
|
||||
Die `.pbix` wurde weiterhin nicht direkt bearbeitet. Die geaenderten Measures
|
||||
muessen in Power BI Desktop manuell ersetzt bzw. eingefuegt werden. Falls die
|
||||
Haupttabelle im Modell nicht `HR_KPI_DATEN_SAP`, sondern z.B. `HR_KPI_Daten`
|
||||
heisst, muss der Tabellenname in den DAX-Measures entsprechend angepasst werden.
|
||||
|
||||
## Power-BI-Datei / PBIX
|
||||
|
||||
Die `.pbix`-Datei wurde nicht direkt bearbeitet.
|
||||
|
||||
Grund:
|
||||
|
||||
- `.pbix` ist kein normales Textprojekt.
|
||||
- Power-Query-Code und DAX-Measures liegen intern in Power-BI-Modellstrukturen.
|
||||
- Direktes Bearbeiten kann die Datei beschaedigen.
|
||||
- Ohne Power BI Desktop, Tabular Editor oder ein `.pbip`-Projekt ist das direkte Patchen riskant und unverhaeltnismaessig.
|
||||
|
||||
Empfohlener Weg fuer diese Aenderung:
|
||||
|
||||
1. Power BI Desktop oeffnen.
|
||||
2. Query `Rexx_Ausgeschieden` im Power Query Editor oeffnen.
|
||||
3. Inhalt durch den aktuellen Code aus `rexx_ausgeschieden.txt` ersetzen.
|
||||
4. Modell aktualisieren.
|
||||
5. Nur die geaenderten bzw. benoetigten DAX-Measures aus `fluktuation_measures_dax.txt` ersetzen/einfuegen.
|
||||
|
||||
Nicht alle DAX-Measures muessen neu kopiert werden. Zwingend relevant sind vor allem:
|
||||
|
||||
- `Headcount Festangestellt`
|
||||
- `Austritte Fluktuationsrelevant`
|
||||
- `Avg Headcount Quartal`
|
||||
- `Austritte Quartal`
|
||||
- `Avg Headcount Jahr`
|
||||
- `Austritte Jahr`
|
||||
|
||||
Optional als Diagnose:
|
||||
|
||||
- `Headcount Aktiv Total`
|
||||
- `Austritte Total Rexx`
|
||||
- `Austritte Arbeitnehmerkuendigung`
|
||||
|
||||
Falls das Projekt spaeter als `.pbip` statt `.pbix` gespeichert wird, koennen Modell-/Query-Dateien deutlich besser versioniert und direkt angepasst werden.
|
||||
|
||||
## Nicht geaenderte Dateien
|
||||
|
||||
Nicht angepasst wurden:
|
||||
|
||||
- `hr_kpi_daten_query.txt`
|
||||
- `REXX_aBSENZEN.txt`
|
||||
- `formeln.docx`
|
||||
- `HANDOFF_2026-05-11.md`
|
||||
- `HR_KPI_Formeln_CH.xlsx`
|
||||
- `infos.txt`
|
||||
- `infos2.txt`
|
||||
@@ -0,0 +1,440 @@
|
||||
# Handoff Power BI HR KPI - 2026-05-11
|
||||
|
||||
## Kontext
|
||||
|
||||
Ziel ist ein Power-BI-Dashboard fuer HR-KPIs Schweiz. HR soll das Dashboard konsumieren, nicht selbst bauen. Die Daten sollen aus SAP und mehreren Rexx-Exports in Power BI zusammengefuehrt werden.
|
||||
|
||||
Der aktuelle Fokus ist Phase 1: Zeit und Absenzen, insbesondere Krankheit, Unfall, Ferien, GLZ/Saldo und Vergleich der Quoten mit SAP.
|
||||
|
||||
## Dateien im Projektordner
|
||||
|
||||
Arbeitsordner:
|
||||
|
||||
```text
|
||||
C:\Users\koi\source\repos\Ai\Powerbi
|
||||
```
|
||||
|
||||
Vorhandene Dateien:
|
||||
|
||||
```text
|
||||
HR_KPI_Formeln_CH.xlsx
|
||||
infos.txt
|
||||
infos2.txt
|
||||
```
|
||||
|
||||
`infos.txt` und `infos2.txt` wurden gelesen. Sie enthalten den bisherigen Chat-/Projektverlauf zum HR-KPI-Dashboard.
|
||||
|
||||
## Relevante Exportdateien in C:\temp
|
||||
|
||||
In `C:\temp` wurden relevante SAP- und Rexx-Dateien gefunden:
|
||||
|
||||
```text
|
||||
C:\temp\HR_KPI_EXPORT.xlsx
|
||||
C:\temp\Abwesenheitinstunden.xlsx
|
||||
C:\temp\Exportkommengehen.xlsx
|
||||
C:\temp\Saldiperstichdatum.xlsx
|
||||
C:\temp\Saldistundenferien.xlsx
|
||||
C:\temp\Personalausgeschieden.xlsx
|
||||
```
|
||||
|
||||
Wichtige Beobachtung: `HR_KPI_EXPORT.xlsx` wirkt wie ein SAP-Live-Export. Die ersten gelesenen Daten waren fuer `Geschäftsjahr = 2026` und `Buchungsperiode = 4`, also April 2026. Fuer den von HR gewuenschten Test Q1/2026 muessen vermutlich SAP-Daten fuer Perioden 1, 2 und 3 exportiert bzw. bereitgestellt werden.
|
||||
|
||||
## Vorhandene Formelmappe
|
||||
|
||||
Datei:
|
||||
|
||||
```text
|
||||
C:\Users\koi\source\repos\Ai\Powerbi\HR_KPI_Formeln_CH.xlsx
|
||||
```
|
||||
|
||||
Sheets:
|
||||
|
||||
```text
|
||||
KPI_Formeln
|
||||
Beispielrechnung
|
||||
PowerBI_Mapping
|
||||
```
|
||||
|
||||
Diese Datei ist aktuell eine Formel-/Beispielmappe, keine Live-Datendatei.
|
||||
|
||||
### Inhalt KPI_Formeln
|
||||
|
||||
Enthaelt mathematische Definitionen fuer:
|
||||
|
||||
- Krankheitstage je Mitarbeitenden
|
||||
- Krankheitstage gesamt
|
||||
- Krankheitsquote %
|
||||
- Unfallquote %
|
||||
- Gesundheitsbedingte Absenzen %
|
||||
- Soll-Arbeitszeit %
|
||||
- FTE
|
||||
- allgemeine Prozentanteile
|
||||
|
||||
Wichtige Formelidee:
|
||||
|
||||
```text
|
||||
Krankheitstage je Mitarbeitenden = Krankheitsstunden / persoenliche Sollzeit pro Tag
|
||||
Krankheitsquote % = Summe Krankheitsstunden / Summe Soll-Arbeitsstunden * 100
|
||||
Unfallquote % = (BU-Stunden + NBU-Stunden) / Summe Soll-Arbeitsstunden * 100
|
||||
Gesundheitsbedingte Absenzen % = (Krankheit + BU + NBU) / Sollstunden * 100
|
||||
```
|
||||
|
||||
### Inhalt Beispielrechnung
|
||||
|
||||
Enthaelt Beispielwerte fuer drei Mitarbeitende:
|
||||
|
||||
- Vollzeit
|
||||
- Teilzeit 50%
|
||||
- 80%
|
||||
|
||||
Diese Beispielrechnung dient zum Validieren der Formellogik.
|
||||
|
||||
### Inhalt PowerBI_Mapping
|
||||
|
||||
Enthaelt Mapping von KPI auf Power-BI-Felder/Measures, z.B.:
|
||||
|
||||
- `Rexx_Absenzen[Krankheit_Gesamt_Std]`
|
||||
- `M_Krankheitstage_Gesamt`
|
||||
- `M_Gesundheitsbedingte_Absenzen_Prozent`
|
||||
- `HR_KPI_Daten[FTE]`
|
||||
|
||||
## Architekturentscheidung
|
||||
|
||||
SAP und Rexx werden nicht in SAP zusammengefuehrt.
|
||||
|
||||
Stattdessen:
|
||||
|
||||
```text
|
||||
SAP CSV/XLSX + mehrere Rexx XLSX/CSV + manuelle Excel-Dateien
|
||||
werden in Power BI ueber Personalnummer/PERNR zusammengefuehrt.
|
||||
```
|
||||
|
||||
### SAP-Report
|
||||
|
||||
Der SAP-Report `Z_HR_KPI_CONSOLIDATE` liefert nur SAP-Daten:
|
||||
|
||||
- Stammdaten
|
||||
- Organisation/Kostenstelle
|
||||
- Beschaeftigungsgrad/FTE
|
||||
- Ein-/Austritt
|
||||
- Lohn
|
||||
- SAP-Abwesenheiten
|
||||
- Stellenplan
|
||||
- CSV-/Excel-Export fuer Power BI
|
||||
|
||||
Rexx-Daten werden nicht aus SAP extrahiert. Rexx-Felder im SAP-Report sind nur Platzhalter bzw. sollten langfristig eher separat aus Rexx kommen.
|
||||
|
||||
### Rexx
|
||||
|
||||
Rexx kann nicht alles in einem Export liefern, da pro Export maximal ca. 40 Felder moeglich sind. Daher werden thematische Exports benoetigt:
|
||||
|
||||
- Abwesenheiten/Krankheit
|
||||
- Ferien
|
||||
- GLZ/Salden/Ueberstunden
|
||||
- Austritte/Fluktuation
|
||||
- Pulsumfrage/Zufriedenheit
|
||||
- ggf. weitere Themen
|
||||
|
||||
## Gelesene Spalten aus den Live-/Exportdateien
|
||||
|
||||
### C:\temp\HR_KPI_EXPORT.xlsx
|
||||
|
||||
Sheet:
|
||||
|
||||
```text
|
||||
Data
|
||||
```
|
||||
|
||||
Dimension:
|
||||
|
||||
```text
|
||||
A1:AK1139
|
||||
```
|
||||
|
||||
Relevante Spalten aus Zeile 1:
|
||||
|
||||
```text
|
||||
Personalnummer
|
||||
Geschäftsjahr
|
||||
Buchungsperiode
|
||||
Buchungskreis
|
||||
Personalbereich
|
||||
Personalteilbereich
|
||||
Kostenstelle
|
||||
Organisationseinheit
|
||||
Planstelle
|
||||
Stellenschlüssel
|
||||
Mitarbeiterkreis
|
||||
Abrechnungskreis
|
||||
Teilzeitkraft
|
||||
Mitarbeitergruppe
|
||||
Beschäftigungsgrad %
|
||||
Vorname
|
||||
Nachname
|
||||
Geschlecht
|
||||
Geburtsdatum
|
||||
Datum
|
||||
Datum
|
||||
Bruttolohn Monat
|
||||
Krankheitstage gesamt
|
||||
Krankheit < 60 Tage
|
||||
Krankheit >= 60 Tage (LZK)
|
||||
Nichtberufsunfall Tage
|
||||
Berufsunfall Tage
|
||||
Soll-Stelle vorhanden
|
||||
Pulsumfrage Score (Rexx)
|
||||
MA-Zufriedenheit (Rexx)
|
||||
Kununu Score
|
||||
Angelegt am
|
||||
Uhrzeit
|
||||
Angelegt von
|
||||
```
|
||||
|
||||
Beispiel aus den ersten Datenzeilen:
|
||||
|
||||
```text
|
||||
Personalnummer 2005, Geschäftsjahr 2026, Buchungsperiode 4
|
||||
Personalnummer 2010, Geschäftsjahr 2026, Buchungsperiode 4
|
||||
Personalnummer 2012, Geschäftsjahr 2026, Buchungsperiode 4
|
||||
```
|
||||
|
||||
Hinweis: Fuer Q1/2026 brauchen wir Perioden 1, 2, 3 oder einen Q1-Gesamtexport.
|
||||
|
||||
### C:\temp\Abwesenheitinstunden.xlsx
|
||||
|
||||
Sheet:
|
||||
|
||||
```text
|
||||
Abwesenheit in Stunden
|
||||
```
|
||||
|
||||
Dimension:
|
||||
|
||||
```text
|
||||
A1:Z253
|
||||
```
|
||||
|
||||
Spalten:
|
||||
|
||||
```text
|
||||
Personalnummer
|
||||
Foto rund
|
||||
Nachname, Vorname (Link Personal)
|
||||
Stelle
|
||||
Organisation
|
||||
Leitung j/n
|
||||
Eintrittsdatum
|
||||
Personal Status
|
||||
Krank nicht buchbar angetreten (Stunden Ind.)
|
||||
Krankheit angetreten (Stunden Ind.)
|
||||
Krank nicht buchbar angetreten (Stunden)
|
||||
Krankheit angetreten (Stunden)
|
||||
Krank nicht buchbar angetreten (Zeitraum)
|
||||
Krankheit angetreten (Zeitraum)
|
||||
Krank nicht buchbar ausstehend (Stunden Ind.)
|
||||
Krankheit ausstehend (Stunden Ind.)
|
||||
Krank nicht buchbar gebucht gesamt (Stunden)
|
||||
Krankheit genehmigt (Zeitraum)
|
||||
Krank nicht buchbar genehmigt (Zeitraum)
|
||||
Krankheit genehmigt (Stunden)
|
||||
Krank nicht buchbar genehmigt (Stunden)
|
||||
Krankheit genehmigt (Stunden Ind.)
|
||||
Krank nicht buchbar genehmigt (Stunden Ind.)
|
||||
Krankheit gebucht gesamt (Stunden)
|
||||
Krankheit gebucht gesamt (Stunden Ind.)
|
||||
Krank nicht buchbar gebucht gesamt (Stunden Ind.)
|
||||
```
|
||||
|
||||
Wichtig: Rexx liefert Krankheit in Stunden. Laut Projektentscheidung ist Rexx fuer Krankheit/Absenzen fuehrend, weil SAP nur Tage liefert.
|
||||
|
||||
### C:\temp\Exportkommengehen.xlsx
|
||||
|
||||
Sheet:
|
||||
|
||||
```text
|
||||
Export KOMMENGEHEN
|
||||
```
|
||||
|
||||
Dimension:
|
||||
|
||||
```text
|
||||
A1:O253
|
||||
```
|
||||
|
||||
Spalten:
|
||||
|
||||
```text
|
||||
Sozialversicherungsnummer
|
||||
Nachname, Vorname (Link Personal)
|
||||
Geburtsdatum
|
||||
Personal Status
|
||||
Stelle
|
||||
Organisation
|
||||
Arbeitszeitmodell
|
||||
Arbeitszeit
|
||||
Ø tägliche Sollarbeitszeit (Woche)
|
||||
Arbeitszeit Mo.
|
||||
Arbeitszeit Di.
|
||||
Arbeitszeit Mi.
|
||||
Arbeitszeit Do.
|
||||
Arbeitszeit Fr.
|
||||
Arbeitszeit Sa.
|
||||
```
|
||||
|
||||
Wichtig: Dieser Export enthaelt keine Personalnummer, sondern Sozialversicherungsnummer und Namen. Fuer stabile Power-BI-Joins ist Personalnummer besser. Falls moeglich, Rexx-Export um `Personalnummer` ergaenzen.
|
||||
|
||||
### C:\temp\Saldiperstichdatum.xlsx
|
||||
|
||||
Sheet:
|
||||
|
||||
```text
|
||||
Saldi per Stichdatum
|
||||
```
|
||||
|
||||
Dimension:
|
||||
|
||||
```text
|
||||
A1:Q253
|
||||
```
|
||||
|
||||
Spalten:
|
||||
|
||||
```text
|
||||
Personalnummer
|
||||
Kürzel
|
||||
Nachname, Vorname (Link Personal)
|
||||
Stelle
|
||||
Organisation
|
||||
Kostenstelle
|
||||
Leitung j/n
|
||||
Eintrittsdatum
|
||||
Personal Status
|
||||
Anstellungsverhältnis
|
||||
Stunden Saldo
|
||||
Urlaubsanspruch
|
||||
Urlaub Rest
|
||||
Ferien ausstehend (Tage)
|
||||
Lohnart
|
||||
Lohn
|
||||
Lohn Währung
|
||||
```
|
||||
|
||||
Dieser Export ist wichtig fuer:
|
||||
|
||||
- GLZ/Stunden Saldo
|
||||
- Ferienanspruch
|
||||
- Urlaub Rest
|
||||
- Ferien ausstehend
|
||||
- ggf. Lohn aus Rexx, wobei SAP fuer Lohn fuehrend/sensibler sein sollte
|
||||
|
||||
## Fachliche Entscheidungen aus den TXT-Dateien
|
||||
|
||||
- HR soll das Dashboard konsumieren, nicht bauen.
|
||||
- Phase 1 ist Zeit und Absenzen.
|
||||
- Rexx ist fuer Krankheit/Absenzen fuehrend.
|
||||
- SAP liefert ergaenzende Basisdaten, die Rexx nicht sauber oder nicht vollstaendig liefert.
|
||||
- Kununu wird manuell gepflegt.
|
||||
- Refline/Time-to-hire ist spaeteres Thema.
|
||||
- Rexx Scheduled Reports wurden gesucht, aber offenbar nicht gefunden/freigeschaltet.
|
||||
- Manuelles Ablegen der Exports in einem Ordner ist fuer den Start akzeptabel.
|
||||
|
||||
## Welche Daten nur aus SAP kommen
|
||||
|
||||
Diese Felder sind wichtig und kommen eher aus SAP als aus Rexx:
|
||||
|
||||
- Geschlecht
|
||||
- Beschaeftigungsgrad %
|
||||
- FTE-Grundlage
|
||||
- Mitarbeitergruppe/Mitarbeiterkreis
|
||||
- Planstelle/Stellenschluessel
|
||||
- Soll-Stelle vorhanden
|
||||
- Bruttolohn Monat
|
||||
- SAP-Abwesenheiten fuer Vergleich
|
||||
- BU-/NBU-Tage, falls nicht in Rexx als Stunden vorhanden
|
||||
|
||||
## Welche Daten aus Rexx kommen
|
||||
|
||||
Rexx ist wichtig fuer:
|
||||
|
||||
- Krankheit in Stunden
|
||||
- Krankheit genehmigt/gebucht/angetreten/ausstehend
|
||||
- GLZ/Stunden Saldo
|
||||
- Ferienanspruch
|
||||
- Urlaub Rest
|
||||
- Ferien ausstehend
|
||||
- Organisation/Kostenstelle aus Rexx fuer operative Sicht
|
||||
- ggf. Pulsumfrage/Zufriedenheit
|
||||
|
||||
## Q1/2026-Test mit Live-Daten
|
||||
|
||||
HR-Anfrage:
|
||||
|
||||
> Waere es moeglich, dass wir mit den Live Daten das Q1/2026 testen koennten? Dann koennten wir die Quoten mit jenen aus dem SAP vergleichen um zu pruefen, ob wir die identischen Formeln haben.
|
||||
|
||||
Interpretation:
|
||||
|
||||
Es soll mit Live-Daten fuer Q1/2026 gerechnet werden:
|
||||
|
||||
```text
|
||||
Januar 2026
|
||||
Februar 2026
|
||||
Maerz 2026
|
||||
```
|
||||
|
||||
Dann sollen die berechneten Quoten gegen SAP verglichen werden.
|
||||
|
||||
### Noetige Daten fuer Q1/2026
|
||||
|
||||
Aus SAP:
|
||||
|
||||
- Export fuer Perioden 1, 2, 3 im Jahr 2026
|
||||
- oder ein Export, der Q1 kumuliert enthaelt
|
||||
|
||||
Aus Rexx:
|
||||
|
||||
- Abwesenheit/Krankheit mit Zeitraum Q1/2026
|
||||
- Sollzeit pro Mitarbeitenden fuer Q1/2026
|
||||
- optional BU/NBU, falls Rexx das liefert
|
||||
- GLZ/Ferien nur falls fuer den Q1-Vergleich benoetigt
|
||||
|
||||
### Kritischer Punkt
|
||||
|
||||
Die aktuell gefundene SAP-Datei `C:\temp\HR_KPI_EXPORT.xlsx` zeigt in den ersten Datenzeilen Periode 4/2026. Das reicht nicht fuer Q1/2026.
|
||||
|
||||
Naechster sinnvoller Schritt:
|
||||
|
||||
1. SAP-Report fuer Q1/2026 laufen lassen, also Perioden 1 bis 3 oder Q1-Gesamt.
|
||||
2. Rexx-Abwesenheitsexport ebenfalls fuer Q1/2026 ziehen.
|
||||
3. Power-BI-/Excel-Testdatei erstellen, die beide Quellen ueber Personalnummer verbindet.
|
||||
4. Quoten berechnen:
|
||||
- Krankheitsquote %
|
||||
- Unfallquote %
|
||||
- Gesundheitsbedingte Absenzen %
|
||||
- Krankheitstage gesamt
|
||||
5. Ergebnis mit SAP-Quote vergleichen.
|
||||
|
||||
## Technische Hinweise aus dieser Session
|
||||
|
||||
Excel-Dateien wurden nicht ueber Excel-GUI bearbeitet, sondern als XLSX-Zip/XML gelesen. Das vermeidet Dateisperren.
|
||||
|
||||
Ein Versuch mit Excel-COM hing und wurde abgebrochen. Danach wurde ein unsichtbarer Excel-Prozess geprueft. Beim Beenden war der Prozess bereits nicht mehr vorhanden. Es wurden keine Excel-Dateien gespeichert oder veraendert.
|
||||
|
||||
In dieser Session wurde bis zur Erstellung dieser Handoff-Datei keine vorhandene Excel-Datei manipuliert.
|
||||
|
||||
## Empfohlene Antwort an HR
|
||||
|
||||
Vorschlag:
|
||||
|
||||
```text
|
||||
Ja, das koennen wir machen. Fuer den Q1/2026-Test brauche ich die Live-Exports fuer Januar bis Maerz 2026 bzw. einen Q1-Gesamtexport aus SAP sowie den passenden Rexx-Abwesenheitsexport fuer denselben Zeitraum. Dann berechne ich die Quoten mit unserer Formel und stelle sie den SAP-Werten gegenueber, damit wir Abweichungen in Definition oder Zeitraum sofort sehen.
|
||||
```
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
- SAP-Q1/2026-Datei fehlt noch oder wurde noch nicht gefunden.
|
||||
- Rexx-Q1/2026-Abwesenheitsexport muss bereitgestellt werden.
|
||||
- Klaeren, ob `Exportkommengehen.xlsx` kuenftig Personalnummer enthalten kann. Ohne Personalnummer ist der Join unsauber.
|
||||
- Klaeren, ob BU/NBU in Rexx als Stunden verfuegbar ist oder fuer Unfallquote weiter aus SAP-Tagen umgerechnet werden muss.
|
||||
- Definieren, ob Q1-Quote mit Sollstunden aus Kalender/SAP oder Rexx-Sollarbeitszeit gerechnet wird.
|
||||
- Sicherstellen, dass alle Dateien denselben Zeitraum abdecken.
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// ============================================================
|
||||
// QUERY 2: Rexx_Absenzen
|
||||
// ============================================================
|
||||
// Quelle: Abwesenheitinstunden.xlsx (Rexx #744)
|
||||
// Krankheitsstunden Detail → fuehrend statt SAP-Tage
|
||||
// ============================================================
|
||||
|
||||
let
|
||||
Source = Excel.Workbook(File.Contents("C:\temp\Abwesenheitinstunden.xlsx"), null, true),
|
||||
Data = Source{0}[Data],
|
||||
Head = Table.PromoteHeaders(Data, [PromoteAllScalars = true]),
|
||||
|
||||
Sel = Table.SelectColumns(Head, {
|
||||
"Personalnummer",
|
||||
"Nachname, Vorname (Link Personal)",
|
||||
"Stelle", "Organisation", "Leitung j/n", "Personal Status",
|
||||
"Krankheit angetreten (Stunden Ind.)",
|
||||
"Krank nicht buchbar angetreten (Stunden Ind.)",
|
||||
"Krankheit angetreten (Zeitraum)",
|
||||
"Krank nicht buchbar angetreten (Zeitraum)",
|
||||
"Krankheit genehmigt (Stunden Ind.)",
|
||||
"Krank nicht buchbar genehmigt (Stunden Ind.)",
|
||||
"Krankheit gebucht gesamt (Stunden Ind.)",
|
||||
"Krank nicht buchbar gebucht gesamt (Stunden Ind.)"
|
||||
}),
|
||||
|
||||
Ren = Table.RenameColumns(Sel, {
|
||||
{"Personalnummer", "PERNR_Text"},
|
||||
{"Nachname, Vorname (Link Personal)", "Name"},
|
||||
{"Stelle", "Stelle"},
|
||||
{"Organisation", "Organisation"},
|
||||
{"Leitung j/n", "Leitung"},
|
||||
{"Personal Status", "Status"},
|
||||
{"Krankheit angetreten (Stunden Ind.)", "Krankheit_Kurz_Std"},
|
||||
{"Krank nicht buchbar angetreten (Stunden Ind.)", "Krankheit_Lang_Std"},
|
||||
{"Krankheit angetreten (Zeitraum)", "Krankheit_Kurz_Zeitraum"},
|
||||
{"Krank nicht buchbar angetreten (Zeitraum)", "Krankheit_Lang_Zeitraum"},
|
||||
{"Krankheit genehmigt (Stunden Ind.)", "Krankheit_Kurz_Genehmigt"},
|
||||
{"Krank nicht buchbar genehmigt (Stunden Ind.)", "Krankheit_Lang_Genehmigt"},
|
||||
{"Krankheit gebucht gesamt (Stunden Ind.)", "Krankheit_Kurz_Gebucht"},
|
||||
{"Krank nicht buchbar gebucht gesamt (Stunden Ind.)", "Krankheit_Lang_Gebucht"}
|
||||
}),
|
||||
|
||||
AddPernr = Table.AddColumn(Ren, "Personalnummer", each
|
||||
try Number.FromText(Text.Trim(Text.From([PERNR_Text]))) otherwise null, Int64.Type),
|
||||
|
||||
SetTypes = Table.TransformColumnTypes(AddPernr, {
|
||||
{"Krankheit_Kurz_Std", type number},
|
||||
{"Krankheit_Lang_Std", type number},
|
||||
{"Krankheit_Kurz_Genehmigt", type number},
|
||||
{"Krankheit_Lang_Genehmigt", type number},
|
||||
{"Krankheit_Kurz_Gebucht", type number},
|
||||
{"Krankheit_Lang_Gebucht", type number}
|
||||
}),
|
||||
|
||||
// Gesamt-Krankheitsstunden
|
||||
AddGes = Table.AddColumn(SetTypes, "Krankheit_Gesamt_Std", each
|
||||
(if [Krankheit_Kurz_Std] = null then 0 else [Krankheit_Kurz_Std]) +
|
||||
(if [Krankheit_Lang_Std] = null then 0 else [Krankheit_Lang_Std]),
|
||||
type number),
|
||||
|
||||
// Krankheitstage (Stunden / 8.4h pro Tag)
|
||||
AddKTG = Table.AddColumn(AddGes, "Krankheitstage_Gesamt", each
|
||||
Number.Round([Krankheit_Gesamt_Std] / 8.4, 1), type number),
|
||||
AddKTK = Table.AddColumn(AddKTG, "Krankheitstage_Kurz", each
|
||||
let s = if [Krankheit_Kurz_Std] = null then 0 else [Krankheit_Kurz_Std] in
|
||||
Number.Round(s / 8.4, 1), type number),
|
||||
AddKTL = Table.AddColumn(AddKTK, "Krankheitstage_Lang", each
|
||||
let s = if [Krankheit_Lang_Std] = null then 0 else [Krankheit_Lang_Std] in
|
||||
Number.Round(s / 8.4, 1), type number),
|
||||
|
||||
// Krankenquote (kompatibel, Basis 21 Tage)
|
||||
AddKQ = Table.AddColumn(AddKTL, "Krankenquote_MA", each
|
||||
if [Krankheitstage_Gesamt] = 0 then 0 else [Krankheitstage_Gesamt] / 21, type number),
|
||||
AddKQO = Table.AddColumn(AddKQ, "Krankenquote_ohne_LZK", each
|
||||
if [Krankheitstage_Kurz] = 0 then 0 else [Krankheitstage_Kurz] / 21, type number),
|
||||
|
||||
AddAbs = Table.AddColumn(AddKQO, "Absenztage_Total", each [Krankheitstage_Gesamt], type number),
|
||||
|
||||
Filter = Table.SelectRows(AddAbs, each [Status] = "Aktiv"),
|
||||
Clean = Table.RemoveColumns(Filter, {"PERNR_Text"}),
|
||||
Reorder = Table.ReorderColumns(Clean, {"Personalnummer"} &
|
||||
List.RemoveItems(Table.ColumnNames(Clean), {"Personalnummer"}))
|
||||
in
|
||||
Reorder
|
||||
@@ -0,0 +1,5 @@
|
||||
Datenbeschaffung:
|
||||
|
||||
SAP Daten kommen von Programm Z_HR_KPI_CONS
|
||||
|
||||
Rexx Daten aus Rexx export mit Exportprofil SAP_EXPORT(#711) exporieren nach Excel erstell sapexport.xlss
|
||||
@@ -0,0 +1,198 @@
|
||||
// ============================================================
|
||||
// DAX MEASURES: Fluktuation gemaess formeln.docx
|
||||
// ============================================================
|
||||
// Voraussetzung:
|
||||
// - Query/Tabelle: HR_KPI_DATEN_SAP mit aktiven Mitarbeitenden
|
||||
// - Query/Tabelle: Rexx_Ausgeschieden aus rexx_ausgeschieden.txt
|
||||
// - Rexx_Ausgeschieden[Ist_Fluktuationsrelevant] filtert:
|
||||
// nur Arbeitnehmerkuendigungen, keine Aushilfen/Praktikanten/
|
||||
// Werkstudenten/Lehrlinge, keine Pensionierungen, keine befristeten
|
||||
// Vertraege, keine Kuendigungen durch Trafag.
|
||||
//
|
||||
// Hinweis:
|
||||
// HR_KPI_DATEN_SAP ist aktuell eine Stichtags-/Monatstabelle. Falls spaeter
|
||||
// echte Monats-Snapshots geladen werden, funktionieren die Durchschnitts-
|
||||
// Headcount-Measures ueber Monat/Quartal/Jahr genauer.
|
||||
// ============================================================
|
||||
|
||||
Headcount Festangestellt =
|
||||
COALESCE(
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(HR_KPI_DATEN_SAP[Personalnummer]),
|
||||
HR_KPI_DATEN_SAP[Ist_Aktiv] = TRUE(),
|
||||
HR_KPI_DATEN_SAP[Mitarbeitertyp] = "Festangestellt"
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Headcount Aktiv Total =
|
||||
COALESCE(
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(HR_KPI_DATEN_SAP[Personalnummer]),
|
||||
HR_KPI_DATEN_SAP[Ist_Aktiv] = TRUE()
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Austritte Total Rexx =
|
||||
COALESCE(DISTINCTCOUNT(Rexx_Ausgeschieden[Personalnummer]), 0)
|
||||
|
||||
Austritte Arbeitnehmerkuendigung =
|
||||
COALESCE(
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(Rexx_Ausgeschieden[Personalnummer]),
|
||||
Rexx_Ausgeschieden[Ist_Arbeitnehmerkuendigung] = TRUE()
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Austritte Fluktuationsrelevant =
|
||||
VAR Perioden = VALUES(HR_KPI_DATEN_SAP[Periode])
|
||||
VAR Basis =
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(Rexx_Ausgeschieden[Personalnummer]),
|
||||
Rexx_Ausgeschieden[Ist_Fluktuationsrelevant] = TRUE()
|
||||
)
|
||||
VAR NachPeriode =
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(Rexx_Ausgeschieden[Personalnummer]),
|
||||
Rexx_Ausgeschieden[Ist_Fluktuationsrelevant] = TRUE(),
|
||||
TREATAS(Perioden, Rexx_Ausgeschieden[Austrittsmonat])
|
||||
)
|
||||
RETURN
|
||||
COALESCE(
|
||||
IF(
|
||||
ISFILTERED(HR_KPI_DATEN_SAP[Periode]) || ISINSCOPE(HR_KPI_DATEN_SAP[Periode]),
|
||||
NachPeriode,
|
||||
Basis
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Austritte Nicht Fluktuationsrelevant =
|
||||
COALESCE(
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(Rexx_Ausgeschieden[Personalnummer]),
|
||||
Rexx_Ausgeschieden[Ist_Fluktuationsrelevant] = FALSE()
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Fluktuation Monat % =
|
||||
COALESCE(
|
||||
DIVIDE(
|
||||
[Austritte Fluktuationsrelevant],
|
||||
[Headcount Festangestellt]
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Avg Headcount Quartal =
|
||||
COALESCE(
|
||||
AVERAGEX(
|
||||
VALUES(HR_KPI_DATEN_SAP[Periode]),
|
||||
[Headcount Festangestellt]
|
||||
),
|
||||
[Headcount Festangestellt],
|
||||
0
|
||||
)
|
||||
|
||||
Austritte Quartal =
|
||||
VAR HatPeriodenfilter =
|
||||
ISFILTERED(HR_KPI_DATEN_SAP[Periode]) ||
|
||||
ISFILTERED(HR_KPI_DATEN_SAP[Jahr]) ||
|
||||
ISFILTERED(HR_KPI_DATEN_SAP[Monat])
|
||||
VAR Auswertungsdatum =
|
||||
IF(
|
||||
HatPeriodenfilter,
|
||||
MAX(HR_KPI_DATEN_SAP[Periode]),
|
||||
MAX(Rexx_Ausgeschieden[Austrittsmonat])
|
||||
)
|
||||
VAR QuartalsStart =
|
||||
DATE(
|
||||
YEAR(Auswertungsdatum),
|
||||
1 + 3 * QUOTIENT(MONTH(Auswertungsdatum) - 1, 3),
|
||||
1
|
||||
)
|
||||
VAR QuartalsEnde = EOMONTH(QuartalsStart, 2)
|
||||
RETURN
|
||||
COALESCE(
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(Rexx_Ausgeschieden[Personalnummer]),
|
||||
Rexx_Ausgeschieden[Ist_Fluktuationsrelevant] = TRUE(),
|
||||
FILTER(
|
||||
ALL(Rexx_Ausgeschieden[Austrittsmonat]),
|
||||
Rexx_Ausgeschieden[Austrittsmonat] >= QuartalsStart &&
|
||||
Rexx_Ausgeschieden[Austrittsmonat] <= QuartalsEnde
|
||||
)
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Fluktuation Quartal % =
|
||||
COALESCE(
|
||||
DIVIDE(
|
||||
[Austritte Quartal],
|
||||
[Avg Headcount Quartal]
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Fluktuation Hochrechnung Jahr % =
|
||||
COALESCE([Fluktuation Quartal %] * 4, 0)
|
||||
|
||||
Avg Headcount Jahr =
|
||||
COALESCE(
|
||||
AVERAGEX(
|
||||
VALUES(HR_KPI_DATEN_SAP[Periode]),
|
||||
[Headcount Festangestellt]
|
||||
),
|
||||
[Headcount Festangestellt],
|
||||
0
|
||||
)
|
||||
|
||||
Austritte Jahr =
|
||||
VAR HatPeriodenfilter =
|
||||
ISFILTERED(HR_KPI_DATEN_SAP[Periode]) ||
|
||||
ISFILTERED(HR_KPI_DATEN_SAP[Jahr]) ||
|
||||
ISFILTERED(HR_KPI_DATEN_SAP[Monat])
|
||||
VAR Auswertungsdatum =
|
||||
IF(
|
||||
HatPeriodenfilter,
|
||||
MAX(HR_KPI_DATEN_SAP[Periode]),
|
||||
MAX(Rexx_Ausgeschieden[Austrittsmonat])
|
||||
)
|
||||
VAR Auswertungsjahr = YEAR(Auswertungsdatum)
|
||||
RETURN
|
||||
COALESCE(
|
||||
CALCULATE(
|
||||
DISTINCTCOUNT(Rexx_Ausgeschieden[Personalnummer]),
|
||||
Rexx_Ausgeschieden[Ist_Fluktuationsrelevant] = TRUE(),
|
||||
FILTER(
|
||||
ALL(Rexx_Ausgeschieden[Austrittsmonat]),
|
||||
YEAR(Rexx_Ausgeschieden[Austrittsmonat]) = Auswertungsjahr
|
||||
)
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
Fluktuation Jahr Effektiv % =
|
||||
COALESCE(
|
||||
DIVIDE(
|
||||
[Austritte Jahr],
|
||||
[Avg Headcount Jahr]
|
||||
),
|
||||
0
|
||||
)
|
||||
|
||||
BU_Tage_Total =
|
||||
COALESCE(SUM(HR_KPI_DATEN_SAP[BU_Tage]), 0)
|
||||
|
||||
NBU_Tage_Total =
|
||||
COALESCE(SUM(HR_KPI_DATEN_SAP[NBU_Tage]), 0)
|
||||
|
||||
Unfalltage Total =
|
||||
[BU_Tage_Total] + [NBU_Tage_Total]
|
||||
|
||||
Fluktuation Ausschlussgrund Anzahl =
|
||||
COUNTROWS(Rexx_Ausgeschieden)
|
||||
Binary file not shown.
@@ -0,0 +1,304 @@
|
||||
let
|
||||
// ===== REXX #757 LADEN =====
|
||||
Src757 = Excel.Workbook(File.Contents("C:\temp\Saldiperstichdatum.xlsx"), null, true),
|
||||
Data757 = Src757{0}[Data],
|
||||
Head757 = Table.PromoteHeaders(Data757, [PromoteAllScalars = true]),
|
||||
|
||||
Ren757 = Table.RenameColumns(Head757, {
|
||||
{"Personalnummer", "Personalnummer"},
|
||||
{"Kürzel", "Kuerzel"},
|
||||
{"Nachname, Vorname (Link Personal)", "Name_Rexx"},
|
||||
{"Stelle", "Stelle_Rexx"},
|
||||
{"Organisation", "Organisation_Text"},
|
||||
{"Kostenstelle", "Kostenstelle_Rexx"},
|
||||
{"Leitung j/n", "Leitung"},
|
||||
{"Eintrittsdatum", "Eintrittsdatum_Raw"},
|
||||
{"Personal Status", "Personal_Status"},
|
||||
{"Anstellungsverhältnis", "Anstellungsverhaeltnis"},
|
||||
{"Stunden Saldo", "Stunden_Saldo_Raw"},
|
||||
{"Urlaubsanspruch", "Urlaubsanspruch_Raw"},
|
||||
{"Urlaub Rest", "Urlaub_Rest_Raw"},
|
||||
{"Ferien ausstehend (Tage)", "Ferien_Ausstehend_Raw"},
|
||||
{"Lohnart", "Lohnart"},
|
||||
{"Lohn", "Lohn_Raw"},
|
||||
{"Lohn Währung", "Lohn_Waehrung"}
|
||||
}),
|
||||
|
||||
TypePernr = Table.TransformColumnTypes(Ren757, {{"Personalnummer", Int64.Type}}),
|
||||
|
||||
AddKey = Table.AddColumn(TypePernr, "PERNR_Key", each Text.From([Personalnummer]), type text),
|
||||
|
||||
// Name_Voll früh erzeugen für Name-Join mit #732
|
||||
AddNameVollEarly = Table.AddColumn(AddKey, "Name_Voll", each
|
||||
Text.From(if [Name_Rexx] = null then "" else [Name_Rexx]), type text),
|
||||
|
||||
// ===== REXX #732 LADEN (Geburtsdatum + AZ-Modell) =====
|
||||
// Hat keine Personalnummer → Join über Name
|
||||
Tbl732 = try
|
||||
let
|
||||
S = Excel.Workbook(File.Contents("C:\temp\Exportkommengehen.xlsx"), null, true),
|
||||
D = S{0}[Data],
|
||||
H = Table.PromoteHeaders(D, [PromoteAllScalars = true]),
|
||||
Sel = Table.SelectColumns(H, {
|
||||
"Nachname, Vorname (Link Personal)",
|
||||
"Geburtsdatum",
|
||||
"Arbeitszeitmodell",
|
||||
"Ø tägliche Sollarbeitszeit (Woche)"
|
||||
}, MissingField.UseNull),
|
||||
Ren = Table.RenameColumns(Sel, {
|
||||
{"Nachname, Vorname (Link Personal)", "Name_732"},
|
||||
{"Geburtsdatum", "Geburtsdatum_Raw"},
|
||||
{"Arbeitszeitmodell", "Arbeitszeitmodell"},
|
||||
{"Ø tägliche Sollarbeitszeit (Woche)", "Avg_Sollzeit_Tag_Raw"}
|
||||
})
|
||||
in Ren
|
||||
otherwise null,
|
||||
|
||||
// Merge #757 + #732 ueber Name
|
||||
Merged732 = if Tbl732 <> null then
|
||||
let
|
||||
M = Table.NestedJoin(AddNameVollEarly, {"Name_Voll"}, Tbl732, {"Name_732"}, "R732", JoinKind.LeftOuter),
|
||||
E = Table.ExpandTableColumn(M, "R732", {"Geburtsdatum_Raw", "Arbeitszeitmodell", "Avg_Sollzeit_Tag_Raw"})
|
||||
in E
|
||||
else
|
||||
let
|
||||
A1 = Table.AddColumn(AddNameVollEarly, "Geburtsdatum_Raw", each null),
|
||||
A2 = Table.AddColumn(A1, "Arbeitszeitmodell", each null, type text),
|
||||
A3 = Table.AddColumn(A2, "Avg_Sollzeit_Tag_Raw", each null)
|
||||
in A3,
|
||||
|
||||
// ===== SAP LADEN (HR_KPI_Export.xlsx) =====
|
||||
TblSAP = try
|
||||
let
|
||||
S = Excel.Workbook(File.Contents("C:\temp\HR_KPI_Export.xlsx"), null, true),
|
||||
D = S{0}[Data],
|
||||
H = Table.PromoteHeaders(D, [PromoteAllScalars = true]),
|
||||
Sel = Table.SelectColumns(H, {
|
||||
"Personalnummer", "Buchungskreis", "Personalbereich", "Personalteilbereich",
|
||||
"Mitarbeitergruppe", "Mitarbeiterkreis", "Teilzeitkraft",
|
||||
"Beschäftigungsgrad %", "Geschlecht", "Planstelle", "Stellenschlüssel",
|
||||
"Nichtberufsunfall Tage", "Berufsunfall Tage",
|
||||
"Abrechnungskreis"
|
||||
}, MissingField.UseNull),
|
||||
Ren = Table.RenameColumns(Sel, {
|
||||
{"Personalnummer", "PERNR_SAP"},
|
||||
{"Teilzeitkraft", "Teilzeitkennzeichen"},
|
||||
{"Beschäftigungsgrad %", "Beschaeftigungsgrad_Prozent"},
|
||||
{"Stellenschlüssel", "Soll_Stelle"},
|
||||
{"Nichtberufsunfall Tage", "NBU_Tage"},
|
||||
{"Berufsunfall Tage", "BU_Tage"}
|
||||
}, MissingField.Ignore),
|
||||
// PERNR-Key normalisiert (entfernt fuehrende Nullen)
|
||||
AddK = Table.AddColumn(Ren, "PERNR_SAP_Key", each
|
||||
try Text.From(Number.FromText(Text.Trim(Text.From([PERNR_SAP])))) otherwise null,
|
||||
type text)
|
||||
in AddK
|
||||
otherwise null,
|
||||
|
||||
// Merge + SAP
|
||||
MergedSAP = if TblSAP <> null then
|
||||
let
|
||||
M = Table.NestedJoin(Merged732, {"PERNR_Key"}, TblSAP, {"PERNR_SAP_Key"}, "SAP", JoinKind.LeftOuter),
|
||||
E = Table.ExpandTableColumn(M, "SAP", {
|
||||
"Buchungskreis", "Personalbereich", "Personalteilbereich",
|
||||
"Mitarbeitergruppe", "Mitarbeiterkreis", "Teilzeitkennzeichen",
|
||||
"Beschaeftigungsgrad_Prozent", "Geschlecht", "Planstelle", "Soll_Stelle",
|
||||
"NBU_Tage", "BU_Tage",
|
||||
"Abrechnungskreis"
|
||||
})
|
||||
in E
|
||||
else
|
||||
let
|
||||
A1 = Table.AddColumn(Merged732, "Buchungskreis", each null),
|
||||
A2 = Table.AddColumn(A1, "Personalbereich", each null),
|
||||
A3 = Table.AddColumn(A2, "Personalteilbereich", each null),
|
||||
A4 = Table.AddColumn(A3, "Mitarbeitergruppe", each null),
|
||||
A5 = Table.AddColumn(A4, "Mitarbeiterkreis", each null),
|
||||
A6 = Table.AddColumn(A5, "Teilzeitkennzeichen", each null, type text),
|
||||
A7 = Table.AddColumn(A6, "Beschaeftigungsgrad_Prozent", each null),
|
||||
A8 = Table.AddColumn(A7, "Geschlecht", each null),
|
||||
A9 = Table.AddColumn(A8, "Planstelle", each null),
|
||||
A10 = Table.AddColumn(A9, "Soll_Stelle", each null, type text),
|
||||
A11 = Table.AddColumn(A10, "NBU_Tage", each null),
|
||||
A12 = Table.AddColumn(A11, "BU_Tage", each null),
|
||||
A13 = Table.AddColumn(A12, "Abrechnungskreis", each null, type text)
|
||||
in A13,
|
||||
|
||||
// ===== BERECHNETE SPALTEN =====
|
||||
|
||||
// Eintrittsdatum
|
||||
AddEintritt = Table.AddColumn(MergedSAP, "Eintrittsdatum", each
|
||||
let raw = [Eintrittsdatum_Raw] in
|
||||
if raw = null then null
|
||||
else if raw is date then raw
|
||||
else if raw is datetime then Date.From(raw)
|
||||
else try Date.FromText(Text.From(raw), [Format = "dd.MM.yyyy"]) otherwise null,
|
||||
type date),
|
||||
|
||||
// Geburtsdatum
|
||||
AddGebdat = Table.AddColumn(AddEintritt, "Geburtsdatum", each
|
||||
let raw = [Geburtsdatum_Raw] in
|
||||
if raw = null then null
|
||||
else if raw is date then raw
|
||||
else if raw is datetime then Date.From(raw)
|
||||
else try Date.FromText(Text.From(raw), [Format = "dd.MM.yyyy"]) otherwise null,
|
||||
type date),
|
||||
|
||||
// Numerische Konvertierungen
|
||||
AddNum = Table.TransformColumnTypes(AddGebdat, {
|
||||
{"Urlaubsanspruch_Raw", type number},
|
||||
{"Urlaub_Rest_Raw", type number},
|
||||
{"Ferien_Ausstehend_Raw", type number},
|
||||
{"Lohn_Raw", type number},
|
||||
{"Avg_Sollzeit_Tag_Raw", type number},
|
||||
{"Beschaeftigungsgrad_Prozent", type number},
|
||||
{"Geschlecht", Int64.Type},
|
||||
{"NBU_Tage", type number},
|
||||
{"BU_Tage", type number}
|
||||
}),
|
||||
|
||||
RenNum = Table.RenameColumns(AddNum, {
|
||||
{"Urlaubsanspruch_Raw", "Urlaubsanspruch"},
|
||||
{"Urlaub_Rest_Raw", "Urlaub_Rest"},
|
||||
{"Ferien_Ausstehend_Raw", "Ferien_Ausstehend"},
|
||||
{"Lohn_Raw", "Bruttolohn"},
|
||||
{"Avg_Sollzeit_Tag_Raw", "Avg_Sollzeit_Tag"}
|
||||
}),
|
||||
|
||||
// Abrechnungskreis als Text sicherstellen (fuehrende Null bleibt erhalten)
|
||||
TypAbkrs = Table.TransformColumnTypes(RenNum, {{"Abrechnungskreis", type text}}),
|
||||
|
||||
// Name splitten
|
||||
AddNachname = Table.AddColumn(TypAbkrs, "Nachname", each
|
||||
let n = Text.From(if [Name_Rexx] = null then "" else [Name_Rexx]) in
|
||||
Text.Trim(Text.Split(n, ","){0}), type text),
|
||||
AddVorname = Table.AddColumn(AddNachname, "Vorname", each
|
||||
let n = Text.From(if [Name_Rexx] = null then "" else [Name_Rexx]),
|
||||
p = Text.Split(n, ",") in
|
||||
if List.Count(p) > 1 then Text.Trim(p{1}) else "", type text),
|
||||
|
||||
// Stunden_Saldo parsen: "58:10" → 58.17, "-6:12" → -6.20
|
||||
AddSaldo = Table.AddColumn(AddVorname, "Stunden_Saldo", each
|
||||
let
|
||||
raw = Text.Trim(Text.From(if [Stunden_Saldo_Raw] = null then "0:00" else [Stunden_Saldo_Raw])),
|
||||
isNeg = Text.StartsWith(raw, "-"),
|
||||
cleaned = Text.Replace(raw, "-", ""),
|
||||
parts = Text.Split(cleaned, ":"),
|
||||
h = try Number.FromText(parts{0}) otherwise 0,
|
||||
m = if List.Count(parts) > 1 then (try Number.FromText(parts{1}) otherwise 0) else 0,
|
||||
dec = h + m / 60
|
||||
in if isNeg then -dec else dec,
|
||||
type number),
|
||||
|
||||
// FTE
|
||||
AddFTE = Table.AddColumn(AddSaldo, "FTE", each
|
||||
if [Beschaeftigungsgrad_Prozent] <> null and [Beschaeftigungsgrad_Prozent] > 0
|
||||
then [Beschaeftigungsgrad_Prozent] / 100
|
||||
else if [Arbeitszeitmodell] = "Vollzeit" then 1 else 0.5,
|
||||
type number),
|
||||
|
||||
// Alter
|
||||
AddAlter = Table.AddColumn(AddFTE, "Alter_Jahre", each
|
||||
if [Geburtsdatum] = null then null
|
||||
else Number.RoundDown(Duration.TotalDays(Date.From(DateTime.LocalNow()) - [Geburtsdatum]) / 365.25),
|
||||
Int64.Type),
|
||||
|
||||
// Altersgruppe
|
||||
AddAG = Table.AddColumn(AddAlter, "Altersgruppe", each
|
||||
if [Alter_Jahre] = null then "Unbekannt"
|
||||
else if [Alter_Jahre] < 30 then "< 30"
|
||||
else if [Alter_Jahre] < 40 then "30-39"
|
||||
else if [Alter_Jahre] < 50 then "40-49"
|
||||
else if [Alter_Jahre] < 60 then "50-59"
|
||||
else "60+", type text),
|
||||
|
||||
// Geschlecht Text
|
||||
AddGT = Table.AddColumn(AddAG, "Geschlecht_Text", each
|
||||
if [Geschlecht] = 1 then "Maennlich"
|
||||
else if [Geschlecht] = 2 then "Weiblich"
|
||||
else "Unbekannt", type text),
|
||||
|
||||
// Ist Teilzeit
|
||||
AddTZ = Table.AddColumn(AddGT, "Ist_Teilzeit", each
|
||||
if [Beschaeftigungsgrad_Prozent] <> null and [Beschaeftigungsgrad_Prozent] > 0
|
||||
then [Beschaeftigungsgrad_Prozent] < 100
|
||||
else if [Arbeitszeitmodell] <> null then [Arbeitszeitmodell] = "Teilzeit"
|
||||
else false, type logical),
|
||||
|
||||
// Dienstjahre
|
||||
AddDJ = Table.AddColumn(AddTZ, "Dienstjahre", each
|
||||
if [Eintrittsdatum] = null then null
|
||||
else Number.RoundDown(Duration.TotalDays(Date.From(DateTime.LocalNow()) - [Eintrittsdatum]) / 365.25),
|
||||
Int64.Type),
|
||||
|
||||
// Ist Aktiv
|
||||
AddAktiv = Table.AddColumn(AddDJ, "Ist_Aktiv", each [Personal_Status] = "Aktiv", type logical),
|
||||
|
||||
// Periode
|
||||
AddPeriode = Table.AddColumn(AddAktiv, "Periode", each Date.StartOfMonth(Date.From(DateTime.LocalNow())), type date),
|
||||
AddJahr = Table.AddColumn(AddPeriode, "Jahr", each Date.Year([Periode]), Int64.Type),
|
||||
AddMonat = Table.AddColumn(AddJahr, "Monat", each Date.Month([Periode]), Int64.Type),
|
||||
AddPT = Table.AddColumn(AddMonat, "Periode_Text", each
|
||||
Text.From([Jahr]) & "-" & Text.PadStart(Text.From([Monat]), 2, "0"), type text),
|
||||
|
||||
// Sollarbeitstage
|
||||
AddSAT = Table.AddColumn(AddPT, "Sollarbeitstage", each 21, Int64.Type),
|
||||
|
||||
// Ferien bezogen
|
||||
AddFB = Table.AddColumn(AddSAT, "Ferien_Bezogen", each
|
||||
let a = if [Urlaubsanspruch] = null then 0 else [Urlaubsanspruch],
|
||||
r = if [Urlaub_Rest] = null then 0 else [Urlaub_Rest],
|
||||
au = if [Ferien_Ausstehend] = null then 0 else [Ferien_Ausstehend]
|
||||
in a - r - au, type number),
|
||||
AddFT = Table.AddColumn(AddFB, "Ferientage", each
|
||||
let fb = [Ferien_Bezogen] in if fb = null or fb < 0 then 0 else fb, type number),
|
||||
|
||||
// GLZ Ampel
|
||||
AddGLZ = Table.AddColumn(AddFT, "GLZ_Ampel", each
|
||||
let abs = Number.Abs([Stunden_Saldo]) in
|
||||
if abs <= 50 then "Gruen" else if abs <= 100 then "Gelb" else "Rot", type text),
|
||||
AddGLZS = Table.AddColumn(AddGLZ, "GLZ_Ampel_Sort", each
|
||||
if [GLZ_Ampel] = "Gruen" then 1 else if [GLZ_Ampel] = "Gelb" then 2 else 3, Int64.Type),
|
||||
|
||||
// Restferien Ampel
|
||||
AddRA = Table.AddColumn(AddGLZS, "Restferien_Ampel", each
|
||||
if [Urlaub_Rest] = null or [Urlaub_Rest] <= 5 then "Gruen" else "Rot", type text),
|
||||
|
||||
// Mitarbeitertyp
|
||||
AddMT = Table.AddColumn(AddRA, "Mitarbeitertyp", each
|
||||
let s = Text.Lower(Text.From(if [Stelle_Rexx] = null then "" else [Stelle_Rexx])) in
|
||||
if Text.Contains(s, "praktik") then "Praktikant"
|
||||
else if Text.Contains(s, "werkstudent") then "Werkstudent"
|
||||
else if Text.Contains(s, "aushilfe") then "Aushilfe"
|
||||
else if Text.Contains(s, "lehrling") then "Lehrling"
|
||||
else "Festangestellt", type text),
|
||||
|
||||
// Kostenstelle
|
||||
AddKNr = Table.AddColumn(AddMT, "Kostenstelle", each
|
||||
let raw = Text.From(if [Kostenstelle_Rexx] = null then "" else [Kostenstelle_Rexx]),
|
||||
parts = Text.Split(raw, "/"),
|
||||
num = Text.Trim(parts{0})
|
||||
in try Number.FromText(num) otherwise null, Int64.Type),
|
||||
AddKTxt = Table.AddColumn(AddKNr, "Kostenstelle_Text", each
|
||||
if [Kostenstelle_Rexx] = null then "" else Text.From([Kostenstelle_Rexx]), type text),
|
||||
|
||||
// Organisation + Stelle Klartext
|
||||
AddOrg = Table.AddColumn(AddKTxt, "Organisationseinheit", each
|
||||
if [Organisation_Text] = null then "" else Text.From([Organisation_Text]), type text),
|
||||
AddSt = Table.AddColumn(AddOrg, "Stelle", each
|
||||
if [Stelle_Rexx] = null then "" else Text.From([Stelle_Rexx]), type text),
|
||||
|
||||
// Nur aktive MA
|
||||
FilterAktiv = Table.SelectRows(AddSt, each [Personal_Status] = "Aktiv"),
|
||||
|
||||
// Hilfsspalten entfernen
|
||||
Clean = Table.RemoveColumns(FilterAktiv, {
|
||||
"Name_Rexx", "Stelle_Rexx", "Organisation_Text",
|
||||
"Kostenstelle_Rexx", "Eintrittsdatum_Raw", "Geburtsdatum_Raw",
|
||||
"Stunden_Saldo_Raw", "Personal_Status", "PERNR_Key"
|
||||
}),
|
||||
|
||||
// Sortieren
|
||||
Sorted = Table.Sort(Clean, {{"Personalnummer", Order.Ascending}})
|
||||
in
|
||||
Sorted
|
||||
@@ -0,0 +1,118 @@
|
||||
let
|
||||
Source = Excel.Workbook(File.Contents("C:\temp\Personalausgeschieden.xlsx"), null, true),
|
||||
Data = Source{0}[Data],
|
||||
Head = Table.PromoteHeaders(Data, [PromoteAllScalars = true]),
|
||||
|
||||
Ren = Table.RenameColumns(Head, {
|
||||
{"Personalnummer", "PERNR_Text"},
|
||||
{"Nachname, Vorname (Link Personal)", "Name_Voll"},
|
||||
{"Stelle-1", "Stelle"},
|
||||
{"Organisation-1", "Organisationseinheit"},
|
||||
{"Leitung j/n", "Leitung"},
|
||||
{"Austrittsdatum", "Austrittsdatum_Raw"},
|
||||
{"Austrittsart", "Austrittsart"},
|
||||
{"Eintrittsdatum", "Eintrittsdatum_Raw"},
|
||||
{"Personal Status", "Status"}
|
||||
}),
|
||||
|
||||
AddPernr = Table.AddColumn(Ren, "Personalnummer", each
|
||||
try Number.FromText(Text.Trim(Text.From([PERNR_Text]))) otherwise null, Int64.Type),
|
||||
|
||||
// Datumsfelder (Excel liefert evtl. schon als Date)
|
||||
AddAustritt = Table.AddColumn(AddPernr, "Austrittsdatum", each
|
||||
let raw = [Austrittsdatum_Raw] in
|
||||
if raw is date then raw
|
||||
else if raw is datetime then Date.From(raw)
|
||||
else if raw is number then Date.AddDays(#date(1899, 12, 30), Number.RoundDown(raw))
|
||||
else if (try Number.FromText(Text.From(raw)) otherwise null) <> null then
|
||||
Date.AddDays(#date(1899, 12, 30), Number.RoundDown(Number.FromText(Text.From(raw))))
|
||||
else try Date.FromText(Text.From(raw), [Format = "dd.MM.yyyy"]) otherwise null,
|
||||
type date),
|
||||
AddEintritt = Table.AddColumn(AddAustritt, "Eintrittsdatum", each
|
||||
let raw = [Eintrittsdatum_Raw] in
|
||||
if raw is date then raw
|
||||
else if raw is datetime then Date.From(raw)
|
||||
else if raw is number then Date.AddDays(#date(1899, 12, 30), Number.RoundDown(raw))
|
||||
else if (try Number.FromText(Text.From(raw)) otherwise null) <> null then
|
||||
Date.AddDays(#date(1899, 12, 30), Number.RoundDown(Number.FromText(Text.From(raw))))
|
||||
else try Date.FromText(Text.From(raw), [Format = "dd.MM.yyyy"]) otherwise null,
|
||||
type date),
|
||||
|
||||
// Verweildauer
|
||||
AddVW = Table.AddColumn(AddEintritt, "Verweildauer_Monate", each
|
||||
try Number.Round(Duration.TotalDays([Austrittsdatum] - [Eintrittsdatum]) / 30.44, 1)
|
||||
otherwise null, type number),
|
||||
|
||||
// Mitarbeitertyp
|
||||
AddMT = Table.AddColumn(AddVW, "Mitarbeitertyp", each
|
||||
let s = Text.Lower(Text.From(if [Stelle] = null then "" else [Stelle])) in
|
||||
if Text.Contains(s, "praktik") then "Praktikant"
|
||||
else if Text.Contains(s, "werkstudent") then "Werkstudent"
|
||||
else if Text.Contains(s, "aushilfe") then "Aushilfe"
|
||||
else if Text.Contains(s, "lehrling") then "Lehrling"
|
||||
else "Festangestellt", type text),
|
||||
|
||||
// Fluktuationslogik gemaess HR-Definition aus formeln.docx:
|
||||
// Zaehlen nur Arbeitnehmerkuendigungen von festangestellten Mitarbeitenden.
|
||||
// Nicht zaehlen: Aushilfen, Praktikanten, Werkstudenten, Lehrlinge,
|
||||
// befristete Vertraege, Pensionierungen und Kuendigungen durch Trafag.
|
||||
AddAustrittsartNorm = Table.AddColumn(AddMT, "Austrittsart_Normalisiert", each
|
||||
let
|
||||
raw = Text.Lower(Text.Trim(Text.From(if [Austrittsart] = null then "" else [Austrittsart]))),
|
||||
ae = Text.Replace(raw, Character.FromNumber(228), "ae"),
|
||||
oe = Text.Replace(ae, Character.FromNumber(246), "oe"),
|
||||
ue = Text.Replace(oe, Character.FromNumber(252), "ue"),
|
||||
ss = Text.Replace(ue, Character.FromNumber(223), "ss")
|
||||
in ss,
|
||||
type text),
|
||||
AddIstArbeitnehmerkuendigung = Table.AddColumn(AddAustrittsartNorm, "Ist_Arbeitnehmerkuendigung", each
|
||||
let a = [Austrittsart_Normalisiert] in
|
||||
Text.Contains(a, "arbeitnehmer") or
|
||||
Text.Contains(a, "mitarbeiter") or
|
||||
Text.Contains(a, "kuendigung an") or
|
||||
Text.Contains(a, "an kuendigung") or
|
||||
Text.Contains(a, "eigenkuendigung") or
|
||||
Text.Contains(a, "kuendigung ma") or
|
||||
Text.Contains(a, "kuendigung durch ma"),
|
||||
type logical),
|
||||
AddIstAusgeschlossen = Table.AddColumn(AddIstArbeitnehmerkuendigung, "Ist_Fluktuation_Ausgeschlossen", each
|
||||
let a = [Austrittsart_Normalisiert] in
|
||||
[Mitarbeitertyp] <> "Festangestellt" or
|
||||
Text.Contains(a, "befrist") or
|
||||
Text.Contains(a, "pension") or
|
||||
Text.Contains(a, "rente") or
|
||||
Text.Contains(a, "trafag") or
|
||||
Text.Contains(a, "arbeitgeber") or
|
||||
Text.Contains(a, "ag-kuendigung") or
|
||||
Text.Contains(a, "ag kuendigung") or
|
||||
Text.Contains(a, "kuendigung ag"),
|
||||
type logical),
|
||||
AddFluktuationRelevant = Table.AddColumn(AddIstAusgeschlossen, "Ist_Fluktuationsrelevant", each
|
||||
[Ist_Arbeitnehmerkuendigung] = true and [Ist_Fluktuation_Ausgeschlossen] = false,
|
||||
type logical),
|
||||
AddAusschlussgrund = Table.AddColumn(AddFluktuationRelevant, "Fluktuation_Ausschlussgrund", each
|
||||
let a = [Austrittsart_Normalisiert] in
|
||||
if [Ist_Fluktuationsrelevant] then null
|
||||
else if [Mitarbeitertyp] <> "Festangestellt" then [Mitarbeitertyp]
|
||||
else if Text.Trim(a) = "" then "Austrittsart leer/unklar"
|
||||
else if Text.Contains(a, "befrist") then "Befristeter Vertrag"
|
||||
else if Text.Contains(a, "pension") or Text.Contains(a, "rente") then "Pensionierung"
|
||||
else if Text.Contains(a, "trafag") or Text.Contains(a, "arbeitgeber") or Text.Contains(a, "ag-kuendigung") or Text.Contains(a, "ag kuendigung") or Text.Contains(a, "kuendigung ag") then "Kuendigung durch Trafag"
|
||||
else if [Ist_Arbeitnehmerkuendigung] = false then "Keine Arbeitnehmerkuendigung"
|
||||
else "Ausgeschlossen",
|
||||
type text),
|
||||
|
||||
AddAM = Table.AddColumn(AddAusschlussgrund, "Austrittsmonat", each Date.StartOfMonth([Austrittsdatum]), type date),
|
||||
AddAJ = Table.AddColumn(AddAM, "Austrittsjahr", each Date.Year([Austrittsdatum]), Int64.Type),
|
||||
|
||||
AddNN = Table.AddColumn(AddAJ, "Nachname", each
|
||||
Text.Trim(Text.Split(Text.From([Name_Voll]), ","){0}), type text),
|
||||
AddVN = Table.AddColumn(AddNN, "Vorname", each
|
||||
let p = Text.Split(Text.From([Name_Voll]), ",") in
|
||||
if List.Count(p) > 1 then Text.Trim(p{1}) else "", type text),
|
||||
|
||||
Clean = Table.RemoveColumns(AddVN, {"PERNR_Text", "Austrittsdatum_Raw", "Eintrittsdatum_Raw"}),
|
||||
Reorder = Table.ReorderColumns(Clean, {"Personalnummer"} &
|
||||
List.RemoveItems(Table.ColumnNames(Clean), {"Personalnummer"}))
|
||||
in
|
||||
Reorder
|
||||
Reference in New Issue
Block a user