Yesterday's version had it the other way — everyone visible, untick to
hide. The decision from the business side is the opposite: you see your
own notes, and you tick the colleagues you also want. So note_mutes
becomes note_subscriptions and the predicate flips from `not exists` to
`exists`.
The existing rows are not carried over. The meaning inverts rather than
the sign: converting faithfully ("everyone except the muted") would write
almost the whole roster into the new table and reproduce exactly the state
the change is meant to end. Anyone opening the setting tomorrow would
think it had not taken effect. The table is a day old; what is lost is a
few ticks from trying it out.
What this costs is worth saying plainly: the silent case that could not
happen under exceptions can happen now. Do not tick a colleague and you
will not see her follow-ups — not while she is on holiday either. That is
the flip side of the decision, and it is written down in the migration
rather than discovered later.
Each note now says who wrote it. Own notes read "von mir" rather than
repeating your own name, which would sit on every second line and tell
nobody anything. The flag is computed on the server: the user id is
already there, and threading it through four components for one word is a
poor trade. The counter on the button follows the same turn — "+2" for
what you added, nothing when you added nothing.
Verified: 21 tests, five mutation-checked (restoring `not exists`,
dropping the own-notes clause, inverting the default, hiding the author,
and printing your own name instead of "von mir" each turn them red). 489
tests, typecheck, lint, schema drift and build clean. The migration is
reviewed but not run — no reachable database here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
645 lines
24 KiB
TypeScript
645 lines
24 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";
|
|
export type WorkerType = "Angestellte:r" | "Arbeiter:in";
|
|
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";
|
|
|
|
/**
|
|
* 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";
|
|
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;
|
|
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;
|
|
karenz_start_date: string | null;
|
|
karenz_return_date: string | null;
|
|
absence_type: string | null;
|
|
avatar_color: string | null;
|
|
worker_type: WorkerType;
|
|
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;
|
|
/** 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;
|
|
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;
|
|
karenz_start_date?: string | null;
|
|
karenz_return_date?: string | null;
|
|
absence_type?: string | null;
|
|
avatar_color?: string | null;
|
|
worker_type?: WorkerType;
|
|
collective_agreement?: CollectiveAgreement;
|
|
work_days?: Weekday[];
|
|
is_betriebsrat?: boolean;
|
|
has_dienstwagen?: boolean;
|
|
dienstwagen_art?: DienstwagenArt | null;
|
|
has_kuendigungsschutz?: boolean;
|
|
kuendigungsschutz_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"]>;
|
|
};
|
|
hire_drafts: {
|
|
Row: { id: string; created_by: string | null; step: number; payload: Record<string, unknown>; updated_at: string };
|
|
Insert: { id?: string; created_by?: string | null; step?: number; payload: Record<string, unknown>; updated_at?: string };
|
|
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
|
|
// in die Glocke; ohne Zeile sieht man nur die eigenen.
|
|
note_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"]["note_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 };
|
|
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;
|
|
}[];
|
|
};
|
|
};
|
|
};
|
|
};
|