Files
alpenwerk-hr/lib/types.ts
Andrei Laas 57acbfae9d
All checks were successful
CI / Lint, Typen, Tests, Build (push) Successful in 11m29s
CI / Migrationen auf leerer Datenbank (push) Successful in 10m27s
Die Firmen-E-Mail als eigenes, freiwilliges Feld
employees.email ist die private Adresse (20260811140000). Sie taugt nicht
als Dienstadresse und darf auch nicht als solche benutzt werden: der
Honestly-Export geht an einen fremden Anbieter, der damit im Namen des
Arbeitgebers einlaedt. Die Spalte "Email" stand dort deshalb seit jeher
leer, mit einem Vermerk in lib/honestly.ts, dass die Firmenadresse im
Datenmodell fehlt. Jetzt gibt es sie, und die Spalte fuellt sich.

Eindeutig, aber freiwillig -- mehrere Personen ohne Adresse stoeren den
Index nicht, weil null nie gleich null ist. Geschrieben wird ueber
`case when ? then nullif` statt `coalesce`: eine Dienstadresse muss sich
auch wieder entfernen lassen.

Vier SQL-Funktionen mussten mit, weil `create or replace` die ganze
Fassung ersetzt und ein ausgelassenes Feld dort still verschwindet:
hire_employee und rehire_employee (beide teilen sich den Schritt
"Person" -- das Formular haette das Feld gezeigt und den Wert
weggeworfen), change_employee_data (sonst nicht aenderbar),
apply_due_pending_changes (sonst verfiele eine auf spaeter datierte
Aenderung) und die Feldkarte (sonst waere der Eintrag in der Historie
nicht korrigierbar). Die Selbstpruefung am Ende prueft jede einzeln.
2026-09-23 10:52:26 +02:00

756 lines
29 KiB
TypeScript

// Von Hand gepflegt, passend zu db/migrations/*.sql.
//
// Erzeugt wird hier nichts: scripts/check-schema-types.mjs haelt die Datei
// Spalte fuer Spalte gegen die Migrationen und meldet jede Abweichung.
export type EmploymentStatus = "Aktiv" | "Karenz" | "Geplant" | "Ausgetreten";
export type EmploymentType = "Vollzeit" | "Teilzeit";
export type ContractType = "unbefristet" | "befristet";
export type PaygradeType = "A" | "B" | "C" | "D" | "E" | "F";
export type SourceType = "Intern" | "Extern";
export type GenderType = "m" | "w";
/**
* Die sozialversicherungsrechtliche Beschäftigtengruppe.
*
* Lehrling steht seit September 2026 daneben (Migration 20260910140000).
* Bis dahin wurden Lehrlinge als Angestellte:r oder Arbeiter:in geführt,
* was sie nicht sind — und was jede Auswertung nach dieser Spalte um
* genau diese Personen verfälschte.
*/
export type WorkerType = "Angestellte:r" | "Arbeiter:in" | "Lehrling";
export type CollectiveAgreement = "Handel" | "Süßwaren";
export type Weekday = "Mo" | "Di" | "Mi" | "Do" | "Fr" | "Sa" | "So";
/**
* Antriebsart des Dienstwagens.
*
* Bewusst keine dritte Möglichkeit „unbekannt": die Angabe hängt per CHECK
* an has_dienstwagen, und wer einen Dienstwagen hat, weiss auch, ob er lädt
* oder tankt.
*/
export type DienstwagenArt = "Verbrenner" | "Elektro";
/**
* Gesetzlich geregelte Teilzeiten.
*
* Keine Abwesenheiten: die Person arbeitet, nur kürzer. Als Zustand geführt,
* damit sich auswerten lässt, wer gerade in einer ist — mit einem optionalen
* Enddatum, weil nicht jede ein bekanntes Ende hat.
*/
export type TeilzeitArt = "Bildungsteilzeit" | "Elternteilzeit" | "Pflegeteilzeit" | "Wiedereingliederungsteilzeit";
/**
* Auf wessen Betreiben ein Dienstverhältnis endet.
*
* Eigene Angabe und **nicht** aus der Beendigungsart abgeleitet: die in
* Österreich übliche einvernehmliche Auflösung kann von beiden Seiten
* ausgehen und sagt über den Anstoss nichts aus. Die Begründung steht
* ausführlich in lib/beendigung.ts und in Migration 20260917100000.
*/
export type Austrittsart = "freiwillig" | "unfreiwillig";
/**
* Form der Beschäftigung — zweite Achse neben `worker_type`.
*
* Kein Aufzählungstyp in der Datenbank, sondern ein CHECK auf text
* (chk_mitarbeiterart). Die Beschriftungen stehen in lib/mitarbeiterart.ts.
*/
export type Mitarbeiterart = "Standard" | "Praktikant:in" | "Geringfügige Beschäftigung" | "Altersteilzeit";
/**
* Personenkreise des besonderen Kündigungsschutzes.
*
* Kein Aufzählungstyp in der Datenbank, sondern ein CHECK auf text
* (chk_kuendigungsschutz_personenkreis): die Liste ist Rechtslage und ändert
* sich mit dem Gesetz — ein Typ liesse einen zurückgenommenen Wert für immer
* stehen. Die Beschriftungen für die Oberfläche stehen in
* lib/kuendigungsschutz.ts und müssen zur Bedingung passen.
*/
export type KuendigungsschutzGrund =
| "Eltern"
| "Schwangerschaft"
| "Karenzurlaub"
| "Familienzeit"
| "Elternteilzeit"
| "Betriebsräte & Ersatzmitglieder"
| "Jugendvertrauenspersonen"
| "Behindertenvertrauenspersonen & Stellvertreter"
| "Präsenz-, Ausbildungs- oder Zivildienst"
| "Begünstigte behinderte ArbeitnehmerInnen"
| "Familienhospizkarenz"
| "Familienhospizteilzeit";
/**
* Verhältnis zum Notfallkontakt.
*
* Auswahlliste statt Freitext, damit sich danach auswerten lässt und nicht
* „Gattin", „Ehefrau" und „Frau" nebeneinander stehen. „Sonstige" ist der
* Ausweg für alles, was hier fehlt — ohne ihn wäre die Liste eine Anmassung.
*
* In der Datenbank bleibt die Spalte `text`: eine Aufzählung dort würde jede
* Ergänzung zu einer Migration machen, und diese Liste wird sich ändern.
*/
export const EMERGENCY_RELATIONS = [
"Gattin/Gatte",
"Lebensgefährtin/Lebensgefährte",
"Mutter",
"Vater",
"Tochter",
"Sohn",
"Schwester",
"Bruder",
"Freundin/Freund",
"Sonstige",
] as const;
export type EmergencyRelation = (typeof EMERGENCY_RELATIONS)[number];
/**
* Eine einzelne Feldänderung im Protokoll.
*
* `vorher` und `nachher` sind bewusst Text: die Datenbank stellt jeden Typ
* über ::text dar, damit ein Datum, eine Zahl und eine Liste von Arbeitstagen
* in derselben Spalte nebeneinander stehen können. Für die Anzeige reicht
* das; gerechnet wird damit nicht.
*/
export type AuditChange = { feld: string; vorher: string | null; nachher: string | null };
export type RelationshipType = "Ehepartner:in" | "Lebenspartner:in" | "Kind" | "Sonstige";
export type NoteCategory = "Allgemein" | "Vertraulich" | "Personalgespräch" | "Wiedervorlage" | "Lob / Anerkennung";
// Single HR-only role (see docs/decisions/0001-hr-only-access.md). Kept as a
// union (not a string literal) so a future hr_admin/hr_user split, if ever
// technically required, is a type-level addition, not a rewrite.
export type ProfileRole = "hr";
/** Etikett einer Organisationseinheit; die Struktur steckt in parent_id. */
export type OrgUnitType = "Gesellschaft" | "Bereich" | "Abteilung" | "Team";
export type HistoryEventType =
| "Eintritt"
| "Beförderung"
| "Versetzung"
| "Karenz"
| "Vertragsänderung"
| "Stammdatenänderung"
| "Austritt"
| "Wiedereintritt"
| "Reorganisation"
| "Gehaltsanpassung"
| "Rückkehr"
// Wechsel von extern auf intern. Eigenes Ereignis auf Wunsch des Kunden —
// ein Vorgang wie Neueinstellung oder Versetzung, nicht bloss ein
// geändertes Feld. Nur in dieser Richtung (Migration 20260917110000).
| "Übernahme";
export type PendingChangeType =
| "transfer"
| "promotion"
| "karenz_start"
| "karenz_return"
| "contract_change"
| "reorg";
export type PendingChangeStatus = "pending" | "applied" | "cancelled";
export type Database = {
public: {
Tables: {
locations: {
Row: { id: string; name: string; country: string };
Insert: { id?: string; name: string; country: string };
Update: Partial<{ id: string; name: string; country: string }>;
};
profiles: {
Row: {
id: string;
email: string;
full_name: string | null;
role: ProfileRole;
is_active: boolean;
created_by: string | null;
created_at: string;
updated_at: string;
};
Insert: {
id: string;
email: string;
full_name?: string | null;
role?: ProfileRole;
is_active?: boolean;
created_by?: string | null;
created_at?: string;
updated_at?: string;
};
Update: Partial<{
id: string;
email: string;
full_name: string | null;
role: ProfileRole;
is_active: boolean;
created_by: string | null;
created_at: string;
updated_at: string;
}>;
};
employees: {
Row: {
id: string;
personnel_number: number;
first_name: string;
last_name: string;
gender: GenderType;
birth_date: string;
sv_nummer: string | null;
nationality: string;
/** Aufenthaltstitel — nur ausserhalb von EU, EWR und Schweiz erhoben. */
hat_aufenthaltstitel: boolean;
/** Gültig bis, falls befristet. Nur mit dem Kennzeichen zusammen erlaubt. */
aufenthaltstitel_bis: string | null;
address: string | null;
postal_code: string | null;
city: string | null;
address_country: string | null;
/** Private Adresse, freiwillig — eindeutig, wenn angegeben. */
email: string | null;
/**
* Dienstliche Adresse, freiwillig — eindeutig, wenn angegeben.
*
* Getrennt von `email`: die private gehört nicht in Dateien, die
* das Haus verlassen. Der Honestly-Export nimmt diese hier.
*/
company_email: string | null;
phone: string | null;
job_title: string;
location_id: string;
employment_type: EmploymentType;
weekly_hours: number;
/** @deprecated Salary is out of MVP scope; column kept only for pre-existing data. */
monthly_salary_gross: number | null;
contract_type: ContractType;
contract_end_date: string | null;
paygrade: PaygradeType;
source: SourceType;
status: EmploymentStatus;
entry_date: string;
exit_date: string | null;
exit_reason: string | null;
/** Freiwillig oder unfreiwillig — eigene Angabe, nur mit einem Austritt erlaubt. */
austrittsart: Austrittsart | null;
karenz_start_date: string | null;
karenz_return_date: string | null;
absence_type: string | null;
avatar_color: string | null;
worker_type: WorkerType;
/** Form der Beschäftigung, NOT NULL mit Vorgabe „Standard" — chk_mitarbeiterart. */
mitarbeiterart: Mitarbeiterart;
collective_agreement: CollectiveAgreement;
work_days: Weekday[];
is_betriebsrat: boolean;
has_dienstwagen: boolean;
/** Null genau dann, wenn kein Dienstwagen vorhanden ist — chk_dienstwagen_art. */
dienstwagen_art: DienstwagenArt | null;
/** Besonderer Kündigungsschutz — löst beim Austritt eine Warnung aus. */
has_kuendigungsschutz: boolean;
/** Ende des Schutzes, falls bekannt. Nur mit dem Kennzeichen zusammen erlaubt. */
kuendigungsschutz_bis: string | null;
/**
* Personenkreis nach dem besonderen Kündigungsschutz — chk_kuendigungsschutz_personenkreis.
* Freiwillig, denn der Altbestand trägt das Kennzeichen ohne Grund.
*/
kuendigungsschutz_grund: KuendigungsschutzGrund | null;
/** Beginn des Schutzes, falls bekannt. Nur mit dem Kennzeichen zusammen erlaubt. */
kuendigungsschutz_ab: string | null;
/** Begünstigung nach dem Behinderteneinstellungsgesetz. */
ist_beguenstigt_behindert: boolean;
/** Grad der Behinderung in Prozent laut Bescheid, 1..100 — chk_behinderung_grad. */
behinderung_grad: number | null;
/** Beginn laut Bescheid. Nur mit dem Kennzeichen zusammen erlaubt. */
behinderung_ab: string | null;
/** Ende laut Bescheid, falls befristet. Nur mit dem Kennzeichen zusammen erlaubt. */
behinderung_bis: string | null;
/** Bildungs-, Eltern-, Pflege- oder Wiedereingliederungsteilzeit; null bei einer gewöhnlichen Regelung. */
teilzeit_art: TeilzeitArt | null;
/** Ende der Teilzeit, falls bekannt. Nur mit einer Variante zusammen erlaubt. */
teilzeit_bis: string | null;
/** Nur gemeinsam gesetzt oder gemeinsam leer — chk_emergency_contact. */
emergency_contact_name: string | null;
emergency_contact_phone: string | null;
emergency_contact_relation: string | null;
is_laterale_fuehrung: boolean;
is_c_level: boolean;
title_prefix: string[];
title_suffix: string[];
created_at: string;
updated_at: string;
};
Insert: {
id?: string;
first_name: string;
last_name: string;
gender: GenderType;
birth_date: string;
sv_nummer?: string | null;
nationality?: string;
hat_aufenthaltstitel?: boolean;
aufenthaltstitel_bis?: string | null;
address?: string | null;
postal_code?: string | null;
city?: string | null;
address_country?: string | null;
email?: string | null;
company_email?: string | null;
phone?: string | null;
job_title: string;
location_id: string;
employment_type?: EmploymentType;
weekly_hours?: number;
contract_type?: ContractType;
contract_end_date?: string | null;
paygrade?: PaygradeType;
source?: SourceType;
status?: EmploymentStatus;
entry_date: string;
exit_date?: string | null;
exit_reason?: string | null;
austrittsart?: Austrittsart | null;
karenz_start_date?: string | null;
karenz_return_date?: string | null;
absence_type?: string | null;
avatar_color?: string | null;
worker_type?: WorkerType;
mitarbeiterart?: Mitarbeiterart;
collective_agreement?: CollectiveAgreement;
work_days?: Weekday[];
is_betriebsrat?: boolean;
has_dienstwagen?: boolean;
dienstwagen_art?: DienstwagenArt | null;
has_kuendigungsschutz?: boolean;
kuendigungsschutz_bis?: string | null;
kuendigungsschutz_grund?: KuendigungsschutzGrund | null;
kuendigungsschutz_ab?: string | null;
ist_beguenstigt_behindert?: boolean;
behinderung_grad?: number | null;
behinderung_ab?: string | null;
behinderung_bis?: string | null;
teilzeit_art?: TeilzeitArt | null;
teilzeit_bis?: string | null;
emergency_contact_name?: string | null;
emergency_contact_phone?: string | null;
emergency_contact_relation?: string | null;
is_laterale_fuehrung?: boolean;
is_c_level?: boolean;
title_prefix?: string[];
title_suffix?: string[];
created_at?: string;
updated_at?: string;
};
Update: Partial<Database["public"]["Tables"]["employees"]["Insert"]>;
};
employee_history: {
Row: {
id: string;
employee_id: string;
event_date: string;
event_type: HistoryEventType;
description: string;
/** Feldweise Änderungen — dieselbe Form wie audit_log.changes. */
changes: AuditChange[] | null;
/** Der geplante Vorgang, solange die Änderung noch nicht wirksam ist. */
pending_id: string | null;
created_at: string;
};
Insert: {
id?: string;
employee_id: string;
event_date: string;
event_type: HistoryEventType;
description: string;
changes?: AuditChange[] | null;
pending_id?: string | null;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["employee_history"]["Insert"]>;
};
employee_dependents: {
Row: {
id: string;
employee_id: string;
first_name: string;
last_name: string;
relationship: RelationshipType;
sv_nummer: string | null;
birth_date: string;
created_at: string;
};
Insert: {
id?: string;
employee_id: string;
first_name: string;
last_name: string;
relationship: RelationshipType;
sv_nummer?: string | null;
birth_date: string;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["employee_dependents"]["Insert"]>;
};
employee_notes: {
Row: {
id: string;
employee_id: string;
author_user_id: string | null;
author_name: string;
category: NoteCategory;
note_text: string;
due_date: string | null;
done: boolean;
done_at: string | null;
done_by: string | null;
created_at: string;
};
Insert: {
id?: string;
employee_id: string;
author_user_id?: string | null;
author_name: string;
category?: NoteCategory;
note_text: string;
due_date?: string | null;
done?: boolean;
done_at?: string | null;
done_by?: string | null;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["employee_notes"]["Insert"]>;
};
// locked_by/locked_at: wer den Entwurf gerade offen hat. Die Regeln
// hire_drafts_update und _delete lassen nur den Halter schreiben, und
// die Sperre läuft ab (Migration 20260910160000) — sonst nähme ein
// geschlossener Reiter den Entwurf für immer mit.
hire_drafts: {
Row: {
id: string;
created_by: string | null;
step: number;
payload: Record<string, unknown>;
updated_at: string;
locked_by: string | null;
locked_at: string | null;
};
Insert: {
id?: string;
created_by?: string | null;
step?: number;
payload: Record<string, unknown>;
updated_at?: string;
locked_by?: string | null;
locked_at?: string | null;
};
Update: Partial<Database["public"]["Tables"]["hire_drafts"]["Insert"]>;
};
saved_reports: {
Row: { id: string; created_by: string | null; name: string; config: Record<string, unknown>; created_at: string };
Insert: { id?: string; created_by?: string | null; name: string; config: Record<string, unknown>; created_at?: string };
Update: Partial<Database["public"]["Tables"]["saved_reports"]["Insert"]>;
};
audit_log: {
Row: {
id: string;
occurred_at: string;
actor_user_id: string | null;
actor_name: string;
action: string;
target_label: string;
target_employee_id: string | null;
details: string | null;
/**
* Feldweise Änderungen. Null bei Einträgen aus der Zeit vor
* 20260803140000_audit_changes_detail.sql — dort wurden nur die
* Feldnamen behalten, nicht die Werte, und das lässt sich nicht
* nachliefern.
*/
changes: AuditChange[] | null;
};
Insert: {
id?: string;
occurred_at?: string;
actor_user_id?: string | null;
actor_name: string;
action: string;
target_label: string;
target_employee_id?: string | null;
details?: string | null;
changes?: AuditChange[] | null;
};
Update: Partial<Database["public"]["Tables"]["audit_log"]["Insert"]>;
};
pending_org_changes: {
Row: {
id: string;
employee_id: string;
change_type: PendingChangeType;
effective_date: string;
payload: Record<string, unknown>;
status: PendingChangeStatus;
created_by: string | null;
created_at: string;
applied_at: string | null;
};
Insert: {
id?: string;
employee_id: string;
change_type: PendingChangeType;
effective_date: string;
payload: Record<string, unknown>;
status?: PendingChangeStatus;
created_by?: string | null;
created_at?: string;
applied_at?: string | null;
};
Update: Partial<Database["public"]["Tables"]["pending_org_changes"]["Insert"]>;
};
// ── SAP-OM-Modell ──────────────────────────────────────────
// O: rekursiv über parent_id, unit_type ist nur ein Etikett.
org_units: {
Row: {
id: string;
org_number: string;
name: string;
parent_id: string | null;
unit_type: OrgUnitType;
valid_from: string;
valid_to: string | null;
created_at: string;
};
Insert: {
id?: string;
org_number: string;
name: string;
parent_id?: string | null;
unit_type: OrgUnitType;
valid_from?: string;
valid_to?: string | null;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["org_units"]["Insert"]>;
};
// C: Katalog der Tätigkeiten.
jobs: {
Row: { id: string; code: string; title: string; created_at: string };
Insert: { id?: string; code: string; title: string; created_at?: string };
Update: Partial<Database["public"]["Tables"]["jobs"]["Insert"]>;
};
// S: Planstelle. Der Name om_positions stammt aus der Zeit, in der die
// alte positions-Tabelle noch danebenstand; sie ist inzwischen weg.
om_positions: {
Row: {
id: string;
position_number: string;
org_unit_id: string;
job_id: string;
is_chief: boolean;
valid_from: string;
valid_to: string | null;
created_at: string;
};
Insert: {
id?: string;
position_number: string;
org_unit_id: string;
job_id: string;
is_chief?: boolean;
valid_from?: string;
valid_to?: string | null;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["om_positions"]["Insert"]>;
};
// Die Onboarding-Checkliste: je Person und Punkt eine Zeile. Welche
// Punkte es gibt, steht in lib/onboarding.ts — nicht hier.
onboarding_tasks: {
Row: {
id: string;
employee_id: string;
item_key: string;
erledigt: boolean;
wert: string | null;
kommentar: string | null;
created_at: string;
updated_at: string;
updated_by: string | null;
updated_by_name: string | null;
};
Insert: {
id?: string;
employee_id: string;
item_key: string;
erledigt?: boolean;
wert?: string | null;
kommentar?: string | null;
created_at?: string;
updated_at?: string;
updated_by?: string | null;
updated_by_name?: string | null;
};
Update: Partial<Database["public"]["Tables"]["onboarding_tasks"]["Insert"]>;
};
// Die Offboarding-Checkliste — dieselbe Form wie onboarding_tasks, für
// den anderen Weg. Punkte in lib/offboarding.ts.
offboarding_tasks: {
Row: {
id: string;
employee_id: string;
item_key: string;
erledigt: boolean;
wert: string | null;
kommentar: string | null;
created_at: string;
updated_at: string;
updated_by: string | null;
updated_by_name: string | null;
};
Insert: {
id?: string;
employee_id: string;
item_key: string;
erledigt?: boolean;
wert?: string | null;
kommentar?: string | null;
created_at?: string;
updated_at?: string;
updated_by?: string | null;
updated_by_name?: string | null;
};
Update: Partial<Database["public"]["Tables"]["offboarding_tasks"]["Insert"]>;
};
// Kostenstellen. Die Zuordnung hängt an der Planstelle, nicht an der
// Person: der Sitz kostet Geld, auch wenn niemand darauf sitzt.
cost_centers: {
Row: {
id: string;
code: string;
name: string;
org_unit_id: string | null;
valid_from: string;
valid_to: string | null;
created_at: string;
};
Insert: {
id?: string;
code: string;
name: string;
org_unit_id?: string | null;
valid_from?: string;
valid_to?: string | null;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["cost_centers"]["Insert"]>;
};
// A011: Planstelle kontiert auf Kostenstelle, zeitabhängig.
position_cost_centers: {
Row: {
id: string;
position_id: string;
cost_center_id: string;
valid_from: string;
valid_to: string | null;
created_at: string;
};
Insert: {
id?: string;
position_id: string;
cost_center_id: string;
valid_from: string;
valid_to?: string | null;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["position_cost_centers"]["Insert"]>;
};
// A008: Person besetzt Planstelle, zeitabhängig.
position_assignments: {
Row: {
id: string;
position_id: string;
employee_id: string;
valid_from: string;
valid_to: string | null;
created_at: string;
};
Insert: {
id?: string;
position_id: string;
employee_id: string;
valid_from: string;
valid_to?: string | null;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["position_assignments"]["Insert"]>;
// Beide Richtungen: über die Planstelle hängt die Verortung in der
// Organisation, über die Person die Verortung in der Akte. Die
// Einbettung erspart an einem Dutzend Stellen eine zweite Abfrage.
};
// Hinzugewählte Kolleg:innen je Person. Eine Zeile holt deren Notizen
// und Entwürfe in die Ansicht; ohne Zeile sieht man nur die eigenen.
colleague_subscriptions: {
Row: {
user_id: string;
author_user_id: string;
created_at: string;
};
Insert: {
user_id: string;
author_user_id: string;
created_at?: string;
};
Update: Partial<Database["public"]["Tables"]["colleague_subscriptions"]["Insert"]>;
};
};
Views: Record<string, never>;
Functions: {
hire_employee: { Args: { payload: Record<string, unknown> }; Returns: string };
terminate_employee: { Args: { payload: Record<string, unknown> }; Returns: void };
transfer_employee: { Args: { payload: Record<string, unknown> }; Returns: void };
promote_employee: { Args: { payload: Record<string, unknown> }; Returns: void };
start_karenz: { Args: { payload: Record<string, unknown> }; Returns: void };
adjust_karenz_return: { Args: { payload: Record<string, unknown> }; Returns: void };
record_karenz_return: { Args: { payload: Record<string, unknown> }; Returns: void };
change_employee_data: { Args: { payload: Record<string, unknown> }; Returns: void };
rehire_employee: { Args: { payload: Record<string, unknown> }; Returns: void };
add_employee_dependent: { Args: { payload: Record<string, unknown> }; Returns: void };
delete_employee_dependent: { Args: { payload: Record<string, unknown> }; Returns: void };
add_employee_note: { Args: { payload: Record<string, unknown> }; Returns: string };
complete_employee_note: { Args: { payload: Record<string, unknown> }; Returns: void };
// Eigenes Passwort wechseln. Die einzige mutierende Funktion **ohne**
// require_hr_admin() — sonst käme niemand aus dem erzwungenen Wechsel
// heraus, denn solange er aussteht, ist is_hr_user() false.
app_passwort_aendern: { Args: { payload: Record<string, unknown> }; Returns: void };
// Nimmt eine irrtümliche Stammdaten-/Vertragsänderung zurück. Der einzige
// Weg an der fehlenden delete-Policy auf employee_history vorbei.
delete_history_entry: { Args: { payload: Record<string, unknown> }; Returns: void };
// Berichtigt Wert und/oder Datum eines Historieneintrags.
update_history_entry: { Args: { payload: Record<string, unknown> }; Returns: void };
// Planstelle anlegen bzw. schliessen — im OM-Modell Operationen auf
// om_positions, nicht mehr auf einer eigenen Ausschreibungstabelle.
create_position: { Args: { payload: Record<string, unknown> }; Returns: string };
// Kopie einer bestehenden Planstelle — gleiche Einheit, Tätigkeit und
// Kontierung, neue Nummer, unbesetzt. Leitungsplanstellen ausgenommen.
clone_position: { Args: { payload: Record<string, unknown> }; Returns: string };
update_position: { Args: { payload: Record<string, unknown> }; Returns: void };
set_position_cost_center: { Args: { payload: Record<string, unknown> }; Returns: void };
set_onboarding_task: { Args: { payload: Record<string, unknown> }; Returns: void };
start_onboarding: { Args: { payload: Record<string, unknown> }; Returns: number };
set_offboarding_task: { Args: { payload: Record<string, unknown> }; Returns: void };
start_offboarding: { Args: { payload: Record<string, unknown> }; Returns: number };
delete_position: { Args: { payload: Record<string, unknown> }; Returns: void };
is_valid_svnr: { Args: { p_svnr: string; p_birth_date?: string | null }; Returns: boolean };
apply_due_pending_changes: { Args: Record<string, never>; Returns: number };
om_reporting_lines: {
Args: { p_as_of?: string };
Returns: {
employee_id: string;
position_id: string;
org_unit_id: string;
is_chief: boolean;
formal_manager_id: string | null;
acting_manager_id: string | null;
}[];
};
};
};
};