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.
106 lines
4.9 KiB
TypeScript
106 lines
4.9 KiB
TypeScript
import { dienstwagenLabel } from "@/lib/dienstwagen";
|
||
import { fmtDate } from "@/lib/format";
|
||
import { hayGradeLabel } from "@/lib/hay-grade";
|
||
import type { Database } from "@/lib/types";
|
||
|
||
type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"];
|
||
|
||
/**
|
||
* Der Kündigungsschutz mit Grund und Zeitraum.
|
||
*
|
||
* Hier stand nur „bis TT.MM.JJJJ". Erfasst werden aber Personenkreis, Beginn
|
||
* und Ende — und der Personenkreis ist die eigentliche Auskunft: „bis 2030"
|
||
* sagt nicht, warum. Gemeldet am 29.09. von Lara und im Testprotokoll (H.09).
|
||
*
|
||
* Die Wortwahl für den einfachsten Fall bleibt: steht nur das Kennzeichen da,
|
||
* heisst es weiter „Ja (Ende offen)".
|
||
*/
|
||
function kuendigungsschutzText(e: EmployeeRow): string {
|
||
if (!e.has_kuendigungsschutz) return "Nein";
|
||
const teile = [
|
||
e.kuendigungsschutz_grund,
|
||
e.kuendigungsschutz_ab ? `ab ${fmtDate(e.kuendigungsschutz_ab)}` : null,
|
||
e.kuendigungsschutz_bis ? `bis ${fmtDate(e.kuendigungsschutz_bis)}` : null,
|
||
].filter((t): t is string => Boolean(t));
|
||
|
||
if (teile.length === 0) return "Ja (Ende offen)";
|
||
return e.kuendigungsschutz_bis ? teile.join(" · ") : `${teile.join(" · ")} · Ende offen`;
|
||
}
|
||
|
||
/**
|
||
* Die Begünstigung als eigene Zeile, nicht an den Kündigungsschutz gehängt.
|
||
*
|
||
* Sie führt zwar oft zu besonderem Kündigungsschutz, ist aber ein eigener
|
||
* Bescheid — und der Grad gehört zur Behinderung, nicht zum Schutz.
|
||
*/
|
||
function behinderungText(e: EmployeeRow): string {
|
||
if (!e.ist_beguenstigt_behindert) return "Nein";
|
||
const teile = [
|
||
e.behinderung_grad !== null ? `${e.behinderung_grad} %` : null,
|
||
e.behinderung_ab ? `Bescheid ab ${fmtDate(e.behinderung_ab)}` : null,
|
||
e.behinderung_bis ? `bis ${fmtDate(e.behinderung_bis)}` : null,
|
||
].filter((t): t is string => Boolean(t));
|
||
|
||
return teile.length > 0 ? teile.join(" · ") : "Ja";
|
||
}
|
||
|
||
export function VertragTab({ employee }: { employee: EmployeeRow }) {
|
||
// Früher stand hier eine einzige Zeile „Merkmale" mit allem, was zutraf,
|
||
// durch Kommas getrennt — und ein Gedankenstrich, wenn nichts zutraf. Damit
|
||
// liess sich nicht ablesen, ob jemand *keinen* Dienstwagen hat oder ob
|
||
// niemand die Frage je beantwortet hat. Jedes Merkmal steht jetzt für sich,
|
||
// mit Ja oder Nein, wie jede andere Zeile auf diesem Blatt auch.
|
||
const rows: [string, string][] = [
|
||
["Eintrittsdatum", fmtDate(employee.entry_date)],
|
||
["Vertragsart", employee.contract_type === "befristet" ? `befristet bis ${fmtDate(employee.contract_end_date)}` : "unbefristet"],
|
||
["Kollektivvertrag", employee.collective_agreement ?? "–"],
|
||
["Beschäftigungsausmaß", employee.employment_type],
|
||
["Wochenstunden", `${employee.weekly_hours} h`],
|
||
["Urlaubsanspruch", "25 Tage"],
|
||
["Hay-Grade", hayGradeLabel(employee.paygrade)],
|
||
["Beschäftigtengruppe", employee.worker_type ?? "–"],
|
||
["Arbeitstage", employee.work_days?.join(", ") || "–"],
|
||
// Beim Dienstwagen steht die Antriebsart statt eines blossen „Ja" — das
|
||
// war die Frage dahinter, seit E-Fahrzeuge getrennt zu führen sind.
|
||
["Dienstwagen", employee.has_dienstwagen ? dienstwagenLabel(employee.dienstwagen_art) : "Nein"],
|
||
["Betriebsrat", employee.is_betriebsrat ? "Ja" : "Nein"],
|
||
["Laterale Führung", employee.is_laterale_fuehrung ? "Ja" : "Nein"],
|
||
["C-Level", employee.is_c_level ? "Ja" : "Nein"],
|
||
// Das Enddatum steht gleich dabei: „Ja" allein liesse offen, ob der
|
||
// Schutz noch läuft, und genau danach fragt man.
|
||
// Die Teilzeitvariante gehört neben die Stunden: sie erklärt, warum sie
|
||
// sind, wie sie sind.
|
||
[
|
||
"Teilzeitvariante",
|
||
employee.teilzeit_art
|
||
? employee.teilzeit_bis
|
||
? `${employee.teilzeit_art} bis ${fmtDate(employee.teilzeit_bis)}`
|
||
: `${employee.teilzeit_art} (Ende offen)`
|
||
: "–",
|
||
],
|
||
// Grund und Zeitraum stehen dabei, nicht nur das Enddatum. Gemeldet am
|
||
// 29.09.: erfasst werden sieben Felder — Personenkreis, Beginn, Ende,
|
||
// Behinderung mit Grad und Bescheidzeitraum —, sichtbar war in der Akte
|
||
// nur „bis TT.MM.JJJJ". Wer den Grund brauchte, fand ihn nur in der
|
||
// Historie oder über einen Bericht.
|
||
["Besonderer Kündigungsschutz", kuendigungsschutzText(employee)],
|
||
// Eigene Zeile und nicht an den Kündigungsschutz gehängt: die Begünstigung
|
||
// ist ein Bescheid für sich. Sie führt zwar oft zu besonderem
|
||
// Kündigungsschutz, ist aber nicht dasselbe, und der Grad gehört zur
|
||
// Behinderung, nicht zum Schutz.
|
||
["Begünstigt behindert", behinderungText(employee)],
|
||
];
|
||
if (employee.exit_date) rows.push(["Austrittsdatum", fmtDate(employee.exit_date)]);
|
||
|
||
return (
|
||
<dl className="grid grid-cols-1 gap-x-8 gap-y-4 sm:grid-cols-2 lg:grid-cols-3">
|
||
{rows.map(([label, value]) => (
|
||
<div key={label}>
|
||
<dt className="text-xs font-semibold uppercase tracking-wide text-ink-muted">{label}</dt>
|
||
<dd className="mt-1 text-sm text-ink">{value}</dd>
|
||
</div>
|
||
))}
|
||
</dl>
|
||
);
|
||
}
|