feat(kontingent): Credits-Stellwerte je Lizenz + Lizenz beim Org-Anlegen
Bedienseite für das Credits-Monatskontingent des Monitors. Das Portal setzt jetzt alle Stellwerte, der Monitor bleibt Herr der Laufzeitfelder. - Lizenz-Modal und Budget-Formular um Abrechnungszeitraum (monthly/total), Übertrag-Checkbox und Warnschwelle erweitert, Vorbelegungen auf das beschlossene Modell (10.000 Credits, monatlich, Übertrag an, 0,20 USD). - Neue Organisation kann direkt eine Lizenz mit Kontingent anlegen (OrgCreate.license, verschachtelt). Org- und Lizenz-Insert laufen in EINER Transaktion, schlägt der Lizenz-Teil fehl, entsteht keine halbe Organisation. Gemeinsamer Helper insert_license_row statt Duplikation der Widerruf- und Audit-Logik. - PUT /api/token-usage/budget validiert credits_period (monthly|total) und budget_warning_percent (1 bis 100), credits_rollover wird wie unlimited_budget normalisiert. Laufzeitfelder bleiben bewusst außerhalb der Whitelist. - Overview und Org-Detail rechnen den Rest jetzt gegen verfügbar = Kontingent + Übertrag, dieselbe Bezugsgröße wie der Hard-Stop im Monitor. Neuer read-only Block Laufzeitwerte im Token-Subtab. - Startup-Check loggt einen deutlichen Fehler, wenn die licenses-Tabelle die Credits-Spalten noch nicht hat (falsche Deploy-Reihenfolge). - Migration migrations/2026-07-25_credits_period.py (idempotent, wortgleich mit der Monitor-Migration) für die Portal-Staging-DB, auf Live gilt: Monitor VOR Portal promoten. - 13 neue Modell-Tests, pytest grün (die zwei bekannten shared-Import-Fehlschläge bestehen unverändert seit vorher). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
20
src/main.py
20
src/main.py
@@ -22,6 +22,26 @@ logger = logging.getLogger("verwaltung")
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Schutz gegen falsche Deploy-Reihenfolge: Die Credits-Spalten in licenses
|
||||
# legt die Monitor-Migration an. Fehlen sie, scheitern Lizenz-Anlage und
|
||||
# Token-Tab mit "no such column". Hier nur ein deutlicher Log-Hinweis,
|
||||
# kein Hard-Fail.
|
||||
try:
|
||||
from database import get_db
|
||||
db = await get_db()
|
||||
try:
|
||||
cursor = await db.execute("PRAGMA table_info(licenses)")
|
||||
cols = [row[1] for row in await cursor.fetchall()]
|
||||
if "credits_period" not in cols:
|
||||
logger.error(
|
||||
"licenses-Tabelle ohne Credits-Spalten (credits_period fehlt)! "
|
||||
"Zuerst den Monitor deployen (dessen Migration legt sie an) "
|
||||
"oder migrations/2026-07-25_credits_period.py gegen diese DB fahren."
|
||||
)
|
||||
finally:
|
||||
await db.close()
|
||||
except Exception as exc:
|
||||
logger.warning("Startup-Schema-Check uebersprungen: %s", exc)
|
||||
logger.info("Verwaltungsportal gestartet auf Port %s", PORT)
|
||||
yield
|
||||
logger.info("Verwaltungsportal beendet")
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren