From dc9bc40991dd807f4416403f5cffae79d1073bc8 Mon Sep 17 00:00:00 2001 From: Thomas Stallinger Date: Tue, 4 Aug 2026 10:06:45 +0200 Subject: [PATCH] fix: default Auftragskatalog state to workspace membership, not absent fetch_auftragskatalog_state() hardcoded absent as the default when no device_merkmale row exists (COALESCE(dm.aktiv, FALSE)), regardless of whether the Merkmal is part of the device's workspace. Since the catalog is meant to be the same pool of Merkmale that workspaces are composed from (see ADR-0010's clarification), that default was wrong: flagging an already workspace-composed, actively-in-use Merkmal as catalog-eligible would silently remove it from every device using that workspace on their next check-in, without anyone ever deselecting it. Now joins workspace_merkmale for the device's assigned workspace and defaults to present when the Merkmal is a member, absent otherwise; an explicit device_merkmale row always overrides that default in either direction. Verified against anode: catalog-eligible + already workspace-composed Merkmal now defaults to present with no device_merkmale row, explicit aktiv=false overrides to absent, explicit aktiv=true overrides back to present. Co-Authored-By: Claude Sonnet 5 --- app.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index 02ece64..7eb4bfb 100644 --- a/app.py +++ b/app.py @@ -382,16 +382,23 @@ def fetch_assigned_blueprints(device_id: str): def fetch_auftragskatalog_state(device_id: str): """ Voller Soll-Zustand (present/absent) aller katalogfähigen Merkmale für - das Backend des Device, unabhängig von der Workspace-Zuweisung - (siehe ADR-0010). Zustandslos aus der aktuellen Auswahl abgeleitet, - keine Historie nötig. + das Backend des Device (siehe ADR-0010). Zustandslos aus der aktuellen + Auswahl abgeleitet, keine Historie nötig. + + Default ohne explizite Auftragszuweisung ist die Workspace-Zugehörigkeit: + der Auftragskatalog besteht aus denselben Merkmalen, aus denen auch + Workspaces zusammengesetzt werden, und ein Workspace ist fachlich nichts + anderes als eine Vorauswahl aus dem Katalog. Eine vorhandene + device_merkmale-Zeile überschreibt diesen Default in beide Richtungen + (auch ein workspace-komponiertes Merkmal lässt sich damit geräteweise + abwählen). """ with get_database_connection() as conn: with conn.cursor() as cur: cur.execute( """ - SELECT bv.backend_id + SELECT bv.workspace_id, bv.backend_id FROM assignments a JOIN bereitstellungsvorlagen bv ON bv.id = a.bereitstellungsvorlage_id WHERE a.device_id = %s @@ -403,18 +410,21 @@ def fetch_auftragskatalog_state(device_id: str): if assignment_row is None: return [] - (backend_id,) = assignment_row + workspace_id, backend_id = assignment_row cur.execute( """ - SELECT m.key, bp.ansible_role, COALESCE(dm.aktiv, FALSE) + SELECT m.key, bp.ansible_role, + COALESCE(dm.aktiv, wm.merkmal_id IS NOT NULL) FROM merkmale m JOIN blueprints bp ON bp.merkmal_id = m.id AND bp.backend_id = %s LEFT JOIN device_merkmale dm ON dm.merkmal_id = m.id AND dm.device_id = %s + LEFT JOIN workspace_merkmale wm + ON wm.merkmal_id = m.id AND wm.workspace_id = %s WHERE m.im_auftragskatalog """, - (backend_id, device_id), + (backend_id, device_id, workspace_id), ) return [ {