Feature: Dank-Mail an Partner bei bestätigter Registrierung — mit Statistik

Trigger ist die E-Mail-Bestätigung des Geworbenen (nicht die rohe
Registrierung — konsistent zur Registrierungen/Versuche-Zählung) und nur
beim ersten Verify (Doppelklick auf den Link = keine zweite Mail).

Inhalt: Dank + Bilanz (bestätigte Registrierungen gesamt + diesen Monat),
bei QR-Herkunft der Sticker (#seq, Kontingent-Label), bei Gründer-Codes
die offenen Plätze; CTA zur Partner-Statistik. Versand über das
partner@-Konto, Fehler landen in failed_emails (context partner_thank_you).

Env-Fund dabei: SMTP_PASS fehlte in BEIDEN .env (nur SMTP_SUPPORT_PASS da)
— Partner-Konto-Versand wäre fehlgeschlagen; auf der DS ergänzt.
Test: Mail-Capture per monkeypatch, prüft Statistik + Sticker-Nr +
Einmaligkeit. Suite grün.
This commit is contained in:
rene 2026-06-07 18:51:54 +02:00
parent df2f42f8ac
commit 3d7d5dc1c4
7 changed files with 144 additions and 16 deletions

View file

@ -397,6 +397,85 @@ async def me(user=Depends(get_current_user)):
return data
def _notify_partner_registration(user_id: int):
"""Dank-Mail an den Partner (Code-Besitzer), wenn ein Geworbener seine
E-Mail bestätigt hat inkl. kleiner Statistik. Best effort."""
import html as _html
with db() as conn:
u = conn.execute(
"SELECT referred_by, referred_qr FROM users WHERE id=?", (user_id,)
).fetchone()
if not u or (u["referred_by"] or 0) >= 0:
return # kein Partner-Code im Spiel
code_id = -u["referred_by"]
pc = conn.execute(
"""SELECT pc.code, pc.label, pc.grants_founder, pc.owner_user_id,
o.name AS owner_name, o.email AS owner_email
FROM partner_codes pc
LEFT JOIN users o ON o.id = pc.owner_user_id
WHERE pc.id=?""",
(code_id,)
).fetchone()
if not pc or not pc["owner_email"]:
return # Code ohne Besitzer → niemand zu benachrichtigen
total = conn.execute(
"SELECT COUNT(*) FROM users WHERE referred_by=? AND email_verified=1",
(-code_id,)
).fetchone()[0]
month = conn.execute(
"""SELECT COUNT(*) FROM users
WHERE referred_by=? AND email_verified=1
AND strftime('%Y-%m', created_at) = strftime('%Y-%m', 'now')""",
(-code_id,)
).fetchone()[0]
qr_line = ""
if u["referred_qr"]:
qr = conn.execute(
"""SELECT q.seq, b.label FROM partner_qr_codes q
JOIN partner_qr_batches b ON b.id = q.batch_id
WHERE q.token=?""",
(u["referred_qr"],)
).fetchone()
if qr:
qr_line = f"Gekommen über deinen gedruckten QR-Code #{qr['seq']} (Kontingent „{qr['label']}“)."
founder_line = ""
if pc["grants_founder"]:
founders = conn.execute(
"SELECT COUNT(*) FROM users WHERE is_founder=1"
).fetchone()[0]
founder_line = f"Noch {max(0, 100 - founders)} von 100 Gründer-Plätzen frei."
subject = "🐾 Danke! Neue Registrierung über deinen Partner-Code"
_oname = _html.escape(pc["owner_name"] or "Partner")
stats_html = (
f"<p style='margin:0 0 16px'>Deine Bilanz mit dem Code <b>{pc['code']}</b>:<br>"
f"<b>{total}</b> bestätigte Registrierung{'en' if total != 1 else ''} insgesamt · "
f"<b>{month}</b> in diesem Monat.</p>"
)
body_html = f"""
<p style="margin:0 0 16px">Hallo <b>{_oname}</b>,</p>
<p style="margin:0 0 16px">
gerade hat ein neuer Hundefreund seine Registrierung über deinen
Partner-Code bestätigt danke, dass du Ban Yaro weiterträgst! 🎉
</p>
{f'<p style="margin:0 0 16px">{_html.escape(qr_line)}</p>' if qr_line else ''}
{stats_html}
{f'<p style="margin:0 0 16px;color:#888">{_html.escape(founder_line)}</p>' if founder_line else ''}"""
plain = (f"Hallo {pc['owner_name'] or 'Partner'},\n\n"
f"gerade hat ein neuer Hundefreund seine Registrierung über deinen Partner-Code bestätigt — danke!\n"
+ (f"\n{qr_line}\n" if qr_line else "")
+ f"\nDeine Bilanz mit dem Code {pc['code']}: {total} bestätigte Registrierungen insgesamt, {month} in diesem Monat.\n"
+ (f"{founder_line}\n" if founder_line else "")
+ f"\nDeine Statistik: {_APP_URL}/#partner-profil\n")
try:
from routes.outreach import _send_smtp
from mailer import email_html
html = email_html(body_html, cta_url=f"{_APP_URL}/#partner-profil", cta_label="Meine Partner-Statistik")
_send_smtp(pc["owner_email"], subject, plain, "partner", html=html)
except Exception as exc:
_log_smtp_failure(pc["owner_email"], subject, plain, exc, context="partner_thank_you")
@router.get("/verify-email/{token}")
async def verify_email(token: str):
with db() as conn:
@ -409,6 +488,9 @@ async def verify_email(token: str):
"UPDATE users SET email_verified=1, verification_token=NULL WHERE id=?",
(row["id"],)
)
# Dank-Mail an den Partner — nur beim ERSTEN Bestätigen (Link doppelt geklickt = kein Spam)
if not row["email_verified"]:
_notify_partner_registration(row["id"])
return RedirectResponse(f"{_APP_URL}/#settings?verified=1", status_code=302)