Fix attendee_types.code uniqueness: per-event not global

Also update generate_type_code() to accept event_id parameter
for proper per-event uniqueness checking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 14:45:27 +00:00
parent 7485357a07
commit 3b14155594
3 changed files with 79 additions and 10 deletions
+74 -7
View File
@@ -176,7 +176,20 @@ ENGLISH_TRANSLATIONS = {
'welcome': 'Welcome to NetEvents',
'connect_with_professionals': 'Connect with professionals at networking events',
'register_as_organizer': 'Register as Organizer',
'already_have_account': 'Already have an account? Login as:',
'already_have_account': 'Already have an account?',
'dont_have_account': "Don't have an account?",
'click_here': 'Click here',
'login_as_organiser': 'login as organiser',
'attendee_profile_link': 'Are you an attendee and want to check your personal profile page?',
'attendee_profile_url': '/attendee/personal/',
'request_profile_link': 'Request Your Personal Page Link',
'request_link_description': 'Enter your email address and we will send you a link to access your personal attendee profile page.',
'send_link': 'Send Link',
'email_sent': 'If that email is registered, we have sent the link.',
'email_not_found': 'No attendee found with that email address.',
'remember_password': 'Remember your password?',
'not_yet_registered': 'Not yet registered?',
'register_for_free': 'Register for FREE!',
'presenter': 'Presenter',
'visitor': 'Visitor',
'organiser': 'Organiser',
@@ -608,14 +621,14 @@ def generate_staff_code():
conn.close()
def generate_type_code():
"""Generate a unique 10-character alphanumeric attendee type code."""
def generate_type_code(event_id):
"""Generate a unique 10-character alphanumeric attendee type code for an event."""
chars = string.ascii_uppercase + string.digits
while True:
code = ''.join(random.choices(chars, k=10))
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT id FROM attendee_types WHERE code = %s", (code,))
cursor.execute("SELECT id FROM attendee_types WHERE event_id = %s AND code = %s", (event_id, code))
if not cursor.fetchone():
cursor.close()
conn.close()
@@ -2644,7 +2657,7 @@ def manage_attendee_types(event_id):
except ValueError:
price_value = 0.00
type_code = generate_type_code()
type_code = generate_type_code(event_id)
cursor.execute("""
INSERT INTO attendee_types (event_id, code, name, price)
VALUES (%s, %s, %s, %s)
@@ -3820,9 +3833,9 @@ def attendee_profile():
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
UPDATE attendees SET first_name = %s, last_name = %s, organisation = %s, role = %s, introduction = %s
UPDATE attendees SET first_name = %s, last_name = %s, organisation = %s, role = %s, introduction = %s, phone = %s, linkedin = %s
WHERE id = %s
""", (first_name, last_name, organisation, role, introduction, session['user_id']))
""", (first_name, last_name, organisation, role, introduction, request.form.get('phone', '').strip(), request.form.get('linkedin', '').strip(), session['user_id']))
conn.commit()
cursor.close()
conn.close()
@@ -4521,6 +4534,60 @@ def payment_page(code):
return render_template('attendee/payment.html', event=event, pending=pending)
@app.route('/attendee/request-link', methods=['GET', 'POST'])
def request_attendee_link():
"""Page to request a personal page link by email."""
if request.method == 'POST':
email = request.form.get('email', '').strip()
if not email:
flash('Email is required.')
return render_template('attendee/request_link.html')
try:
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
# Check if attendee exists with this email
cursor.execute("SELECT * FROM attendees WHERE email = %s", (email,))
attendee = cursor.fetchone()
if attendee:
# Use confirmation_token if available, otherwise attendee_code
token = attendee['confirmation_token'] if attendee['confirmation_token'] else attendee['attendee_code']
personal_page_url = url_for('attendee_personal_page', token=token, _external=True)
full_name = f"{attendee['first_name']} {attendee['last_name']}"
# Get event name
cursor.execute("SELECT name FROM events WHERE id = %s", (attendee['event_id'],))
event = cursor.fetchone()
event_name = event['name'] if event else 'Event'
# Send email
send_attendee_confirmation_email(
attendee_email=email,
attendee_name=full_name,
event_name=event_name,
event_date='See your profile',
event_location='See your profile',
personal_page_url=personal_page_url
)
cursor.close()
conn.close()
# Always show success message for security (don't reveal if email exists)
flash(t('email_sent'))
return render_template('attendee/request_link.html')
except Exception as e:
flash(f'Error: {e}')
return render_template('attendee/request_link.html')
return render_template('attendee/request_link.html')
@app.route('/attendee/personal/<token>')
def attendee_personal_page(token):
"""Personal page accessed via confirmation email link."""
+3 -2
View File
@@ -189,11 +189,12 @@ def create_tables():
CREATE TABLE IF NOT EXISTS attendee_types (
id INT PRIMARY KEY AUTO_INCREMENT,
event_id INT NOT NULL,
code VARCHAR(10) UNIQUE NOT NULL,
code VARCHAR(10) NOT NULL,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE,
UNIQUE KEY unique_type_code_per_event (event_id, code)
)
"""
]
+2 -1
View File
@@ -156,10 +156,11 @@
|--------|------|-------------|
| id | INT | PRIMARY KEY AUTO_INCREMENT |
| event_id | INT | NOT NULL, FOREIGN KEY → events(id) |
| code | VARCHAR(10) | UNIQUE NOT NULL |
| code | VARCHAR(10) | NOT NULL |
| name | VARCHAR(100) | NOT NULL |
| price | DECIMAL(10,2) | DEFAULT 0.00 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
| | | UNIQUE KEY (event_id, code) per event |
## 4. User Types & Roles