provisioning-server/migrations/0018_organisationseinheiten_gruppen.sql
Thomas Stallinger 25e1c5525b feat: Organisationseinheiten (OEs) + Gruppen für Konfigurationsgruppen
Neues Datenmodell (Migration 0018/0019 + Backfill-Script):
- organisationseinheiten: Baum pro Organisation, ein Gerät gehört zu
  genau einer OE (devices.oe_id). Jede Organisation bekommt automatisch
  eine geschützte Standard-OE 'Neue Geräte' als Landeplatz für neu
  aktivierte Geräte (create_organization() legt sie mit an,
  load_or_create_device() weist sie neuen Geräten zu).
- gruppen + device_gruppen: flach, M:N - ein Gerät kann in mehreren
  Gruppen gleichzeitig sein.
- oe_merkmale/gruppen_merkmale: sparsame present/absent-Overrides,
  gleiches Muster wie device_merkmale.

Resolution-Algorithmus (resolve_katalog_overrides): Workspace-Default →
OE-Kette (spezifischste OE gewinnt, klassische Vererbung) → Gruppen
('mehr gewinnt' bei Widerspruch, OE-Kettenergebnis zählt als weitere
Stimme) → Geräte-Override (gewinnt immer, wie bisher).
fetch_auftragskatalog_listing() liefert zusätzlich die Herkunft
('workspace'/'oe'/'gruppe'/'geraet') für die Kundenportal-UI.

Neue Endpunkte: CRUD für OEs/Gruppen, Merkmale-Select/Deselect/Unset je
OE/Gruppe, Geräte-OE-Verschiebung, Geräte-Gruppen-Mitgliedschaft.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 16:40:34 +02:00

84 lines
2.7 KiB
PL/PgSQL

BEGIN;
-- Organisationseinheiten (OEs): Baum pro Organisation, ein Geraet gehoert zu
-- genau einer OE. "Neue Geraete" ist pro Organisation die einzige Zeile mit
-- ist_standard=true - Landeplatz fuer neu aktivierte Geraete, geschuetzt vor
-- Loeschen (siehe app.py). parent_id = NULL heisst "oberste Ebene direkt
-- unter der Organisation" - kein eigener Wurzel-Datensatz noetig, die
-- Organisation selbst ist implizit die Wurzel.
CREATE TABLE organisationseinheiten (
id UUID PRIMARY KEY,
organization_id UUID NOT NULL
REFERENCES organizations(id)
ON DELETE CASCADE,
parent_id UUID
REFERENCES organisationseinheiten(id)
ON DELETE RESTRICT,
name TEXT NOT NULL,
ist_standard BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (organization_id, parent_id, name)
);
CREATE UNIQUE INDEX organisationseinheiten_one_standard_per_org
ON organisationseinheiten (organization_id)
WHERE ist_standard;
-- Nullable hier: bestehende Geraete brauchen erst einen Backfill
-- (scripts/backfill_organisationseinheiten.py), bevor NOT NULL erzwungen
-- werden kann (siehe Migration 0019).
ALTER TABLE devices ADD COLUMN oe_id UUID
REFERENCES organisationseinheiten(id)
ON DELETE RESTRICT;
-- Gruppen: flach (keine Verschachtelung), ein Geraet kann in mehreren
-- gleichzeitig sein (device_gruppen als M:N).
CREATE TABLE gruppen (
id UUID PRIMARY KEY,
organization_id UUID NOT NULL
REFERENCES organizations(id)
ON DELETE CASCADE,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (organization_id, name)
);
CREATE TABLE device_gruppen (
device_id UUID NOT NULL
REFERENCES devices(id)
ON DELETE CASCADE,
gruppe_id UUID NOT NULL
REFERENCES gruppen(id)
ON DELETE CASCADE,
PRIMARY KEY (device_id, gruppe_id)
);
-- Sparsame Merkmal-Overrides je OE/Gruppe, gleiches Muster wie
-- device_merkmale (0007_auftragskatalog.sql): nur eine Zeile, wenn diese
-- OE/Gruppe tatsaechlich eine explizite Meinung zu diesem Merkmal hat.
CREATE TABLE oe_merkmale (
oe_id UUID NOT NULL
REFERENCES organisationseinheiten(id)
ON DELETE CASCADE,
merkmal_id UUID NOT NULL
REFERENCES merkmale(id)
ON DELETE RESTRICT,
aktiv BOOLEAN NOT NULL,
optionen JSONB NOT NULL DEFAULT '{}'::jsonb,
PRIMARY KEY (oe_id, merkmal_id)
);
CREATE TABLE gruppen_merkmale (
gruppe_id UUID NOT NULL
REFERENCES gruppen(id)
ON DELETE CASCADE,
merkmal_id UUID NOT NULL
REFERENCES merkmale(id)
ON DELETE RESTRICT,
aktiv BOOLEAN NOT NULL,
optionen JSONB NOT NULL DEFAULT '{}'::jsonb,
PRIMARY KEY (gruppe_id, merkmal_id)
);
COMMIT;