From 83af0d6897c8e678a9b50eef6074e80092630091 Mon Sep 17 00:00:00 2001 From: claude-dev Date: Sat, 25 Jul 2026 15:52:40 +0000 Subject: [PATCH] feat(org-detail): Widerrufen-Knopf entfernt, Warnschwelle 0 = aus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zwei Rückmeldungen des Nutzers. - Der Widerrufen-Knopf in der Lizenz-Tabelle ist raus. Eine neue Lizenz widerruft die alte automatisch, und einen Kunden abschalten erledigt der prominente Org-Status-Umschalter. Der API-Endpoint /revoke bleibt. - Die Warnschwelle lässt sich jetzt abschalten, 0 = aus, in allen drei Formularen (Budget, Neue Lizenz, Org-Anlage) samt Modell- und Endpoint-Validierung. Gegenstück im Monitor ist Commit 17b1886 (bisher fiel eine 0 dort still auf die Voreinstellung 80 zurück). Co-Authored-By: Claude Fable 5 --- src/models.py | 2 +- src/routers/token_usage.py | 4 ++-- src/static/dashboard.html | 14 +++++++------- src/static/js/app.js | 18 +++--------------- tests/test_models.py | 7 +++++++ 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/models.py b/src/models.py index 072198c..ae51f2d 100644 --- a/src/models.py +++ b/src/models.py @@ -36,7 +36,7 @@ class LicenseParams(BaseModel): token_budget_usd: Optional[float] = None credits_total: Optional[int] = None cost_per_credit: Optional[float] = None - budget_warning_percent: Optional[int] = Field(default=80, ge=1, le=100) + budget_warning_percent: Optional[int] = Field(default=80, ge=0, le=100) # 0 = Warnung aus unlimited_budget: bool = False # 'monthly' = Kontingent wird zum Monatswechsel neu gefuellt (der Monitor # setzt den Verbrauch traege zurueck), 'total' = gilt fuer die Laufzeit. diff --git a/src/routers/token_usage.py b/src/routers/token_usage.py index 7996fca..4a19671 100644 --- a/src/routers/token_usage.py +++ b/src/routers/token_usage.py @@ -196,8 +196,8 @@ async def update_budget(license_id: int, data: dict, request: Request, admin=Dep pct = int(data["budget_warning_percent"]) except (TypeError, ValueError): raise HTTPException(status_code=400, detail="budget_warning_percent muss eine Zahl sein") - if not 1 <= pct <= 100: - raise HTTPException(status_code=400, detail="budget_warning_percent muss zwischen 1 und 100 liegen") + if not 0 <= pct <= 100: + raise HTTPException(status_code=400, detail="budget_warning_percent muss zwischen 0 und 100 liegen (0 = Warnung aus)") data["budget_warning_percent"] = pct if "max_users" in data: try: diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 71a9b19..8216a20 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -263,8 +263,8 @@
- - + +
@@ -625,8 +625,8 @@
- - + +
@@ -724,8 +724,8 @@
- - + +
@@ -1162,7 +1162,7 @@
- + diff --git a/src/static/js/app.js b/src/static/js/app.js index 0d6e7cd..6a2321e 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -561,7 +561,6 @@ async function loadOrgLicenses(orgId) { ${l.status === "active" ? ` - ` : ""} @@ -602,20 +601,9 @@ document.addEventListener("DOMContentLoaded", function () { }); }); -function confirmRevokeLicense(licId) { - showConfirm( - "Lizenz widerrufen", - "Soll die Lizenz wirklich widerrufen werden? Nutzer können dann nur noch lesen.", - async () => { - try { - await API.put(`/api/licenses/${licId}/revoke`); - if (currentOrgId) loadOrgLicenses(currentOrgId); - } catch (err) { - showToast(err.message, "error"); - } - } - ); -} +// Der Widerrufen-Knopf ist bewusst entfernt. Eine neue Lizenz widerruft die +// alte automatisch, und einen Kunden abschalten erledigt der Org-Status- +// Umschalter in der Kopfzeile. Der API-Endpoint /revoke bleibt bestehen. // --- Expiring Licenses (global view) --- async function loadExpiringLicenses() { diff --git a/tests/test_models.py b/tests/test_models.py index 0da0ca0..05a7d47 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -54,6 +54,13 @@ def test_license_params_credits_defaults(): assert p.budget_warning_percent == 80 +def test_license_params_warnschwelle_null_ist_aus(): + p = LicenseParams(license_type="trial", budget_warning_percent=0) + assert p.budget_warning_percent == 0 + with pytest.raises(ValidationError): + LicenseParams(license_type="trial", budget_warning_percent=101) + + def test_license_params_period_pattern(): LicenseParams(license_type="trial", credits_period="monthly") LicenseParams(license_type="trial", credits_period="total")