Gründer-Tickets: 50%-Rabatt-Weitergabe pro Gründer gedeckelt + Pro-Wording korrigiert
Rene: 'ungern jemandem auf ewig die Möglichkeit geben 50% Rabatt zu vergeben — bei 100 Gründern ein großer Faktor. Ich hätte jedem 25–50 Tickets gegeben.' - users.founder_referral_tickets (Default 25): Kontingent an 50%-Rabatten, die ein Gründer an Geworbene weitergeben kann. Technisch = die ersten N VERIFIZIERTEN Geworbenen (nach Anmeldedatum) bekommen 50%, danach 0. Unbestätigte verbrauchen kein Ticket. In scheduler.py (Rechnung) + admin.py (Vorschau) konsistent. - BUGFIX nebenbei: admin.py zeigte für referred_by_founder fälschlich 100% statt 50% (scheduler war korrekt) — jetzt beide 50%. - Admin: Grant-Formular bekommt Feld 'Gründer-Tickets' (0–200, Vorbelegung aus User-Stand); Endpoint /grant akzeptiert founder_tickets. - Gründer-Seite + Settings + Admin-Hilfe: 'sobald Bezahlfunktionen aktiv sind' raus (Pro kostet bereits); Vorteil 'lebenslang Pro gratis' + '25 Freunde zum halben Preis' (Ticket-Framing). - Tests: test_founder_tickets.py (Cap, Unverified-Schutz, 50%-Bugfix, Grant). Suite: 64 passed.
This commit is contained in:
parent
98ec6c36c6
commit
60fb866283
13 changed files with 154 additions and 35 deletions
|
|
@ -1352,10 +1352,19 @@ def _get_discount_info(conn, user_id: int) -> dict:
|
|||
referred_by = row["referred_by"] or 0
|
||||
if referred_by > 0:
|
||||
referrer = conn.execute(
|
||||
"SELECT is_founder, is_founder_pending FROM users WHERE id=?", (referred_by,)
|
||||
"SELECT is_founder, is_founder_pending, founder_referral_tickets FROM users WHERE id=?", (referred_by,)
|
||||
).fetchone()
|
||||
if referrer and (referrer["is_founder"] or referrer["is_founder_pending"]):
|
||||
return {"discount_pct": 100, "reason": "referred_by_founder", "referral_count": row["referral_count"]}
|
||||
# 50%-Weitergabe nur innerhalb des Ticket-Kontingents des Gründers
|
||||
# (Rang unter den verifizierten Geworbenen ≤ Tickets). 50%, NICHT 100%.
|
||||
rank = conn.execute(
|
||||
"""SELECT COUNT(*) FROM users
|
||||
WHERE referred_by=? AND email_verified=1
|
||||
AND created_at <= (SELECT created_at FROM users WHERE id=?)""",
|
||||
(referred_by, user_id)
|
||||
).fetchone()[0]
|
||||
if rank <= (referrer["founder_referral_tickets"] or 0):
|
||||
return {"discount_pct": 50, "reason": "referred_by_founder", "referral_count": row["referral_count"]}
|
||||
|
||||
count = row["referral_count"]
|
||||
for threshold, pct in [(50, 50), (20, 30), (10, 20)]:
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class PartnerCodeCreate(BaseModel):
|
|||
class GrantRequest(BaseModel):
|
||||
is_founder: Optional[int] = None
|
||||
is_partner: Optional[int] = None
|
||||
founder_tickets: Optional[int] = Field(None, ge=0, le=200) # 50%-Rabatt-Kontingent
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -129,8 +130,10 @@ def grant_user_status(user_id: int, data: GrantRequest, user=Depends(require_adm
|
|||
updates["is_founder"] = data.is_founder
|
||||
if data.is_partner is not None:
|
||||
updates["is_partner"] = data.is_partner
|
||||
if data.founder_tickets is not None:
|
||||
updates["founder_referral_tickets"] = data.founder_tickets
|
||||
if not updates:
|
||||
raise HTTPException(400, "Mindestens is_founder oder is_partner muss angegeben werden.")
|
||||
raise HTTPException(400, "Mindestens is_founder, is_partner oder founder_tickets muss angegeben werden.")
|
||||
with db() as conn:
|
||||
target = conn.execute(
|
||||
"SELECT id, is_founder, founder_number FROM users WHERE id=?", (user_id,)
|
||||
|
|
@ -167,7 +170,7 @@ def grant_user_status(user_id: int, data: GrantRequest, user=Depends(require_adm
|
|||
(*updates.values(), user_id)
|
||||
)
|
||||
row = conn.execute(
|
||||
"SELECT id, name, email, is_founder, is_partner, founder_number FROM users WHERE id=?",
|
||||
"SELECT id, name, email, is_founder, is_partner, founder_number, founder_referral_tickets FROM users WHERE id=?",
|
||||
(user_id,)
|
||||
).fetchone()
|
||||
return dict(row)
|
||||
|
|
@ -178,7 +181,7 @@ def search_users(q: str, user=Depends(require_admin)):
|
|||
"""User-Suche für Admin (Name-Präfix, max. 10 Ergebnisse)."""
|
||||
with db() as conn:
|
||||
rows = conn.execute(
|
||||
"""SELECT id, name, email, is_founder, is_partner, rolle
|
||||
"""SELECT id, name, email, is_founder, is_partner, rolle, founder_referral_tickets
|
||||
FROM users WHERE name LIKE ? COLLATE NOCASE
|
||||
ORDER BY name LIMIT 10""",
|
||||
(f"{q}%",)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue