From ea00480df15465ef28f55f2f141765ac46bd053e Mon Sep 17 00:00:00 2001 From: Thomas Stallinger Date: Fri, 9 Oct 2026 11:50:13 +0200 Subject: [PATCH] Bereitstellungsvorlagen: archivierte Geraete/abgelaufene Medien zaehlen nicht als Nutzung Nutzung und Loeschsperre zaehlen nur aktive Geraete und gueltige Installationsmedien. Beim Loeschen werden Zuordnungen archivierter Geraete und abgelaufene/widerrufene Medien mit entfernt (sonst blockieren die Fremdschluessel). Anlass: Liebherrs Entwicklungsprofile hingen nur noch an Altlasten vom 08./09.09. Co-Authored-By: Claude Opus 5.5 --- app.py | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/app.py b/app.py index 6a184ba..95a5be6 100644 --- a/app.py +++ b/app.py @@ -755,8 +755,10 @@ def fetch_templates(organization_id: str) -> list[dict]: w.id, w.name, b.id, b.name, b.version, bv.partitioning, bv.disk_encryption, bv.secure_boot_required, - (SELECT count(*) FROM assignments a WHERE a.bereitstellungsvorlage_id = bv.id), - (SELECT count(*) FROM enrollment_sessions es WHERE es.bereitstellungsvorlage_id = bv.id) + (SELECT count(*) FROM assignments a JOIN devices d ON d.id = a.device_id + WHERE a.bereitstellungsvorlage_id = bv.id AND d.archived_at IS NULL), + (SELECT count(*) FROM enrollment_sessions es + WHERE es.bereitstellungsvorlage_id = bv.id AND es.revoked_at IS NULL AND es.expires_at > now()) FROM bereitstellungsvorlagen bv JOIN workspaces w ON w.id = bv.workspace_id JOIN backends b ON b.id = bv.backend_id @@ -777,8 +779,9 @@ def fetch_templates(organization_id: str) -> list[dict]: "partitioning": row[8], "disk_encryption": row[9], "secure_boot_required": row[10], - # GUI-Runde 09.10.2026 (Punkt 5): Nutzung - solange Geräte oder - # Installationsmedien darauf verweisen, ist Löschen gesperrt. + # GUI-Runde 09.10.2026 (Punkt 5): Nutzung - nur aktive Geräte und + # gültige Installationsmedien; solange es die gibt, ist Löschen + # gesperrt (archivierte Geräte/abgelaufene Medien zählen nicht). "geraete_anzahl": row[11], "medien_anzahl": row[12], } @@ -6617,17 +6620,37 @@ def delete_bereitstellungsvorlage_endpoint( with conn.cursor() as cur: cur.execute( """ - SELECT (SELECT count(*) FROM assignments WHERE bereitstellungsvorlage_id = %s) - + (SELECT count(*) FROM enrollment_sessions WHERE bereitstellungsvorlage_id = %s) + SELECT (SELECT count(*) FROM assignments a JOIN devices d ON d.id = a.device_id + WHERE a.bereitstellungsvorlage_id = %(v)s AND d.archived_at IS NULL) + + (SELECT count(*) FROM enrollment_sessions + WHERE bereitstellungsvorlage_id = %(v)s AND revoked_at IS NULL AND expires_at > now()) """, - (vorlage_id, vorlage_id), + {"v": vorlage_id}, ) (verweise,) = cur.fetchone() if verweise: return { "success": False, "error": "in_benutzung", - "message": "Die Vorlage wird noch von Geräten oder Installationsmedien verwendet und kann nicht gelöscht werden.", + "message": "Die Vorlage wird noch von aktiven Geräten oder gültigen Installationsmedien verwendet und kann nicht gelöscht werden.", } + # Altlasten mit entfernen (09.10.2026): Zuordnungen archivierter + # Geräte und abgelaufene/widerrufene Medien - sonst blockieren die + # Fremdschlüssel. Ein archiviertes Gerät, das sich neu installiert, + # bekommt bei der Installation ohnehin eine neue Zuordnung. + cur.execute( + """ + DELETE FROM assignments a USING devices d + WHERE d.id = a.device_id AND a.bereitstellungsvorlage_id = %s AND d.archived_at IS NOT NULL + """, + (vorlage_id,), + ) + cur.execute( + """ + DELETE FROM enrollment_sessions + WHERE bereitstellungsvorlage_id = %s AND (revoked_at IS NOT NULL OR expires_at <= now()) + """, + (vorlage_id,), + ) cur.execute( "DELETE FROM bereitstellungsvorlagen WHERE id = %s AND organization_id = %s", (vorlage_id, organization_id),