feat(pricing): Sätze kommen aus der Preistabelle, Rechner kann sie speichern

Die Credits-Sätze sind im Portal nicht mehr hart codiert. Neuer Endpoint
GET /api/pricing/tariff liest die geteilte Tabelle billing_tariff (mit
den beschlossenen Werten als Rückfallebene, falls die Tabelle fehlt),
PUT /api/pricing/tariff schreibt sie mit Validierung und Audit-Eintrag.

- Die Infozeile im Verbrauch-Bereich rendert ihre Zahlen aus der Tabelle,
  der statische Text bleibt nur als Rückfallebene stehen.
- Die Rechner-Regler starten auf den aktiven Preisen, der Hinweis nennt
  die tatsächlich abgerechneten Sätze, und der neue Knopf "Diese Sätze
  als aktive Preise speichern" schreibt sie nach Rückfrage in die
  Tabelle. Der Monitor bucht ab der nächsten Aktion mit den neuen Werten.
- Migration migrations/2026-07-25_billing_tariff.py für die eigene
  Staging-DB (auf Live legt die Tabelle der Monitor an und befüllt sie
  mit seinen wirksamen Konfigurationswerten).

Gegenstück im Monitor: Commit 027244a (billing_tariff als Quelle der
Buchung). Promote-Reihenfolge bleibt, erst Monitor, dann Verwaltung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-07-25 14:32:25 +00:00
Ursprung a639579366
Commit c4ea949f91
5 geänderte Dateien mit 257 neuen und 5 gelöschten Zeilen

Datei anzeigen

@@ -0,0 +1,67 @@
"""Migration 2026-07-25: Preistabelle billing_tariff anlegen und befüllen.
Die Tabelle ist die pflegbare Quelle der Credits-Sätze je Aktion. Der Monitor
legt sie beim Start selbst an (init_db, mit seinen Konfigurationswerten als
Erstbefüllung). Die Portal-Staging-Umgebung nutzt aber eine EIGENE Datenbank,
auf der kein Monitor läuft, deshalb braucht sie dieses Skript. Idempotent,
vorhandene Sätze werden NICHT überschrieben (INSERT OR IGNORE).
Ausführung:
DB_PATH=/home/claude-dev/AegisSight-Monitor-staging/data/osint.db python3 migrations/2026-07-25_billing_tariff.py
DB_PATH=/home/claude-dev/osint-data/osint.db python3 migrations/2026-07-25_billing_tariff.py
"""
import os
import sqlite3
import sys
# Beschlossene Verkaufssätze (23.07.2026), identisch zu den Monitor-Defaults
SEED = {
"monitor_adhoc": 45.0,
"monitor_research": 40.0,
"analysis": 12.0,
"factcheck": 12.0,
"chat": 1.0,
"enhance": 1.0,
"globe": 1.0,
}
def main(db_path: str) -> int:
if not os.path.exists(db_path):
print(f"FEHLER: DB nicht gefunden: {db_path}", file=sys.stderr)
return 1
conn = sqlite3.connect(db_path, timeout=60)
conn.execute("PRAGMA busy_timeout = 60000")
conn.execute("PRAGMA journal_mode = WAL")
print(f"Migration auf {db_path}")
conn.execute("""
CREATE TABLE IF NOT EXISTS billing_tariff (
tariff_key TEXT PRIMARY KEY,
credits REAL NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
print(" + billing_tariff angelegt (oder vorhanden)")
for key, credits in SEED.items():
cur = conn.execute(
"INSERT OR IGNORE INTO billing_tariff (tariff_key, credits) VALUES (?, ?)",
(key, credits),
)
if cur.rowcount:
print(f" + Satz {key} = {credits}")
else:
print(f" = Satz {key} war bereits gesetzt")
conn.commit()
conn.close()
print("Migration abgeschlossen.")
return 0
if __name__ == "__main__":
db_path = os.environ.get("DB_PATH", "/home/claude-dev/osint-data/osint.db")
sys.exit(main(db_path))