Feature: Partner-Codes + Gründer-Lizenz-System für Influencer-Kooperationen

- partner_codes Tabelle: Code, Label, max_uses, grants_founder, uses-Counter
- users: is_founder + is_partner Flags (DB-Migration + auth.py SELECT)
- Registrierung: Partner-Code löst Gründer-Lizenz aus (vor User-Referral geprüft)
- API: GET/POST/DELETE /api/admin/partner/codes, POST /api/admin/partner/users/{id}/grant
- API: GET /api/partner/codes/{code}/info (öffentlich, für Registrierungsvalidierung)
- API: GET /api/admin/users/search (Name-Suche für Admin-UI)
- Admin-Tab "Partner & Codes": Code anlegen, Stats, User-Status vergeben
- Registrierungsformular: Einladungscode-Feld mit Live-Validierung
- Settings: Gründer (lila) + Partner (blau) Badge neben Kostenlos/Plus
- SW by-v515, APP_VER 492
This commit is contained in:
rene 2026-04-29 21:20:16 +02:00
parent 810c1a79dc
commit e57c6db013
9 changed files with 469 additions and 20 deletions

View file

@ -559,6 +559,9 @@ def _migrate(conn_factory):
("users", "ki_zucht_paarung", "INTEGER NOT NULL DEFAULT 1"),
("users", "ki_zucht_beschreibung", "INTEGER NOT NULL DEFAULT 1"),
("users", "ki_zucht_jahresbericht", "INTEGER NOT NULL DEFAULT 1"),
# Partner-Code + Gründer-Lizenz
("users", "is_founder", "INTEGER NOT NULL DEFAULT 0"),
("users", "is_partner", "INTEGER NOT NULL DEFAULT 0"),
]
with conn_factory() as conn:
for table, column, col_type in migrations:
@ -1485,6 +1488,25 @@ def _migrate(conn_factory):
CREATE INDEX IF NOT EXISTS idx_bj_user ON breeder_jahresberichte(user_id, jahr DESC);
""")
# Partner-Codes (Influencer-Kooperationen + Gründer-Lizenz)
try:
conn.executescript("""
CREATE TABLE IF NOT EXISTS partner_codes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL UNIQUE COLLATE NOCASE,
label TEXT NOT NULL,
grants_founder INTEGER NOT NULL DEFAULT 1,
max_uses INTEGER DEFAULT NULL,
uses INTEGER NOT NULL DEFAULT 0,
created_by INTEGER REFERENCES users(id),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_partner_codes_code ON partner_codes(code);
""")
logger.info("Migration: partner_codes Tabelle bereit.")
except Exception as e:
logger.warning(f"Migration partner_codes: {e}")
# js_exercise_id zu training_exercises — verbindet training_exercises mit exercise_progress
existing_te = [row[1] for row in conn.execute("PRAGMA table_info(training_exercises)").fetchall()]
if 'js_exercise_id' not in existing_te: