-- Welche Kolleg:innen man in seinen Benachrichtigungen sehen will. -- -- ═══ Was sich damit ändert ═══ -- -- Bisher war die Glocke ein gemeinsamer Topf: jede aktive HR-Person sah jede -- offene Notiz, unabhängig davon, wer sie verfasst hat. Das war so -- abgestimmt und bleibt der Standard — die Einstellung engt ein, sie öffnet -- nichts. -- -- ═══ Warum hier die Ausnahmen stehen und nicht die Auswahl ═══ -- -- Eine Zeile heisst: „die Notizen dieser Person will ich nicht sehen." -- Gespeichert wird also das Abgewählte, nicht das Gewählte. -- -- Der Unterschied fällt auf, sobald jemand Neues in die Personalabteilung -- kommt. Stünde hier die Auswahl, wäre die neue Kollegin für alle unsichtbar, -- bis jede einzelne Person sie anhakt — und niemandem fiele auf, dass ihre -- Wiedervorlagen liegenbleiben. So herum ist sie ab dem ersten Tag sichtbar, -- und wer sie ausblenden will, tut das ausdrücklich. -- -- Dieselbe Überlegung wie bei den Notizen selbst: die stille Lücke ist -- schlimmer als eine Zeile zu viel. create table if not exists note_mutes ( -- Wessen Einstellung das ist. user_id uuid not null references app_users(id) on delete cascade, -- Wessen Notizen ausgeblendet werden. muted_user_id uuid not null references app_users(id) on delete cascade, created_at timestamptz not null default now(), primary key (user_id, muted_user_id), -- Die eigenen Notizen lassen sich nicht abwählen. Sie sind der Grund, -- warum es die Glocke gibt; ein Haken, der die eigene Wiedervorlage -- verschwinden lässt, wäre eine Falle. constraint chk_note_mutes_nicht_selbst check (user_id <> muted_user_id) ); comment on table note_mutes is 'Abgewählte Kolleg:innen je Person. Eine Zeile blendet die Notizen von muted_user_id für user_id aus. Ohne Zeile ist alles sichtbar.'; -- Der Zugriffsweg fragt immer „meine Ausnahmen": ohne Index ein Tabellen- -- scan je Seitenaufruf, mit ihm ein Indexzugriff. Bei einer Handvoll HR- -- Personen ist das heute belanglos und morgen nicht mehr. create index if not exists idx_note_mutes_user on note_mutes (user_id); alter table note_mutes enable row level security; -- Eigentümergebunden wie hire_drafts und saved_reports: man sieht und -- ändert ausschliesslich die eigenen Zeilen. Eine fremde Einstellung geht -- niemanden etwas an — auch keine andere HR-Person. drop policy if exists "note_mutes_owner" on note_mutes; create policy "note_mutes_owner" on note_mutes for all using (user_id = app_current_user_id() and is_hr_user()) with check (user_id = app_current_user_id() and is_hr_user()); -- ═══ Gegenprobe ═══════════════════════════════════════════════════ do $$ begin if not exists ( select 1 from pg_tables where tablename = 'note_mutes' and rowsecurity ) then raise exception 'note_mutes hat keinen Zeilenschutz.'; end if; if not exists ( select 1 from pg_policies where tablename = 'note_mutes' and policyname = 'note_mutes_owner' ) then raise exception 'Die Eigentümerregel auf note_mutes fehlt.'; end if; -- Der Selbstbezug muss abgewiesen werden. Ohne diese Prüfung liesse sich -- die eigene Wiedervorlage ausblenden. begin insert into note_mutes (user_id, muted_user_id) values ('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000001'); raise exception 'Eine Person kann sich selbst abwaehlen — die Pruefbedingung greift nicht.'; exception when check_violation then null; -- so soll es sein when foreign_key_violation then null; -- Fremdschluessel zuerst: ebenfalls abgewiesen end; end $$;