Feature: QR-Kontingente für Partner — Bestellung, Übergabe, Rückverfolgung
Partner verteilen gedruckte QR-Codes (Sticker/Flyer); jeder physische Code
ist einzeln rückverfolgbar von Scan bis Registrierung.
Backend:
- partner_qr_batches + partner_qr_codes (Token 8-stellig, ohne 0/O/1/l/I),
users.referred_qr, partner_codes.owner_user_id (+Backfill über referred_by)
- /q/{token}: Scan zählen (scans, first/last_scan_at) → Redirect
/?ref=CODE&qr=TOKEN — dockt am bestehenden Referral-Flow an
- Registrierung: qr_token wird nur zugeordnet, wenn er zum eingelösten
Partner-Code gehört (Manipulationsschutz)
- Admin: Kontingent bestellen (max 500), Liste mit Scans/Registrierungen,
Löschen (Zweiklick), druckfertiges A4-PDF (segno+fpdf2, 3×4 Grid mit
Kurz-URL + laufender Nummer), Code-Besitzer zuordnen
- Partner-Self-Service: /partner/my-qr (+PDF) für Code-Besitzer
Frontend:
- Admin-Partner-Tab: Karte 'QR-Kontingente' (Bestellung, Stats, PDF, Besitzer)
- Partner-Profil: 'Meine QR-Codes' mit Scans/Registrierungen + PDF-Download
- boot.js/app.js speichern ?qr=, Registrierung schickt qr_token mit
Neu: segno==1.6.6 (pure-python QR). Tests: 5 neue (PDF, Scan-Zählung,
Attribution, Fremd-Token-Schutz, Self-Service). Suite: 51 passed.
This commit is contained in:
parent
cadfb24a8d
commit
f604ab7c4f
16 changed files with 621 additions and 23 deletions
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '1256'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '1257'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
||||
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
||||
window.APP_VERSION = APP_VERSION;
|
||||
|
|
@ -1140,10 +1140,13 @@ const App = (() => {
|
|||
// überlebt App-Schließen, sodass die Zuordnung auch bei späterer Registrierung klappt)
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const refCode = urlParams.get('ref');
|
||||
const qrToken = urlParams.get('qr');
|
||||
if (refCode) {
|
||||
try {
|
||||
localStorage.setItem('by_ref_code', refCode.toUpperCase());
|
||||
localStorage.setItem('by_ref_code_ts', String(Date.now()));
|
||||
// Partner-QR-Token (Sticker/Flyer) für Einzelcode-Rückverfolgung mitspeichern
|
||||
if (qrToken) localStorage.setItem('by_qr_token', qrToken);
|
||||
} catch {}
|
||||
// URL bereinigen ohne Reload
|
||||
history.replaceState({}, '', window.location.pathname + window.location.hash);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue