Feat: Rabattsystem in Rechnungserstellung integriert (Gründer/Referral)

- _get_discount_info() Hilfsfunktion in admin.py (Gründer 100%, Referral-Stufen 20/30/50%, von Gründer eingeladen 50%)
- list_upgrade_requests liefert discount_pct + discount_reason pro User
- GET /admin/users/{user_id}/discount Endpoint
- _handle_upgrade_invoices nutzt Rabatt für amount_net/discount_pct/after_disc + passende Notiz
- scheduler.py _create_renewal_invoice_draft: inline Rabattberechnung + korrekte Beträge
- admin.js: Discount-Badge in Upgrade-Card, data-Attribute am Invoice-Button, _discountNote(), discount_pct + notes im Modal vorbelegt
This commit is contained in:
rene 2026-05-15 12:21:33 +02:00
parent db4d5cb1b6
commit 2163169b73
3 changed files with 132 additions and 11 deletions

View file

@ -246,6 +246,49 @@ async def _create_renewal_invoice_draft(user: dict, expires: date, tier_label: s
).fetchone()
billing_address = row["billing_address"] if row else None
# Rabatt berechnen (inline, da kein Admin-Import möglich)
disc_row = conn.execute(
"""SELECT u.is_founder, u.is_founder_pending, u.referred_by,
COALESCE((SELECT COUNT(*) FROM users WHERE referred_by=u.id), 0) AS referral_count
FROM users u WHERE u.id=?""",
(user["id"],)
).fetchone()
discount_pct = 0
discount_reason = None
referral_count = 0
if disc_row:
referral_count = disc_row["referral_count"]
if disc_row["is_founder"] or disc_row["is_founder_pending"]:
discount_pct = 100
discount_reason = "founder"
elif (disc_row["referred_by"] or 0) > 0:
ref = conn.execute(
"SELECT is_founder, is_founder_pending FROM users WHERE id=?",
(disc_row["referred_by"],)
).fetchone()
if ref and (ref["is_founder"] or ref["is_founder_pending"]):
discount_pct = 50
discount_reason = "referred_by_founder"
if not discount_reason:
for thr, pct in [(50, 50), (20, 30), (10, 20)]:
if referral_count >= thr:
discount_pct = pct
discount_reason = "referral"
break
discount_amt = round(price * discount_pct / 100, 2)
after_disc = round(price - discount_amt, 2)
_AGB = "Jahresbeitrag gem. AGB. Bei vorzeitiger Kündigung keine anteilige Rückerstattung; Zugang bleibt bis Laufzeitende bestehen."
if discount_reason == "founder":
notes = f"Gründer-Sonderkonditionen: {tier_label} kostenfrei als Dankeschön für deine Unterstützung als Gründer! {_AGB} (Automatisch erstellt, Ablauf: {expires.strftime('%d.%m.%Y')})"
elif discount_reason == "referred_by_founder":
notes = f"Willkommen in der Gründer-Community! Als persönlich von einem Gründer eingeladenes Mitglied erhältst du dauerhaft {discount_pct}% Rabatt. {_AGB} (Automatisch erstellt, Ablauf: {expires.strftime('%d.%m.%Y')})"
elif discount_reason == "referral":
notes = f"Herzlichen Dank für deine Unterstützung! Für {referral_count} geworbene Freunde erhältst du {discount_pct}% Rabatt. {_AGB} (Automatisch erstellt, Ablauf: {expires.strftime('%d.%m.%Y')})"
else:
notes = f"{_AGB} (Automatisch erstellt, Ablauf: {expires.strftime('%d.%m.%Y')})"
invoice_number = _next_invoice_number(conn)
description = f"{tier_label} Jahresabo (Verlängerung)"
conn.execute("""
@ -253,12 +296,11 @@ async def _create_renewal_invoice_draft(user: dict, expires: date, tier_label: s
(invoice_number, user_id, recipient_name, recipient_email, recipient_address,
description, service_period, amount_net, discount_pct, discount_amount,
amount_after_discount, tax_rate, tax_amount, amount_gross, notes)
VALUES (?,?,?,?,?,?,?,?,0,0,?,0,0,?,?)
VALUES (?,?,?,?,?,?,?,?,?,?,?,0,0,?,?)
""", (
invoice_number, user["id"], user["name"], user["email"], billing_address,
description, period,
price, price, price,
f"Jahresbeitrag gem. AGB. Bei vorzeitiger Kündigung keine anteilige Rückerstattung; Zugang bleibt bis Laufzeitende bestehen. (Automatisch erstellt, Ablauf: {expires.strftime('%d.%m.%Y')})",
price, discount_pct, discount_amt, after_disc, after_disc, notes,
))
conn.execute(
"INSERT INTO invoice_items (invoice_id, description, quantity, unit_price, total) VALUES (?,?,1,?,?)",