F.24 -- eine Planstelle, auf die eine Versetzung vorgemerkt ist, galt als frei. Die Vormerkung steht in pending_org_changes, "besetzt?" wurde allein an position_assignments gefragt. Auffallen wuerde es erst in der Nacht des Wirksamkeitstages: dort legt der Nachtlauf die zweite Besetzung an und laeuft in den Teilindex, der genau eine laufende Besetzung je Planstelle zulaesst. Er arbeitet in einer Transaktion ueber alle faelligen Vorgaenge -- eine einzige solche Buchung haette ihn vollstaendig zum Stehen gebracht, auch fuer alle anderen. Neue Auskunft planstelle_vorgemerkt, abgefragt in hire_employee, rehire_employee, transfer_employee und promote_employee, mit Datum in der Meldung. Die eigene Vormerkung zaehlt nicht als Hindernis. D.06 -- die Struktursicht zeigte Einheiten, die es am gewaehlten Stichtag noch nicht gab: org_units wurde ungefiltert gelesen, waehrend Planstellen und Besetzungen daneben auf den Stichtag eingeschraenkt waren. Die Druckansicht filterte schon immer richtig; jetzt tun es alle drei Stellen. H.09 -- der Kuendigungsschutz stand in der Akte nur als "bis TT.MM.JJJJ", obwohl Personenkreis, Beginn, Ende und die ganze Begunstigung erfasst werden. Der Personenkreis ist die eigentliche Auskunft. Die Behinderung bekommt eine eigene Zeile: sie fuehrt oft zu Kuendigungsschutz, ist aber ein eigener Bescheid. Elternteilzeit und Wiedereingliederungsteilzeit sind jetzt auch bei der Stundenaenderung waehlbar. Sie standen nur bei der Rueckkehr aus einer Abwesenheit, mit der Begruendung, dass sie typischerweise dann beginnen -- typischerweise ist aber nicht immer. Die Ueberschneidung der beiden Listen ist damit gewollt; der Test prueft nicht mehr auf Partition, sondern darauf, dass keine Teilzeit an keinem der beiden Wege haengt.
55 lines
2.0 KiB
TypeScript
55 lines
2.0 KiB
TypeScript
import { Suspense } from "react";
|
|
import { OrgChartClient } from "@/components/orgchart/OrgChartClient";
|
|
import type { OrgUnitNode } from "@/components/orgchart/types";
|
|
import { todayIso } from "@/lib/format";
|
|
import { loadOrgAsOf } from "@/lib/orgchart-data";
|
|
import { parseIsoDateParam } from "@/lib/reports";
|
|
import { currentUserId } from "@/lib/auth/session";
|
|
import { withUser } from "@/lib/db";
|
|
|
|
type SearchParams = { asOf?: string; focus?: string };
|
|
|
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
|
export default async function OrgChartPage({ searchParams }: { searchParams: Promise<SearchParams> }) {
|
|
const params = await searchParams;
|
|
const today = todayIso();
|
|
const asOf = parseIsoDateParam(params.asOf) ?? today;
|
|
// Only ever used to match against ids already on the page, but validated
|
|
// so a junk value can't reach the client as an arbitrary string.
|
|
const focusId = params.focus && UUID.test(params.focus) ? params.focus : null;
|
|
|
|
const { org, units } = await withUser(await currentUserId(), async (tx) => {
|
|
const [org, units] = await Promise.all([
|
|
loadOrgAsOf(tx, asOf),
|
|
tx
|
|
.selectFrom("org_units")
|
|
// valid_from/valid_to nur hier: die Angaben zur Einheit zeigen sie,
|
|
// der Druck braucht sie nicht.
|
|
.select(["id", "org_number", "name", "parent_id", "unit_type", "valid_from", "valid_to"])
|
|
// Zum Stichtag — sonst zeigt die Struktursicht zum 01.01.2020
|
|
// Einheiten, die es damals nicht gab (D.06).
|
|
.where("valid_from", "<=", asOf)
|
|
.where((eb) => eb.or([eb("valid_to", "is", null), eb("valid_to", ">", asOf)]))
|
|
.orderBy("org_number")
|
|
.execute(),
|
|
]);
|
|
return { org, units };
|
|
});
|
|
|
|
return (
|
|
<Suspense>
|
|
<OrgChartClient
|
|
employees={org.employees}
|
|
units={units as OrgUnitNode[]}
|
|
vacancies={org.vacancies}
|
|
asOf={asOf}
|
|
today={today}
|
|
projectedCount={org.projectedCount}
|
|
historyStartsAt={org.historyStartsAt}
|
|
focusId={focusId}
|
|
/>
|
|
</Suspense>
|
|
);
|
|
}
|