Ask for a residence permit only where one is needed
Employees from outside the EU, the EEA and Switzerland need a residence permit, and HR needs to know when it expires — about eighty people in the current data, across Türkei, Serbien and Bosnien. Two columns, the same shape as the dismissal protection: a flag and a date that only means anything with it. The date is optional, because an open-ended permit has none and a mandatory field would force an invented one. The nationality coupling deliberately stays out of the database. Putting it there would mean keeping the country list in two places — SQL and lib/countries.ts, where the picker needs it anyway — so an EU accession would become a migration instead of a line in a list. Worse, correcting somebody's nationality would fail the constraint while the old permit was still attached, which is exactly the moment someone is fixing a mistake. The UI decides whether the fields appear, and clears them when the nationality moves into the free-movement area. So the list is the load-bearing part, and it is tested: 31 entries, all of them values the picker can actually produce, no duplicates, no third countries. A missing nationality reads as "no permit required" — an unanswered question is a reason to record it, not to demand papers. The permit shows on the Stammdaten tab only for the nationalities it applies to. A line reading "Aufenthaltstitel: Nein" under an Austrian citizenship would look like information rather than a question that does not arise. Filter by it and by when it expires — the question behind that being "whose permit runs out next quarter" — plus columns in the export. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ import { CountryPicker } from "@/components/ui/CountryPicker";
|
||||
import { Field, SelectField, TextField } from "@/components/ui/Field";
|
||||
import { SlideOver } from "@/components/ui/SlideOver";
|
||||
import { useToast } from "@/components/ui/Toast";
|
||||
import { UN_COUNTRIES } from "@/lib/countries";
|
||||
import { brauchtAufenthaltstitel, UN_COUNTRIES } from "@/lib/countries";
|
||||
import { STUNDEN_GRUENDE } from "@/lib/absence";
|
||||
import { fmtFullName, todayIso } from "@/lib/format";
|
||||
import { isValidSvnr, requiresAustrianSvnr } from "@/lib/svnr";
|
||||
@@ -53,6 +53,10 @@ export function DatenAendernPanel({
|
||||
const svNummerOk =
|
||||
!svNummer.trim() || !requiresAustrianSvnr(locationCountry) || isValidSvnr(svNummer, birthDate || null);
|
||||
const [nationality, setNationality] = useState(employee.nationality);
|
||||
const [hatTitel, setHatTitel] = useState(employee.hat_aufenthaltstitel ?? false);
|
||||
const [titelBis, setTitelBis] = useState(employee.aufenthaltstitel_bis ?? "");
|
||||
// Die Staatsbürgerschaft entscheidet, ob die Frage überhaupt gestellt wird.
|
||||
const titelNoetig = brauchtAufenthaltstitel(nationality);
|
||||
const [address, setAddress] = useState(employee.address ?? "");
|
||||
const [postalCode, setPostalCode] = useState(employee.postal_code ?? "");
|
||||
const [city, setCity] = useState(employee.city ?? "");
|
||||
@@ -131,6 +135,12 @@ export function DatenAendernPanel({
|
||||
birth_date: birthDate,
|
||||
sv_nummer: svNummer,
|
||||
nationality,
|
||||
// Wechselt die Staatsbürgerschaft in den Freizügigkeitsraum, fällt
|
||||
// der Titel weg — sonst bliebe er als Rest an einer Person hängen,
|
||||
// die ihn nicht mehr braucht. Die Datenbank prüft diese Kopplung
|
||||
// bewusst nicht (siehe Migration), also gehört sie hierher.
|
||||
hat_aufenthaltstitel: titelNoetig ? hatTitel : false,
|
||||
aufenthaltstitel_bis: titelNoetig && hatTitel ? titelBis : "",
|
||||
address,
|
||||
postal_code: postalCode,
|
||||
city,
|
||||
@@ -232,6 +242,34 @@ export function DatenAendernPanel({
|
||||
<CountryPicker {...p} value={nationality} onChange={setNationality} countries={UN_COUNTRIES} placeholder="Staatsbürgerschaft suchen…" />
|
||||
)}
|
||||
</Field>
|
||||
{/* Nur wo er verlangt ist. Bei Freizügigkeit — EU, EWR, Schweiz —
|
||||
wäre die Frage gegenstandslos, und ein „Nein" im Formular
|
||||
sähe aus wie eine Auskunft. */}
|
||||
{titelNoetig && (
|
||||
<>
|
||||
<SelectField
|
||||
label="Aufenthaltstitel"
|
||||
dense
|
||||
value={hatTitel ? "ja" : "nein"}
|
||||
onChange={(v) => setHatTitel(v === "ja")}
|
||||
options={[
|
||||
{ value: "nein", label: "Nein" },
|
||||
{ value: "ja", label: "Ja" },
|
||||
]}
|
||||
hint="Bei Staatsbürgerschaften ausserhalb von EU, EWR und Schweiz."
|
||||
/>
|
||||
{hatTitel && (
|
||||
<TextField
|
||||
label="Aufenthaltstitel gültig bis"
|
||||
dense
|
||||
type="date"
|
||||
value={titelBis}
|
||||
onChange={setTitelBis}
|
||||
hint="Optional. Leer lassen, wenn unbefristet."
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<TextField label="Adresse (Straße und Hausnummer)" dense value={address} onChange={setAddress} />
|
||||
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,2fr)] gap-3">
|
||||
<TextField label="Postleitzahl" dense inputMode="numeric" value={postalCode} onChange={setPostalCode} />
|
||||
|
||||
Reference in New Issue
Block a user