diff --git a/components/employees/EmployeeDetail.tsx b/components/employees/EmployeeDetail.tsx index 1f97306..d8e7d62 100644 --- a/components/employees/EmployeeDetail.tsx +++ b/components/employees/EmployeeDetail.tsx @@ -117,7 +117,7 @@ export function EmployeeDetail(props: EmployeeDetailProps) {

Pers.-Nr. {employee.personnel_number} - {employee.status !== "Geplant" && <> · Zugehörigkeit: {tenure(employee.entry_date, employee.exit_date)}} + {status !== "Geplant" && <> · Zugehörigkeit: {tenure(employee.entry_date, employee.exit_date)}}

@@ -129,7 +129,7 @@ export function EmployeeDetail(props: EmployeeDetailProps) { setPanel("promote")} /> setPanel("karenz")} /> @@ -140,7 +140,7 @@ export function EmployeeDetail(props: EmployeeDetailProps) { noch keinen Tag gearbeitet, und genau dafür gibt es den Grund „No Show" — ohne diesen Knopf bliebe sie auf Dauer als geplanter Eintritt stehen. */} - {(isActive || employee.status === "Geplant") && ( + {(isActive || status === "Geplant") && ( )} - {employee.status === "Ausgetreten" && ( + {/* Der Knopf hing an `employee.status` — der Spalte, die + nachhängt. An einer Person, deren Austritt erfasst und + inzwischen vollzogen war, stand dort weiter „Aktiv", und der + Wiedereintritt war schlicht nicht erreichbar. Der Kunde hat + ihn deshalb für verschwunden gehalten („ich dachte, das ist + schon implementiert, das war mal drin"). + * + Beim Statusfix waren nur `isActive` und `canEditData` + umgestellt worden — die fünf einzelnen Abfragen in dieser + Datei blieben stehen. Jetzt liest keine mehr die Spalte. */} + {status === "Ausgetreten" && ( )} @@ -195,7 +205,6 @@ export function EmployeeDetail(props: EmployeeDetailProps) { directReports={directReports} breadcrumb={breadcrumb} kostenstelle={kostenstelle} - standort={location?.name ?? null} /> )} {tab === "Onboarding" && ( @@ -215,7 +224,7 @@ export function EmployeeDetail(props: EmployeeDetailProps) { openPositions={openPositions} /> setPanel(null)} employee={employee} /> - setPanel(null)} employee={employee} /> + setPanel(null)} employee={employee} status={status} /> setPanel(null)} @@ -223,7 +232,13 @@ export function EmployeeDetail(props: EmployeeDetailProps) { dependents={dependents} locationCountry={location?.country} /> - setPanel(null)} employee={employee} directReportCount={directReports.length} /> + setPanel(null)} + employee={employee} + status={status} + directReportCount={directReports.length} + /> setPanel(null)} diff --git a/components/employees/panels/KarenzPanel.tsx b/components/employees/panels/KarenzPanel.tsx index ac2e112..8abd639 100644 --- a/components/employees/panels/KarenzPanel.tsx +++ b/components/employees/panels/KarenzPanel.tsx @@ -10,16 +10,27 @@ import { SlideOver } from "@/components/ui/SlideOver"; import { useToast } from "@/components/ui/Toast"; import { ABSENCE_TYPES, absenceLabel, RUECKKEHR_GRUENDE } from "@/lib/absence"; import { fmtDate, fmtName } from "@/lib/format"; -import type { Database } from "@/lib/types"; +import type { Database, EmploymentStatus } from "@/lib/types"; type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"]; type Mode = "adjust" | "return"; type EmploymentMode = "unverändert" | "Vollzeit" | "Teilzeit"; -export function KarenzPanel({ open, onClose, employee }: { open: boolean; onClose: () => void; employee: EmployeeRow }) { +export function KarenzPanel({ + open, + onClose, + employee, + status, +}: { + open: boolean; + onClose: () => void; + employee: EmployeeRow; + /** Der zum heutigen Tag abgeleitete Status — nicht `employee.status`, die Spalte hängt nach. */ + status: EmploymentStatus; +}) { const { showToast } = useToast(); const router = useRouter(); - const isOnKarenz = employee.status === "Karenz"; + const isOnKarenz = status === "Karenz"; const [mode, setMode] = useState("adjust"); const [pending, setPending] = useState(false); @@ -188,7 +199,7 @@ export function KarenzPanel({ open, onClose, employee }: { open: boolean; onClos {mode === "adjust" && ( <>

- {absenceLabel(employee.status, employee.absence_type)} + {absenceLabel(status, employee.absence_type)} {" · Rückkehr "} {fmtDate(employee.karenz_return_date)}

diff --git a/components/employees/panels/TerminatePanel.tsx b/components/employees/panels/TerminatePanel.tsx index 7517048..004b1c0 100644 --- a/components/employees/panels/TerminatePanel.tsx +++ b/components/employees/panels/TerminatePanel.tsx @@ -15,7 +15,7 @@ import { beendigungsartenZu, type Austrittsart, } from "@/lib/beendigung"; -import type { Database } from "@/lib/types"; +import type { Database, EmploymentStatus } from "@/lib/types"; import { fmtDate, fmtName } from "@/lib/format"; type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"]; @@ -28,17 +28,19 @@ type TerminatePanelProps = { open: boolean; onClose: () => void; employee: EmployeeRow; + /** Der zum heutigen Tag abgeleitete Status — nicht `employee.status`, die Spalte hängt nach. */ + status: EmploymentStatus; directReportCount: number; }; -export function TerminatePanel({ open, onClose, employee, directReportCount }: TerminatePanelProps) { +export function TerminatePanel({ open, onClose, employee, status, directReportCount }: TerminatePanelProps) { const { showToast } = useToast(); const router = useRouter(); const [exitDate, setExitDate] = useState(""); // Wer noch gar nicht angefangen hat, tritt fast nie aus einem anderen Grund // aus. Die Vorbelegung nimmt den wahrscheinlichen Fall vorweg, ohne die // übrigen zu verstellen. - const [reason, setReason] = useState(employee.status === "Geplant" ? NO_SHOW : BEENDIGUNGSART_WERTE[0]); + const [reason, setReason] = useState(status === "Geplant" ? NO_SHOW : BEENDIGUNGSART_WERTE[0]); // „Alle" statt einer Vorauswahl: die Einschränkung ist eine Hilfe beim // Suchen, keine Pflichtangabe. Wer die gewünschte Art auswendig kennt, // soll sie unmittelbar wählen können. diff --git a/components/employees/tabs/OrganisationTab.tsx b/components/employees/tabs/OrganisationTab.tsx index 81a34b2..cdf3a5c 100644 --- a/components/employees/tabs/OrganisationTab.tsx +++ b/components/employees/tabs/OrganisationTab.tsx @@ -19,23 +19,17 @@ type OrganisationTabProps = { * hat die Person keine laufende Besetzung (geplanter Eintritt, Austritt). */ kostenstelle: { code: string; name: string } | null; - /** - * Die Niederlassung, an der die Person arbeitet. - * - * Aus dem Workshop nachgetragen (Anforderung 8, „Niederlassungen auch in - * Zuordnung einfügen"). Sie stand bisher nur im Stammdatenblatt, obwohl sie - * zur Einordnung gehört wie die Einheit und die Kostenstelle: wer wissen - * will, wo jemand sitzt, sieht hier nach und nicht bei der Privatadresse. - * - * Anders als Einheit und Kostenstelle hängt sie **an der Person** - * (`employees.location_id`) und nicht an der Planstelle. Deshalb steht sie - * auch dann da, wenn es keine laufende Besetzung gibt — bei einem - * geplanten Eintritt zum Beispiel, wo daneben „Keine laufende - * Planstellenbesetzung" zu lesen ist. - */ - standort: string | null; }; +// Die Niederlassung stand hier kurzzeitig neben Einheit und Kostenstelle — +// unsere Auslegung von „Niederlassungen auch in Zuordnung einfügen" +// (Anforderung 8). Im Gespräch am 17.09.2026 hat der Kunde sie hier wieder +// gestrichen: „nimm's mal hier raus, ich glaub, da brauchen wir's nicht +// drin." Sie steht weiterhin im Vertragsblatt. +// +// Was mit der Anforderung gemeint war, ist damit weiterhin offen — siehe +// docs/rueckfragen-workshop-2026-09.md. + export function OrganisationTab({ employeeId, manager, @@ -43,7 +37,6 @@ export function OrganisationTab({ directReports, breadcrumb, kostenstelle, - standort, }: OrganisationTabProps) { return (
@@ -51,10 +44,6 @@ export function OrganisationTab({

Organisationseinheit

{breadcrumb}

-

Niederlassung

-

- {standort ?? Nicht erfasst} -

Kostenstelle

{kostenstelle ? ( diff --git a/tests/components/TerminatePanel.test.tsx b/tests/components/TerminatePanel.test.tsx index a7668c3..a781b30 100644 --- a/tests/components/TerminatePanel.test.tsx +++ b/tests/components/TerminatePanel.test.tsx @@ -3,7 +3,7 @@ import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { TerminatePanel } from "@/components/employees/panels/TerminatePanel"; import { ToastProvider } from "@/components/ui/Toast"; -import type { Database } from "@/lib/types"; +import type { Database, EmploymentStatus } from "@/lib/types"; type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"]; @@ -76,10 +76,23 @@ function mitarbeiter(teil: Partial = {}): EmployeeRow { } as EmployeeRow; } -function zeige(teil: Partial = {}) { +// Der Status kommt als eigener Wert und nicht mehr aus der Zeile: die Spalte +// `employees.status` hängt nach, deshalb leitet die Akte ihn aus den Daten ab +// und reicht ihn durch (siehe lib/reports.ts, deriveStatusAsOf). +function zeige(teil: Partial = {}, status?: EmploymentStatus) { + // Vorgabe aus der Zeile, damit die vorhandenen Fälle unverändert das meinen, + // was sie meinten — `zeige({ status: "Aktiv" })` beschreibt weiterhin eine + // aktive Person. Der abgeleitete Status ist nur noch ein eigener Weg zum + // selben Wert, und die Vorgabe „Geplant" entspricht der Zeile oben. return render( - {}} employee={mitarbeiter(teil)} directReportCount={0} /> + {}} + employee={mitarbeiter(teil)} + status={status ?? teil.status ?? "Geplant"} + directReportCount={0} + /> ); }