add payment for registration clean up

timetoconfirm
Raz 7 months ago
parent 42090b5ab7
commit 28e39be609
  1. 23
      tournaments/migrations/0119_tournament_enable_online_payment_and_more.py
  2. 23
      tournaments/migrations/0120_tournament_enable_online_payment_refund_and_more.py
  3. 4
      tournaments/models/team_registration.py
  4. 69
      tournaments/models/tournament.py
  5. 2
      tournaments/services/email_service.py
  6. 193
      tournaments/services/payment_service.py
  7. 365
      tournaments/services/registration_cart.py
  8. 777
      tournaments/services/tournament_registration.py
  9. 3
      tournaments/templates/register_tournament.html
  10. 84
      tournaments/templates/tournaments/tournament_info.html
  11. 765
      tournaments/views.py

@ -0,0 +1,23 @@
# Generated by Django 5.1 on 2025-04-09 18:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0118_remove_playerregistration_payment_status_and_more'),
]
operations = [
migrations.AddField(
model_name='tournament',
name='enable_online_payment',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='tournament',
name='online_payment_is_mandatory',
field=models.BooleanField(default=False),
),
]

@ -0,0 +1,23 @@
# Generated by Django 5.1 on 2025-04-09 19:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0119_tournament_enable_online_payment_and_more'),
]
operations = [
migrations.AddField(
model_name='tournament',
name='enable_online_payment_refund',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='tournament',
name='refund_date_limit',
field=models.DateTimeField(blank=True, null=True),
),
]

@ -388,6 +388,6 @@ class TeamRegistration(SideStoreModel):
if status == 'PAID': if status == 'PAID':
return 0 return 0
elif status == 'UNPAID': elif status == 'UNPAID':
return self.tournament.entry_fee * 2 return self.tournament.team_fee()
elif status == 'MIXED': elif status == 'MIXED':
return self.tournament.entry_fee return self.tournament.player_fee()

@ -79,6 +79,10 @@ class Tournament(BaseModel):
hide_umpire_phone = models.BooleanField(default=True) hide_umpire_phone = models.BooleanField(default=True)
disable_ranking_federal_ruling = models.BooleanField(default=False) disable_ranking_federal_ruling = models.BooleanField(default=False)
reserved_spots = models.IntegerField(default=0) reserved_spots = models.IntegerField(default=0)
enable_online_payment = models.BooleanField(default=False)
online_payment_is_mandatory = models.BooleanField(default=False)
enable_online_payment_refund = models.BooleanField(default=False)
refund_date_limit = models.DateTimeField(null=True, blank=True) # Equivalent to Date? = nil
def delete_dependencies(self): def delete_dependencies(self):
for team_registration in self.team_registrations.all(): for team_registration in self.team_registrations.all():
@ -1056,6 +1060,19 @@ class Tournament(BaseModel):
if self.license_is_required: if self.license_is_required:
options.append("Licence requise") options.append("Licence requise")
# Options de paiement en ligne
if self.enable_online_payment:
if self.online_payment_is_mandatory:
options.append("Paiement en ligne obligatoire")
else:
options.append("Paiement en ligne disponible")
if self.enable_online_payment_refund and self.refund_date_limit:
date = formats.date_format(self.refund_date_limit.astimezone(timezone), format='j F Y H:i')
options.append(f"Remboursement possible jusqu'au {date}")
elif self.enable_online_payment_refund:
options.append("Remboursement possible")
# Joueurs par équipe # Joueurs par équipe
min_players = self.minimum_player_per_team min_players = self.minimum_player_per_team
max_players = self.maximum_player_per_team max_players = self.maximum_player_per_team
@ -1675,18 +1692,14 @@ class Tournament(BaseModel):
return None return None
def is_user_registered(self, user): def get_user_registered(self, user):
"""
Check if a user is already registered for this tournament.
Returns True if the user is registered, False otherwise.
"""
if not user.is_authenticated or not user.licence_id: if not user.is_authenticated or not user.licence_id:
return False return None
# Validate the license format # Validate the license format
validator = LicenseValidator(user.licence_id) validator = LicenseValidator(user.licence_id)
if not validator.validate_license(): if not validator.validate_license():
return False return None
# Get the stripped license (without check letter) # Get the stripped license (without check letter)
stripped_license = validator.stripped_license stripped_license = validator.stripped_license
@ -1698,13 +1711,49 @@ class Tournament(BaseModel):
team_registration__tournament=self, team_registration__tournament=self,
licence_id__icontains=stripped_license, licence_id__icontains=stripped_license,
team_registration__walk_out=False team_registration__walk_out=False
).exists() ).first()
def is_user_registered(self, user):
return self.get_user_registered(user) is not None
def get_user_team_registration(self, user):
user_registered = self.get_user_registered(user)
if user_registered:
return user_registered.team_registration
else:
return None
def should_request_payment(self): def should_request_payment(self):
return True if self.entry_fee is not None and self.entry_fee > 0 and self.enable_online_payment:
return True
else:
return False
def is_refund_possible(self):
if self.enable_online_payment_refund:
time = timezone.now()
if self.refund_date_limit:
if time <= self.refund_date_limit:
return True
else:
return False
else:
return True
else:
return False
def player_fee(self):
if self.entry_fee is not None and self.entry_fee > 0 and self.enable_online_payment:
return self.entry_fee
else:
return 0
def team_fee(self): def team_fee(self):
return self.entry_fee * 2 entry_fee = self.entry_fee
if entry_fee is not None and entry_fee > 0 and self.enable_online_payment:
return self.entry_fee * self.minimum_player_per_team
else:
return 0
class MatchGroup: class MatchGroup:
def __init__(self, name, matches, formatted_schedule, round_id=None): def __init__(self, name, matches, formatted_schedule, round_id=None):

@ -515,7 +515,7 @@ class TournamentEmailService:
# For unpaid teams, add payment instructions # For unpaid teams, add payment instructions
payment_info = [ payment_info = [
"\n\n Paiement des frais d'inscription requis", "\n\n Paiement des frais d'inscription requis",
f"Les frais d'inscription de {tournament.entry_fee}€ doivent être payés pour confirmer votre participation.", f"Les frais d'inscription de {tournament.entry_fee}par joueur doivent être payés pour confirmer votre participation.",
"Vous pouvez effectuer le paiement en vous connectant à votre compte Padel Club.", "Vous pouvez effectuer le paiement en vous connectant à votre compte Padel Club.",
f"Lien pour payer: https://padelclub.app/tournament/{tournament.id}/info" f"Lien pour payer: https://padelclub.app/tournament/{tournament.id}/info"
] ]

@ -0,0 +1,193 @@
from django.shortcuts import get_object_or_404, redirect
from django.conf import settings
from django.urls import reverse
from django.contrib import messages
import stripe
from ..models import TeamRegistration, PlayerRegistration, Tournament
from ..models.player_registration import PlayerPaymentType, RegistrationStatus
from .email_service import TournamentEmailService
from .tournament_registration import RegistrationCartManager
from ..utils.extensions import is_not_sqlite_backend
class PaymentService:
"""
Service for handling payment processing for tournament registrations
"""
def __init__(self, request):
self.request = request
self.stripe_api_key = settings.STRIPE_SECRET_KEY
def create_checkout_session(self, tournament_id, team_fee, cart_data=None, team_registration_id=None):
"""
Create a Stripe checkout session for tournament payment
"""
stripe.api_key = self.stripe_api_key
tournament = get_object_or_404(Tournament, id=tournament_id)
# Get user email if authenticated
customer_email = self.request.user.email if self.request.user.is_authenticated else None
# Determine the appropriate cancel URL based on the context
if team_registration_id:
# If we're paying for an existing registration, go back to tournament info
cancel_url = self.request.build_absolute_uri(
reverse('tournament-info', kwargs={'tournament_id': tournament_id})
)
else:
# If we're in the registration process, go back to registration form
cancel_url = self.request.build_absolute_uri(
reverse('register_tournament', kwargs={'tournament_id': tournament_id})
)
# Common checkout session parameters
checkout_session_params = {
'payment_method_types': ['card'],
'line_items': [{
'price_data': {
'currency': 'eur',
'product_data': {
'name': f'{tournament.broadcast_display_name()} du {tournament.formatted_start_date()}',
'description': f'Lieu {tournament.event.club.name}',
},
'unit_amount': int(team_fee * 100), # Amount in cents
},
'quantity': 1,
}],
'mode': 'payment',
'success_url': self.request.build_absolute_uri(
reverse('tournament-payment-success', kwargs={'tournament_id': tournament_id})
),
'cancel_url': cancel_url,
'metadata': {
'tournament_id': str(tournament_id), # Convert UUID to string
'user_id': str(self.request.user.id) if self.request.user.is_authenticated else None, # Convert UUID to string
'source_page': 'tournament_info' if team_registration_id else 'register_tournament',
}
}
# Add cart or team data to metadata based on payment context
if cart_data:
checkout_session_params['metadata']['registration_cart_id'] = str(cart_data['cart_id']) # Convert to string
elif team_registration_id:
checkout_session_params['metadata']['team_registration_id'] = str(team_registration_id) # Convert to string
self.request.session['team_registration_id'] = str(team_registration_id) # Convert to string
# Add customer_email if available
if customer_email:
checkout_session_params['customer_email'] = customer_email
# Create the checkout session
checkout_session = stripe.checkout.Session.create(**checkout_session_params)
# Store checkout session ID and source page in session
self.request.session['stripe_checkout_session_id'] = checkout_session.id
self.request.session['payment_source_page'] = 'tournament_info' if team_registration_id else 'register_tournament'
self.request.session.modified = True
return checkout_session
def process_successful_payment(self, tournament_id, checkout_session):
"""
Process a successful Stripe payment
Returns a tuple (success, redirect_response)
"""
print(f"Processing payment for tournament {tournament_id}")
tournament = get_object_or_404(Tournament, id=tournament_id)
# Check if this is a payment for an existing team registration
team_registration_id = self.request.session.get('team_registration_id')
print(f"Team registration ID from session: {team_registration_id}")
# Track payment statuses for debugging
payment_statuses = []
if team_registration_id:
success = self._process_direct_payment(checkout_session)
payment_statuses.append(success)
print(f"Direct payment processing result: {success}")
else:
# This is a payment during registration process
success = self._process_registration_payment(tournament, checkout_session)
payment_statuses.append(success)
print(f"Registration payment processing result: {success}")
# Print combined payment status
print(f"Payment statuses: {payment_statuses}")
print(any(payment_statuses))
# Clear checkout session ID
if 'stripe_checkout_session_id' in self.request.session:
del self.request.session['stripe_checkout_session_id']
return any(payment_statuses)
def _process_direct_payment(self, checkout_session):
"""Process payment for an existing team registration"""
team_registration_id = self.request.session.get('team_registration_id')
if not team_registration_id:
print("No team registration ID found in session")
return False
try:
print(f"Looking for team registration with ID: {team_registration_id}")
team_registration = TeamRegistration.objects.get(id=team_registration_id)
success = self._update_registration_payment_info(
team_registration,
checkout_session.payment_intent
)
# Clean up session
if 'team_registration_id' in self.request.session:
del self.request.session['team_registration_id']
return success
except TeamRegistration.DoesNotExist:
print(f"Team registration not found with ID: {team_registration_id}")
return False
except Exception as e:
print(f"Error in _process_direct_payment: {str(e)}")
return False
def _process_registration_payment(self, tournament, checkout_session):
"""Process payment made during registration"""
cart_manager = RegistrationCartManager(self.request)
cart_data = cart_manager.get_cart_data()
# Checkout and create registration
success, result = cart_manager.checkout()
if not success:
return False
# Process payment for the new registration
team_registration = result # result is team_registration object
self._update_registration_payment_info(
team_registration,
checkout_session.payment_intent
)
# Send confirmation email if appropriate
waiting_list_position = cart_data.get('waiting_list_position', -1)
if is_not_sqlite_backend():
email_service = TournamentEmailService()
email_service.send_registration_confirmation(
self.request,
tournament,
team_registration,
waiting_list_position
)
return True
def _update_registration_payment_info(self, team_registration, payment_intent_id):
"""Update player registrations with payment information"""
player_registrations = PlayerRegistration.objects.filter(team_registration=team_registration)
for player_reg in player_registrations:
player_reg.payment_type = PlayerPaymentType.CREDIT_CARD
player_reg.registration_status = RegistrationStatus.CONFIRMED
player_reg.payment_id = payment_intent_id
player_reg.save()
return True

@ -1,365 +0,0 @@
from django.utils import timezone
import uuid
import datetime
from ..models import PlayerRegistration, TeamRegistration, Tournament
from ..utils.licence_validator import LicenseValidator
from ..utils.player_search import get_player_name_from_csv
def get_or_create_registration_cart_id(request):
"""Get or create a registration cart ID in the session"""
if 'registration_cart_id' not in request.session:
request.session['registration_cart_id'] = str(uuid.uuid4())
return request.session['registration_cart_id']
def get_cart_expiry(request):
"""Get the cart expiry time from the session"""
if 'registration_cart_expiry' not in request.session:
# Set default expiry to 30 minutes from now
expiry = timezone.now() + datetime.timedelta(minutes=30)
request.session['registration_cart_expiry'] = expiry.isoformat()
return request.session['registration_cart_expiry']
def is_cart_expired(request):
"""Check if the registration cart is expired"""
if 'registration_cart_expiry' not in request.session:
return False
expiry_str = request.session['registration_cart_expiry']
try:
expiry = datetime.datetime.fromisoformat(expiry_str)
return timezone.now() > expiry
except (ValueError, TypeError):
return True
def reset_cart_expiry(request):
"""Reset the cart expiry time"""
expiry = timezone.now() + datetime.timedelta(minutes=30)
request.session['registration_cart_expiry'] = expiry.isoformat()
request.session.modified = True
def get_tournament_from_cart(request):
"""Get the tournament ID associated with the current cart"""
return request.session.get('registration_tournament_id')
def initialize_registration_cart(request, tournament_id):
"""Initialize a new registration cart for a tournament"""
# Clear any existing cart
clear_registration_cart(request)
try:
tournament = Tournament.objects.get(id=tournament_id)
except Tournament.DoesNotExist:
return False, "Tournoi introuvable."
tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
waiting_list_position = tournament.get_waiting_list_position()
if waiting_list_position >= 0:
tournament.reserved_spots = 0
else:
tournament.reserved_spots += 1
tournament.save()
# Set up the new cart
request.session['registration_cart_id'] = str(uuid.uuid4())
request.session['waiting_list_position'] = waiting_list_position
request.session['registration_tournament_id'] = tournament_id
request.session['registration_cart_players'] = []
reset_cart_expiry(request)
request.session.modified = True
def get_registration_cart_data(request):
"""Get the data for the current registration cart"""
# Ensure cart players array exists
if 'registration_cart_players' not in request.session:
request.session['registration_cart_players'] = []
request.session.modified = True
# Ensure tournament ID exists
if 'registration_tournament_id' not in request.session:
# If no tournament ID but we have players, this is an inconsistency
if request.session.get('registration_cart_players'):
print("WARNING: Found players but no tournament ID - clearing players")
request.session['registration_cart_players'] = []
request.session.modified = True
cart_data = {
'cart_id': get_or_create_registration_cart_id(request),
'tournament_id': request.session.get('registration_tournament_id'),
'waiting_list_position': request.session.get('waiting_list_position'),
'players': request.session.get('registration_cart_players', []),
'expiry': get_cart_expiry(request),
'mobile_number': request.session.get('registration_mobile_number',
request.user.phone if hasattr(request.user, 'phone') else '')
}
# Debug: print the cart content
print(f"Cart data - Tournament ID: {cart_data['tournament_id']}")
print(f"Cart data - Players count: {len(cart_data['players'])}")
return cart_data
def add_player_to_cart(request, player_data):
"""Add a player to the registration cart"""
if is_cart_expired(request):
return False, "Votre session d'inscription a expiré, veuillez réessayer."
# Get cart data
tournament_id = request.session.get('registration_tournament_id')
if not tournament_id:
return False, "Pas d'inscription active."
# Get tournament
try:
tournament = Tournament.objects.get(id=tournament_id)
except Tournament.DoesNotExist:
return False, "Tournoi introuvable."
# Get existing players directly from session
players = request.session.get('registration_cart_players', [])
# Debug: Initial players count
print(f"Before adding - Players in session: {len(players)}")
# Check if we've reached the team limit (usually 2 for padel)
if len(players) >= 2: # Assuming teams of 2 for padel
return False, "Nombre maximum de joueurs déjà ajouté."
# Process player data
licence_id = player_data.get('licence_id', '').upper() if player_data.get('licence_id') else None
first_name = player_data.get('first_name', '')
last_name = player_data.get('last_name', '').upper()
# Handle case where user is authenticated, has no license, and license is required
if tournament.license_is_required:
# If license is required but not provided
if not licence_id:
# First player (authentication check) or partner
user_message = "Le numéro de licence est obligatoire." if len(players) == 0 else "Le numéro de licence de votre partenaire est obligatoire."
return False, user_message
# Validate the license format
validator = LicenseValidator(licence_id)
if not validator.validate_license():
return False, "Le numéro de licence est invalide, la lettre ne correspond pas."
# Check if player is already registered in tournament
stripped_license = validator.stripped_license
if _is_player_already_registered(stripped_license, tournament):
return False, "Un joueur avec ce numéro de licence est déjà inscrit dans une équipe."
# Check if this is the authenticated user trying to register as first player
if request.user.is_authenticated and len(players) == 0 and request.user.licence_id is None:
# Try to update the user's license ID in the database
try:
request.user.licence_id = validator.computed_licence_id
request.user.save()
request.user.refresh_from_db()
except:
return False, "Erreur lors de la mise à jour de votre licence: cette licence est déjà utilisée par un autre joueur."
# Check for duplicate licenses in cart
existing_licenses = [p.get('licence_id') for p in players if p.get('licence_id')]
if licence_id and licence_id in existing_licenses:
return False, "Ce joueur est déjà dans l'équipe."
# Process based on whether license ID was provided and tournament rules
if licence_id:
# Get federation data
fed_data, found = get_player_name_from_csv(tournament.federal_category, licence_id)
if found and fed_data:
# Use federation data (including check for eligibility)
player_register_check = tournament.player_register_check(licence_id)
if player_register_check:
return False, ", ".join(player_register_check)
# Update player data from federation data
player_data.update({
'first_name': fed_data['first_name'],
'last_name': fed_data['last_name'],
'rank': fed_data['rank'],
'is_woman': fed_data['is_woman'],
'points': fed_data.get('points'),
'assimilation': fed_data.get('assimilation'),
'tournament_count': fed_data.get('tournament_count'),
'ligue_name': fed_data.get('ligue_name'),
'club_name': fed_data.get('club_name'),
'birth_year': fed_data.get('birth_year'),
'found_in_french_federation': True,
})
elif tournament.license_is_required:
# License required but not found in federation data
return False, "La licence fournit n'a pas été trouvée dans la base FFT. Contactez le juge arbitre si cette licence est valide."
elif not first_name or not last_name:
# License not required or not found, but name is needed
return False, "Le prénom et le nom sont obligatoires pour les joueurs sans licence."
elif not tournament.license_is_required:
# License not required, check if name is provided
if not first_name or not last_name:
return False, "Le prénom et le nom sont obligatoires pour les joueurs sans licence."
# Set default rank for players without a license
if player_data.get('rank') is None:
default_data, _ = get_player_name_from_csv(tournament.federal_category, None)
if default_data:
player_data['rank'] = default_data.get('rank')
player_data['is_woman'] = default_data.get('is_woman', False)
else:
# License is required but not provided
return False, "Le numéro de licence est obligatoire."
# Add player to cart
players.append(player_data)
request.session['registration_cart_players'] = players
reset_cart_expiry(request)
request.session.modified = True
return True, "Joueur ajouté avec succès."
def remove_player_from_cart(request):
"""Remove the last player from the cart"""
if is_cart_expired(request):
return False, "Votre session d'inscription a expiré, veuillez réessayer."
players = request.session.get('registration_cart_players', [])
if not players:
return False, "Pas de joueur à supprimer."
# Remove last player
players.pop()
request.session['registration_cart_players'] = players
reset_cart_expiry(request)
request.session.modified = True
# If cart is now empty and user is authenticated with license, re-add them automatically
if not players:
add_authenticated_user_to_cart(request)
return True, "Joueur retiré."
def update_cart_contact_info(request, mobile_number=None):
"""Update contact info for the cart"""
if is_cart_expired(request):
return False, "Votre session d'inscription a expiré, veuillez réessayer."
if mobile_number is not None:
request.session['registration_mobile_number'] = mobile_number
reset_cart_expiry(request)
request.session.modified = True
return True, "Informations de contact mises à jour."
def checkout_registration_cart(request):
"""Convert cart to an actual tournament registration"""
if is_cart_expired(request):
return False, "Votre session d'inscription a expiré, veuillez réessayer."
# Get cart data
cart_data = get_registration_cart_data(request)
tournament_id = cart_data.get('tournament_id')
players = cart_data.get('players')
mobile_number = cart_data.get('mobile_number')
# Validate cart data
if not tournament_id:
return False, "Aucun tournoi sélectionné."
if not players:
return False, "Aucun joueur dans l'inscription."
# Get tournament
try:
tournament = Tournament.objects.get(id=tournament_id)
except Tournament.DoesNotExist:
return False, "Tournoi introuvable."
# Check minimum players
if len(players) < tournament.minimum_player_per_team:
return False, f"Vous avez besoin d'au moins {tournament.minimum_player_per_team} joueurs pour vous inscrire."
# Create team registration
team_registration = TeamRegistration.objects.create(
tournament=tournament,
registration_date=timezone.now(),
walk_out=False
)
# Create player registrations
for idx, player_data in enumerate(players):
PlayerRegistration.objects.create(
team_registration=team_registration,
first_name=player_data.get('first_name', ''),
last_name=player_data.get('last_name', '').upper(),
licence_id=player_data.get('licence_id'),
rank=player_data.get('rank'),
points=player_data.get('points'),
club_name=player_data.get('club_name'),
ligue_name=player_data.get('ligue_name'),
email=player_data.get('email'),
phone_number=player_data.get('phone'),
assimilation=player_data.get('assimilation'),
tournament_played=player_data.get('tournament_count'),
birthdate=str(player_data.get('birth_year', '')),
captain=(idx == 0), # First player is captain
registered_online=True,
registration_status='CONFIRMED' if request.session['waiting_list_position'] < 0 else 'WAITING'
)
# Update user phone if provided
if request.user.is_authenticated and mobile_number:
request.user.phone = mobile_number
request.user.save(update_fields=['phone'])
# Clear the cart
clear_registration_cart(request)
tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
tournament.save()
return True, team_registration
def clear_registration_cart(request):
"""Clear the registration cart"""
keys_to_clear = [
'registration_cart_id',
'registration_tournament_id',
'registration_cart_players',
'registration_cart_expiry',
'registration_mobile_number'
]
for key in keys_to_clear:
if key in request.session:
del request.session[key]
request.session.modified = True
def _is_player_already_registered(stripped_license, tournament):
"""Check if a player is already registered in the tournament"""
return PlayerRegistration.objects.filter(
team_registration__tournament=tournament,
licence_id__icontains=stripped_license,
team_registration__walk_out=False
).exists()
def add_authenticated_user_to_cart(request):
"""
Adds the authenticated user to the cart if they have a valid license.
Returns True if added, False otherwise.
"""
if not request.user.is_authenticated or not request.user.licence_id:
return False
# Create player data for the authenticated user
player_data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name,
'licence_id': request.user.licence_id,
'email': request.user.email,
'phone': request.user.phone
}
# Add the user to the cart
success, _ = add_player_to_cart(request, player_data)
return success

@ -1,372 +1,449 @@
from django.utils import timezone from django.utils import timezone
from ..forms import TournamentRegistrationForm, AddPlayerForm import uuid
from ..repositories import TournamentRegistrationRepository import datetime
from .email_service import TournamentEmailService from ..models import PlayerRegistration, TeamRegistration, Tournament
from django.contrib import messages
from ..utils.licence_validator import LicenseValidator from ..utils.licence_validator import LicenseValidator
from ..utils.player_search import get_player_name_from_csv from ..utils.player_search import get_player_name_from_csv
from tournaments.models import PlayerRegistration
from ..utils.extensions import is_not_sqlite_backend class RegistrationCartManager:
from django.contrib.auth import get_user_model """
from django.contrib.messages import get_messages Manages the registration cart for tournament registrations.
from django.db import IntegrityError Handles session-based cart operations, player additions/removals,
and checkout processes.
class TournamentRegistrationService: """
def __init__(self, request, tournament):
CART_EXPIRY_MINUTES = 30
def __init__(self, request):
self.request = request self.request = request
self.tournament = tournament self.session = request.session
self.context = {}
self.repository = TournamentRegistrationRepository() def get_or_create_cart_id(self):
self.email_service = TournamentEmailService() """Get or create a registration cart ID in the session"""
if 'registration_cart_id' not in self.session:
def initialize_context(self): self.session['registration_cart_id'] = str(uuid.uuid4()) # Ensure it's a string
self.context = { self.session.modified = True
'tournament': self.tournament, return self.session['registration_cart_id']
'registration_successful': False,
'team_form': None, def get_cart_expiry(self):
'add_player_form': None, """Get the cart expiry time from the session"""
'current_players': self.request.session.get('team_registration', []), if 'registration_cart_expiry' not in self.session:
} # Set default expiry to 30 minutes from now
return self.context expiry = timezone.now() + datetime.timedelta(minutes=self.CART_EXPIRY_MINUTES)
self.session['registration_cart_expiry'] = expiry.isoformat()
def handle_post_request(self): self.session.modified = True
self.context['team_form'] = TournamentRegistrationForm(self.request.POST) return self.session['registration_cart_expiry']
self.context['add_player_form'] = AddPlayerForm(self.request.POST)
def is_cart_expired(self):
if 'add_player' in self.request.POST: """Check if the registration cart is expired"""
self.handle_add_player() if 'registration_cart_expiry' not in self.session:
if 'remove_player' in self.request.POST: return False
self.handle_remove_player()
elif 'register_team' in self.request.POST: expiry_str = self.session['registration_cart_expiry']
self.handle_team_registration() try:
expiry = datetime.datetime.fromisoformat(expiry_str)
def handle_remove_player(self): return timezone.now() > expiry
team_registration = self.request.session.get('team_registration', []) except (ValueError, TypeError):
if team_registration: # Check if list is not empty return True
team_registration.pop() # Remove last element
self.request.session['team_registration'] = team_registration def reset_cart_expiry(self):
self.context['current_players'] = team_registration """Reset the cart expiry time"""
expiry = timezone.now() + datetime.timedelta(minutes=self.CART_EXPIRY_MINUTES)
def handle_add_player(self): self.session['registration_cart_expiry'] = expiry.isoformat()
if not self.context['add_player_form'].is_valid(): self.session.modified = True
return
def get_tournament_id(self):
# Clear existing messages if the form is valid """Get the tournament ID associated with the current cart"""
storage = get_messages(self.request) return self.session.get('registration_tournament_id')
# Iterate through the storage to clear it
for _ in storage: def initialize_cart(self, tournament_id):
pass """Initialize a new registration cart for a tournament"""
# Clear any existing cart
player_data = self.context['add_player_form'].cleaned_data self.clear_cart()
licence_id = player_data.get('licence_id', '').upper()
try:
# Validate license tournament = Tournament.objects.get(id=tournament_id)
if not self._validate_license(licence_id): except Tournament.DoesNotExist:
return return False, "Tournoi introuvable."
# Check for duplicate players # Update tournament reserved spots
if self._is_duplicate_player(licence_id): tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
return waiting_list_position = tournament.get_waiting_list_position()
# Check if player is already registered in tournament if waiting_list_position >= 0:
if self._is_already_registered(licence_id): tournament.reserved_spots = 0
return
if self.request.user.is_authenticated and self.request.user.licence_id is None and len(self.context['current_players']) == 0:
if self._update_user_license(player_data.get('licence_id')) == False:
# if no licence id for authentificated user and trying to add him as first player of the team, we check his federal data
self._handle_invalid_names(licence_id, player_data)
else: else:
# Handle player data tournament.reserved_spots += 1
if self.context['add_player_form'].names_is_valid():
self._handle_valid_names(player_data) tournament.save()
else:
self._handle_invalid_names(licence_id, player_data) # Set up the new cart
self.session['registration_cart_id'] = str(uuid.uuid4()) # Ensure it's a string
def handle_team_registration(self): self.session['waiting_list_position'] = waiting_list_position
if not self.context['team_form'].is_valid(): self.session['registration_tournament_id'] = str(tournament_id) # Ensure it's a string
return self.session['registration_cart_players'] = []
self.reset_cart_expiry()
if self.request.user.is_authenticated: self.session.modified = True
cleaned_data = self.context['team_form'].cleaned_data
mobile_number = cleaned_data.get('mobile_number') return True, "Cart initialized successfully"
self.request.user.phone = mobile_number
self.request.user.save() def get_cart_data(self):
"""Get the data for the current registration cart"""
# Ensure cart players array exists
if 'registration_cart_players' not in self.session:
self.session['registration_cart_players'] = []
self.session.modified = True
# Ensure tournament ID exists
if 'registration_tournament_id' not in self.session:
# If no tournament ID but we have players, this is an inconsistency
if self.session.get('registration_cart_players'):
print("WARNING: Found players but no tournament ID - clearing players")
self.session['registration_cart_players'] = []
self.session.modified = True
# Get user phone if authenticated
user_phone = ''
if hasattr(self.request.user, 'phone'):
user_phone = self.request.user.phone
cart_data = {
'cart_id': self.get_or_create_cart_id(),
'tournament_id': self.session.get('registration_tournament_id'),
'waiting_list_position': self.session.get('waiting_list_position'),
'players': self.session.get('registration_cart_players', []),
'expiry': self.get_cart_expiry(),
'mobile_number': self.session.get('registration_mobile_number', user_phone)
}
waiting_list_position = self.tournament.get_waiting_list_position() # Debug: print the cart content
print(f"Cart data - Tournament ID: {cart_data['tournament_id']}")
print(f"Cart data - Players count: {len(cart_data['players'])}")
team_registration = self.repository.create_team_registration( return cart_data
self.tournament,
timezone.now().replace(microsecond=0) def add_player(self, player_data):
) """Add a player to the registration cart"""
if self.is_cart_expired():
return False, "Votre session d'inscription a expiré, veuillez réessayer."
# Get cart data
tournament_id = self.session.get('registration_tournament_id')
if not tournament_id:
return False, "Pas d'inscription active."
# Get tournament
try:
tournament = Tournament.objects.get(id=tournament_id)
except Tournament.DoesNotExist:
return False, "Tournoi introuvable."
# Get existing players directly from session
players = self.session.get('registration_cart_players', [])
# Check if we've reached the team limit
if len(players) >= 2: # Assuming teams of 2 for padel
return False, "Nombre maximum de joueurs déjà ajouté."
self.repository.create_player_registrations( # Process player data
self.request, licence_id = player_data.get('licence_id', '').upper() if player_data.get('licence_id') else None
team_registration, first_name = player_data.get('first_name', '')
self.request.session['team_registration'], last_name = player_data.get('last_name', '').upper() if player_data.get('last_name') else ''
self.context['team_form'].cleaned_data
# Handle license validation logic
result = self._process_player_license(
tournament, licence_id, first_name, last_name, players, len(players) == 0
)
if not result[0]:
return result # Return the error
# If we got here, license validation passed
if licence_id:
# Get federation data
fed_data, found = get_player_name_from_csv(tournament.federal_category, licence_id)
if found and fed_data:
# Use federation data (including check for eligibility)
player_register_check = tournament.player_register_check(licence_id)
if player_register_check:
return False, ", ".join(player_register_check)
# Update player data from federation data
player_data.update({
'first_name': fed_data['first_name'],
'last_name': fed_data['last_name'],
'rank': fed_data['rank'],
'is_woman': fed_data['is_woman'],
'points': fed_data.get('points'),
'assimilation': fed_data.get('assimilation'),
'tournament_count': fed_data.get('tournament_count'),
'ligue_name': fed_data.get('ligue_name'),
'club_name': fed_data.get('club_name'),
'birth_year': fed_data.get('birth_year'),
'found_in_french_federation': True,
})
elif tournament.license_is_required:
# License required but not found in federation data
return False, "La licence fournit n'a pas été trouvée dans la base FFT. Contactez le juge arbitre si cette licence est valide."
elif not first_name or not last_name:
# License not required or not found, but name is needed
return False, "Le prénom et le nom sont obligatoires pour les joueurs sans licence."
elif not tournament.license_is_required:
# License not required, check if name is provided
if not first_name or not last_name:
return False, "Le prénom et le nom sont obligatoires pour les joueurs sans licence."
# Set default rank for players without a license
if player_data.get('rank') is None:
default_data, _ = get_player_name_from_csv(tournament.federal_category, None)
if default_data:
player_data['rank'] = default_data.get('rank')
player_data['is_woman'] = default_data.get('is_woman', False)
else:
# License is required but not provided
return False, "Le numéro de licence est obligatoire."
# Add player to cart
players.append(player_data)
self.session['registration_cart_players'] = players
self.reset_cart_expiry()
self.session.modified = True
return True, "Joueur ajouté avec succès."
def _process_player_license(self, tournament, licence_id, first_name, last_name, players, is_first_player):
"""
Process and validate player license
Returns (True, None) if valid, (False, error_message) if invalid
"""
# Handle case where license is required
if tournament.license_is_required:
# If license is required but not provided
if not licence_id:
# First player (authentication check) or partner
user_message = "Le numéro de licence est obligatoire." if is_first_player else "Le numéro de licence de votre partenaire est obligatoire."
return False, user_message
# Validate the license format
validator = LicenseValidator(licence_id)
if not validator.validate_license():
return False, "Le numéro de licence est invalide, la lettre ne correspond pas."
# Check if player is already registered in tournament
stripped_license = validator.stripped_license
if self._is_player_already_registered(stripped_license, tournament):
return False, "Un joueur avec ce numéro de licence est déjà inscrit dans une équipe."
# Check if this is the authenticated user trying to register as first player
if self.request.user.is_authenticated and is_first_player and self.request.user.licence_id is None:
# Try to update the user's license ID in the database
try:
self.request.user.licence_id = validator.computed_licence_id
self.request.user.save()
self.request.user.refresh_from_db()
except:
return False, "Erreur lors de la mise à jour de votre licence: cette licence est déjà utilisée par un autre joueur."
# Check for duplicate licenses in cart
existing_licenses = [p.get('licence_id') for p in players if p.get('licence_id')]
if licence_id and licence_id in existing_licenses:
return False, "Ce joueur est déjà dans l'équipe."
return True, None
def remove_player(self):
"""Remove the last player from the cart"""
if self.is_cart_expired():
return False, "Votre session d'inscription a expiré, veuillez réessayer."
players = self.session.get('registration_cart_players', [])
if not players:
return False, "Pas de joueur à supprimer."
# Remove last player
players.pop()
self.session['registration_cart_players'] = players
self.reset_cart_expiry()
self.session.modified = True
return True, "Joueur retiré."
def update_contact_info(self, mobile_number=None):
"""Update contact info for the cart"""
if self.is_cart_expired():
return False, "Votre session d'inscription a expiré, veuillez réessayer."
if mobile_number is not None:
self.session['registration_mobile_number'] = mobile_number
self.reset_cart_expiry()
self.session.modified = True
return True, "Informations de contact mises à jour."
def checkout(self):
"""Convert cart to an actual tournament registration"""
if self.is_cart_expired():
return False, "Votre session d'inscription a expiré, veuillez réessayer."
# Get cart data
cart_data = self.get_cart_data()
tournament_id = cart_data.get('tournament_id')
players = cart_data.get('players')
mobile_number = cart_data.get('mobile_number')
# Validate cart data
if not tournament_id:
return False, "Aucun tournoi sélectionné."
if not players:
return False, "Aucun joueur dans l'inscription."
# Get tournament
try:
tournament = Tournament.objects.get(id=tournament_id)
except Tournament.DoesNotExist:
return False, "Tournoi introuvable."
# Check minimum players
if len(players) < tournament.minimum_player_per_team:
return False, f"Vous avez besoin d'au moins {tournament.minimum_player_per_team} joueurs pour vous inscrire."
# Create team registration
team_registration = TeamRegistration.objects.create(
tournament=tournament,
registration_date=timezone.now(),
walk_out=False
) )
if is_not_sqlite_backend(): # Create player registrations
self.email_service.send_registration_confirmation( for idx, player_data in enumerate(players):
self.request, PlayerRegistration.objects.create(
self.tournament, team_registration=team_registration,
team_registration, first_name=player_data.get('first_name', ''),
waiting_list_position last_name=player_data.get('last_name', '').upper(),
licence_id=player_data.get('licence_id'),
rank=player_data.get('rank'),
points=player_data.get('points'),
club_name=player_data.get('club_name'),
ligue_name=player_data.get('ligue_name'),
email=player_data.get('email'),
phone_number=player_data.get('phone'),
assimilation=player_data.get('assimilation'),
tournament_played=player_data.get('tournament_count'),
birthdate=str(player_data.get('birth_year', '')),
captain=(idx == 0), # First player is captain
registered_online=True,
registration_status='CONFIRMED' if self.session['waiting_list_position'] < 0 else 'WAITING'
) )
self.clear_session_data() # Update user phone if provided
self.context['registration_successful'] = True if self.request.user.is_authenticated and mobile_number:
self.request.user.phone = mobile_number
def handle_get_request(self): self.request.user.save(update_fields=['phone'])
print("handle_get_request")
storage = get_messages(self.request) # Clear the cart
# Iterate through the storage to clear it self.clear_cart()
for _ in storage: tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
pass tournament.save()
self.context['add_player_form'] = AddPlayerForm() return True, team_registration
self.context['team_form'] = self.initialize_team_form()
self.initialize_session_data() def clear_cart(self):
"""Clear the registration cart"""
def add_player_to_session(self, player_data): keys_to_clear = [
print("add_player_to_session", player_data) 'registration_cart_id',
if not self.request.session.get('team_registration'): 'team_registration_id',
self.request.session['team_registration'] = [] 'registration_tournament_id',
'registration_cart_players',
self.request.session['team_registration'].append(player_data) 'registration_cart_expiry',
self.context['current_players'] = self.request.session.get('team_registration', []) 'registration_mobile_number'
self.context['add_player_form'].first_tournament = False ]
self.context['add_player_form'].user_without_licence = False
self.request.session.modified = True for key in keys_to_clear:
if key in self.session:
def clear_session_data(self): del self.session[key]
self.request.session['team_registration'] = []
self.request.session.modified = True self.session.modified = True
def initialize_team_form(self): def _is_player_already_registered(self, stripped_license, tournament):
initial_data = {} """Check if a player is already registered in the tournament"""
if self.request.user.is_authenticated: return PlayerRegistration.objects.filter(
initial_data = { team_registration__tournament=tournament,
'email': self.request.user.email, licence_id__icontains=stripped_license,
'mobile_number': self.request.user.phone, team_registration__walk_out=False
} ).exists()
return TournamentRegistrationForm(initial=initial_data)
def add_authenticated_user(self):
def initialize_session_data(self): """
print("initialize_session_data") Adds the authenticated user to the cart if they have a valid license.
self.request.session['team_registration'] = [] Returns True if added, False otherwise.
if self.request.user.is_authenticated: """
self._add_authenticated_user_to_session() if not self.request.user.is_authenticated or not self.request.user.licence_id:
return False
def _add_authenticated_user_to_session(self):
if not self.request.user.licence_id: # Create player data for the authenticated user
self._handle_user_without_license()
return
player_data = self._get_authenticated_user_data()
if player_data:
self.request.session['team_registration'].insert(0, player_data)
self.context['current_players'] = self.request.session.get('team_registration', [])
self.request.session.modified = True
def _handle_user_without_license(self):
player_data = { player_data = {
'first_name': self.request.user.first_name, 'first_name': self.request.user.first_name,
'last_name': self.request.user.last_name.upper(), 'last_name': self.request.user.last_name,
'licence_id': self.request.user.licence_id,
'email': self.request.user.email,
'phone': self.request.user.phone
} }
self.context['add_player_form'] = AddPlayerForm(initial=player_data)
self.context['add_player_form'].user_without_licence = True
self.request.session.modified = True
def _get_authenticated_user_data(self): # Add the user to the cart
user = self.request.user success, _ = self.add_player(player_data)
validator = LicenseValidator(user.licence_id) return success
player_data = {
'first_name': user.first_name,
'last_name': user.last_name.upper(),
'email': user.email,
'phone': user.phone,
'licence_id': validator.computed_licence_id
}
data, found = get_player_name_from_csv(self.tournament.federal_category, user.licence_id) # For backward compatibility with existing code that uses the function-based approach
if found and data: def get_or_create_registration_cart_id(request):
player_data.update({ cart_manager = RegistrationCartManager(request)
'rank': data['rank'], return cart_manager.get_or_create_cart_id()
'points': data.get('points'),
'assimilation': data.get('assimilation'),
'tournament_count': data.get('tournament_count'),
'ligue_name': data.get('ligue_name'),
'club_name': data.get('club_name'),
'birth_year': data.get('birth_year'),
'found_in_french_federation': True,
})
return player_data
def _validate_license(self, licence_id):
print("Validating license...")
validator = LicenseValidator(licence_id)
if validator.validate_license() is False and self.tournament.license_is_required:
if not licence_id:
message = ("Le numéro de licence est obligatoire."
if not self.request.session.get('team_registration', [])
else "Le numéro de licence de votre partenaire est obligatoire.")
messages.error(self.request, message)
else:
# computed_license_key = validator.computed_license_key
# messages.error(self.request, f"Le numéro de licence est invalide, la lettre ne correspond pas. {computed_license_key}")
messages.error(self.request, "Le numéro de licence est invalide, la lettre ne correspond pas.")
print("License validation failed")
return False
return True
def _is_duplicate_player(self, licence_id): def get_cart_expiry(request):
existing_players = [player['licence_id'] for player in self.request.session.get('team_registration', [])] cart_manager = RegistrationCartManager(request)
if licence_id in existing_players: return cart_manager.get_cart_expiry()
messages.error(self.request, "Ce joueur est déjà dans l'équipe.")
return True
return False
def _is_already_registered(self, licence_id):
validator = LicenseValidator(licence_id)
if (validator.validate_license() and
self._license_already_registered(validator.stripped_license) and
self.tournament.license_is_required):
messages.error(self.request, "Un joueur avec ce numéro de licence est déjà inscrit dans une équipe.")
return True
return False
def _handle_valid_names(self, player_data):
print("_handle_valid_names", player_data)
if player_data.get('rank') is None:
self._set_default_rank(player_data)
self.add_player_to_session(player_data)
self.context['add_player_form'] = AddPlayerForm()
self.context['add_player_form'].first_tournament = False
def _handle_invalid_names(self, licence_id, player_data):
data, found = get_player_name_from_csv(self.tournament.federal_category, licence_id)
print("_handle_invalid_names get_player_name_from_csv", data, found)
if found and data:
self._update_player_data_from_csv(player_data, data)
player_check = self._player_check(player_data)
if player_check == True:
self.add_player_to_session(player_data)
self.context['add_player_form'] = AddPlayerForm()
else:
return
else:
print("_handle_first_tournament_case")
self._handle_first_tournament_case(data)
def _set_default_rank(self, player_data):
if self.request.session.get('last_rank') is None:
data, found = get_player_name_from_csv(self.tournament.federal_category, None)
if data:
self.request.session['last_rank'] = data['rank']
self.request.session['is_woman'] = data['is_woman']
self.request.session.modified = True
player_data['rank'] = self.request.session.get('last_rank', None)
player_data['is_woman'] = self.request.session.get('is_woman', False)
def _update_user_license(self, licence_id):
if not self.request.user.is_authenticated or not licence_id:
return False
self.context['add_player_form'].user_without_licence = False def is_cart_expired(request):
validator = LicenseValidator(licence_id) cart_manager = RegistrationCartManager(request)
return cart_manager.is_cart_expired()
if validator.validate_license():
computed_licence_id = validator.computed_licence_id
try:
self.request.user.licence_id = computed_licence_id
self.request.user.save()
self.request.user.refresh_from_db()
self.request.session.modified = True
return True
except IntegrityError:
# Handle the duplicate license error
error_msg = f"Ce numéro de licence ({computed_licence_id}) est déjà utilisé par un autre joueur."
messages.error(self.request, error_msg)
return False
def _update_player_data_from_csv(self, player_data, csv_data):
print("_update_player_data_from_csv", player_data, csv_data)
player_data.update({
'first_name': csv_data['first_name'],
'last_name': csv_data['last_name'],
'rank': csv_data['rank'],
'is_woman': csv_data['is_woman'],
'points': csv_data.get('points'),
'assimilation': csv_data.get('assimilation'),
'tournament_count': csv_data.get('tournament_count'),
'ligue_name': csv_data.get('ligue_name'),
'club_name': csv_data.get('club_name'),
'birth_year': csv_data.get('birth_year'),
'found_in_french_federation': True,
'email': None,
'phone': None,
})
User = get_user_model()
# Get the license ID from player_data
licence_id = player_data.get('licence_id')
validator = LicenseValidator(licence_id)
if validator.validate_license():
try:
# Try to find a user with matching license
user_with_same_license = User.objects.get(licence_id__iexact=validator.computed_licence_id)
# If found, update the email and phone
if user_with_same_license:
player_data.update({
'email': user_with_same_license.email,
'phone': user_with_same_license.phone
})
print(f"Found user with license {licence_id}, updated email and phone")
except User.DoesNotExist:
# No user found with this license, continue with None email and phone
pass
def _handle_first_tournament_case(self, data):
print("_handle_first_tournament_case", data)
if data:
self.request.session['last_rank'] = data['rank']
self.request.session['is_woman'] = data['is_woman']
self.request.session.modified = True
self.context['add_player_form'].first_tournament = True
if not self.context['add_player_form'].names_is_valid():
message = ("Pour confirmer votre inscription votre prénom et votre nom sont obligatoires."
if not self.request.session.get('team_registration', [])
else "Pour rajouter un partenaire, son prénom et son nom sont obligatoires.")
messages.error(self.request, message)
def _player_check(self, player_data):
licence_id = player_data['licence_id'].upper()
validator = LicenseValidator(licence_id)
is_license_valid = validator.validate_license()
player_register_check = self.tournament.player_register_check(licence_id)
if is_license_valid and player_register_check is not None:
for message in player_register_check:
messages.error(self.request, message)
return False
return True def reset_cart_expiry(request):
cart_manager = RegistrationCartManager(request)
cart_manager.reset_cart_expiry()
def _license_already_registered(self, stripped_license): def get_tournament_from_cart(request):
return PlayerRegistration.objects.filter( cart_manager = RegistrationCartManager(request)
team_registration__tournament=self.tournament, return cart_manager.get_tournament_id()
licence_id__icontains=stripped_license,
team_registration__walk_out=False def initialize_registration_cart(request, tournament_id):
).exists() cart_manager = RegistrationCartManager(request)
return cart_manager.initialize_cart(tournament_id)
def get_registration_cart_data(request):
cart_manager = RegistrationCartManager(request)
return cart_manager.get_cart_data()
def add_player_to_cart(request, player_data):
cart_manager = RegistrationCartManager(request)
return cart_manager.add_player(player_data)
def remove_player_from_cart(request):
cart_manager = RegistrationCartManager(request)
return cart_manager.remove_player()
def update_cart_contact_info(request, mobile_number=None):
cart_manager = RegistrationCartManager(request)
return cart_manager.update_contact_info(mobile_number)
def checkout_registration_cart(request):
cart_manager = RegistrationCartManager(request)
return cart_manager.checkout()
def clear_registration_cart(request):
cart_manager = RegistrationCartManager(request)
cart_manager.clear_cart()
def add_authenticated_user_to_cart(request):
cart_manager = RegistrationCartManager(request)
return cart_manager.add_authenticated_user()

@ -179,7 +179,8 @@
<button type="submit" name="proceed_to_payment" class="rounded-button"> <button type="submit" name="proceed_to_payment" class="rounded-button">
Procéder au paiement ({{ tournament.team_fee }}€) Procéder au paiement ({{ tournament.team_fee }}€)
</button> </button>
{% else %} {% endif %}
{% if tournament.should_request_payment is False or tournament.online_payment_is_mandatory is False or cart_data.waiting_list_position >= 0 %}
<button type="submit" name="register_team" class="rounded-button"> <button type="submit" name="register_team" class="rounded-button">
{% if cart_data.waiting_list_position < 0 %} {% if cart_data.waiting_list_position < 0 %}
Confirmer l'inscription Confirmer l'inscription

@ -19,6 +19,44 @@
<h1 class="club padding10 topmargin20">Votre équipe</h1 > <h1 class="club padding10 topmargin20">Votre équipe</h1 >
<div class="bubble"> <div class="bubble">
{% if team.needs_confirmation %}
<div class="alert {% if team.get_confirmation_deadline %}alert-warning{% else %}alert-info{% endif %}">
<h4 class="semibold">Confirmation requise</h4>
<p>Votre place dans le tournoi a été libérée suite à une désinscription.</p>
{% if tournament.should_request_payment and tournament.online_payment_is_mandatory %}
<p>Vous devez payer votre participation pour confirmer votre inscription.</p>
{% endif %}
{% if team.get_confirmation_deadline %}
{% timezone tournament.timezone %}
{% with time_to_confirm=team.get_confirmation_deadline %}
{% now "Y-m-d H:i:s" as current_time_str %}
{% if time_to_confirm %}
{% if time_to_confirm|date:"Y-m-d H:i:s" > current_time_str %}
<p>Vous devez confirmer votre participation avant le:</p>
<p class="semibold highlight">{{ time_to_confirm|date:"d/m/Y à H:i" }}</p>
<p>Temps restant: <span class="countdown-timer">{{ time_to_confirm|timeuntil }}</span></p>
{% else %}
<p class="alert alert-danger">
Le délai de confirmation est expiré. Votre place a été proposée à une autre équipe.
</p>
{% endif %}
{% endif %}
{% endwith %}
{% endtimezone %}
{% endif %}
</div>
{% endif %}
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<div class="alert"> <div class="alert">
{% if team.is_in_waiting_list >= 0 %} {% if team.is_in_waiting_list >= 0 %}
Tournoi complet, vous êtes en liste d'attente. Tournoi complet, vous êtes en liste d'attente.
@ -43,15 +81,6 @@
<p> <p>
<div>Inscrits le {{ team.local_registration_date }}</div> <div>Inscrits le {{ team.local_registration_date }}</div>
</p> </p>
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<!-- Payment status information --> <!-- Payment status information -->
{% if tournament.should_request_payment and team.is_in_waiting_list < 0 %} {% if tournament.should_request_payment and team.is_in_waiting_list < 0 %}
<div style="margin-bottom: 40px;"> <div style="margin-bottom: 40px;">
@ -62,36 +91,18 @@
{% else %} {% else %}
<div> <div>
<a href="{% url 'proceed_to_payment' tournament.id %}" class="rounded-button positive-button"> <a href="{% url 'proceed_to_payment' tournament.id %}" class="rounded-button positive-button">
Procéder au paiement ({{ team.get_remaining_fee }}€) {% if team.needs_confirmation %}
Confirmer en payant ({{ team.get_remaining_fee }}€)
{% else %}
Procéder au paiement ({{ team.get_remaining_fee }}€)
{% endif %}
</a> </a>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% elif team.needs_confirmation %} {% endif %}
{% if team.needs_confirmation and tournament.online_payment_is_mandatory is False %}
<div class="alert {% if team.get_confirmation_deadline %}alert-warning{% else %}alert-info{% endif %}"> <div class="alert {% if team.get_confirmation_deadline %}alert-warning{% else %}alert-info{% endif %}">
<h4 class="semibold">Confirmation requise</h4>
<p>Votre place dans le tournoi a été libérée suite à une désinscription.</p>
{% if team.get_confirmation_deadline %}
{% timezone tournament.timezone %}
{% with time_to_confirm=team.get_confirmation_deadline %}
{% now "Y-m-d H:i:s" as current_time_str %}
{% if time_to_confirm %}
{% if time_to_confirm|date:"Y-m-d H:i:s" > current_time_str %}
<p>Vous devez confirmer votre participation avant le:</p>
<p class="semibold highlight">{{ time_to_confirm|date:"d/m/Y à H:i" }}</p>
<p>Temps restant: <span class="countdown-timer">{{ time_to_confirm|timeuntil }}</span></p>
{% else %}
<p class="alert alert-danger">
Le délai de confirmation est expiré. Votre place a été proposée à une autre équipe.
</p>
{% endif %}
{% endif %}
{% endwith %}
{% endtimezone %}
{% endif %}
<form method="post" action="{% url 'confirm_tournament_registration' tournament.id %}"> <form method="post" action="{% url 'confirm_tournament_registration' tournament.id %}">
{% csrf_token %} {% csrf_token %}
<button type="submit" class="rounded-button positive-button"> <button type="submit" class="rounded-button positive-button">
@ -103,6 +114,11 @@
{% if tournament.is_unregistration_possible %} {% if tournament.is_unregistration_possible %}
<div class="topmargin20"> <div class="topmargin20">
{% if tournament.is_refund_possible and team.is_paid %}
<p>
Votre inscription sera remboursé automatiquement.
</p>
{% endif %}
<a href="{% url 'unregister_tournament' tournament.id %}" <a href="{% url 'unregister_tournament' tournament.id %}"
class="rounded-button destructive-button" class="rounded-button destructive-button"

@ -52,7 +52,6 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import PasswordResetConfirmView from django.contrib.auth.views import PasswordResetConfirmView
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
from .services import registration_cart
from .services.tournament_unregistration import TournamentUnregistrationService from .services.tournament_unregistration import TournamentUnregistrationService
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from .forms import ( from .forms import (
@ -67,6 +66,8 @@ from .utils.licence_validator import LicenseValidator
from django.views.generic.edit import UpdateView from django.views.generic.edit import UpdateView
from .forms import CustomPasswordChangeForm from .forms import CustomPasswordChangeForm
from .services.email_service import TournamentEmailService from .services.email_service import TournamentEmailService
from .services.tournament_registration import RegistrationCartManager
from .services.payment_service import PaymentService
def index(request): def index(request):
now = timezone.now() now = timezone.now()
@ -726,393 +727,6 @@ def profile(request):
'user_name': user.username 'user_name': user.username
}) })
@csrf_protect
def register_tournament(request, tournament_id):
tournament = get_object_or_404(Tournament, id=tournament_id)
# Debug session content
print("===== SESSION DUMP AT START =====")
for key, value in request.session.items():
if key.startswith('registration_'):
print(f"{key}: {value}")
print("================================")
# Check if tournament is open for registration
if not tournament.enable_online_registration:
messages.error(request, "L'inscription en ligne n'est pas activée pour ce tournoi.")
return redirect('tournament-info', tournament_id=tournament_id)
# Check if user is already registered
if request.user.is_authenticated:
user_licence_id = request.user.licence_id
if user_licence_id and tournament.is_user_registered(request.user):
messages.info(request, "Vous êtes déjà inscrit à ce tournoi.")
return redirect('tournament-info', tournament_id=tournament_id)
# Only initialize a fresh cart for GET requests
# For POST requests, use the existing cart to maintain state
if request.method == 'GET':
# Check if we're returning from Stripe (don't reinitialize if we have a checkout session)
if 'stripe_checkout_session_id' in request.session:
# We're returning from Stripe, don't reinitialize the cart
pass
else:
# ALWAYS initialize a fresh cart when entering the registration page (GET request)
# This ensures no old cart data persists
registration_cart.initialize_registration_cart(request, tournament_id)
# Auto-add the authenticated user with license
if request.user.is_authenticated and request.user.licence_id:
player_data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name,
'licence_id': request.user.licence_id,
'email': request.user.email,
'phone': request.user.phone
}
registration_cart.add_player_to_cart(request, player_data)
else:
# For POST, ensure tournament ID is correct
current_tournament_id = registration_cart.get_tournament_from_cart(request)
if current_tournament_id != str(tournament_id):
registration_cart.initialize_registration_cart(request, tournament_id)
# Re-add the authenticated user if they have a license
if request.user.is_authenticated and request.user.licence_id:
player_data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name,
'licence_id': request.user.licence_id,
'email': request.user.email,
'phone': request.user.phone
}
registration_cart.add_player_to_cart(request, player_data)
# Get cart data
cart_data = registration_cart.get_registration_cart_data(request)
# Debug print
print(f"View - Cart Players Count: {len(cart_data['players'])}")
# Initialize context with cart data
context = {
'tournament': tournament,
'current_players': cart_data['players'],
'registration_successful': False,
'cart_data': cart_data # Add this line
}
# Initialize forms
context['team_form'] = TournamentRegistrationForm(initial={
'email': request.user.email if request.user.is_authenticated else '',
'mobile_number': request.user.phone if request.user.is_authenticated else cart_data.get('mobile_number', '')
})
# Initialize the add player form
add_player_form = AddPlayerForm()
# Special handling for authenticated user without license
if request.user.is_authenticated and not request.user.licence_id and not cart_data['players']:
# Setup form for user without license
initial_data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name
}
# If license is required, only show license field initially
if tournament.license_is_required:
add_player_form = AddPlayerForm(initial=initial_data)
add_player_form.user_without_licence = True
else:
# If license not required, show all fields
add_player_form = AddPlayerForm(initial=initial_data)
elif not cart_data['players'] or len(cart_data['players']) < tournament.minimum_player_per_team:
# Regular partner addition form
add_player_form = AddPlayerForm()
context['add_player_form'] = add_player_form
# Handle POST requests
if request.method == 'POST':
if 'add_player' in request.POST:
add_player_form = AddPlayerForm(request.POST)
if add_player_form.is_valid():
success, message = registration_cart.add_player_to_cart(request, add_player_form.cleaned_data)
if success:
messages.success(request, message)
# Refresh cart data
cart_data = registration_cart.get_registration_cart_data(request)
context['current_players'] = cart_data['players']
# Prepare a fresh form for the next player if needed
if len(cart_data['players']) < tournament.minimum_player_per_team:
context['add_player_form'] = AddPlayerForm()
else:
# Remove the form if we've reached the team limit
context['add_player_form'] = None
else:
messages.error(request, message)
context['add_player_form'] = add_player_form
else:
for field, errors in add_player_form.errors.items():
for error in errors:
messages.error(request, f"{field}: {error}")
context['add_player_form'] = add_player_form
elif 'remove_player' in request.POST:
success, message = registration_cart.remove_player_from_cart(request)
if success:
messages.info(request, message)
# Refresh cart data
cart_data = registration_cart.get_registration_cart_data(request)
context['current_players'] = cart_data['players']
# If after removing, the cart is empty and user has no license but license is required
if not cart_data['players'] and request.user.is_authenticated:
if not request.user.licence_id and tournament.license_is_required:
initial_data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name
}
add_player_form = AddPlayerForm(initial=initial_data)
add_player_form.user_without_licence = True
else:
# This will handle re-adding the user with license
add_player_form = AddPlayerForm()
context['add_player_form'] = add_player_form
else:
add_player_form = AddPlayerForm()
context['add_player_form'] = add_player_form
else:
messages.error(request, message)
elif 'register_team' in request.POST:
team_form = TournamentRegistrationForm(request.POST)
if team_form.is_valid():
# Debug print before checkout
print(f"Before checkout - Players in cart: {len(cart_data['players'])}")
# Ensure the session data is correctly saved before proceeding
request.session.modified = True
request.session.save()
# Update cart with contact info
registration_cart.update_cart_contact_info(
request,
mobile_number=team_form.cleaned_data.get('mobile_number')
)
# Get fresh cart data again after updating contact info
fresh_cart_data = registration_cart.get_registration_cart_data(request)
print(f"After contact update - Players in cart: {len(fresh_cart_data['players'])}")
# Debug session content
print("===== SESSION DUMP BEFORE CHECKOUT =====")
for key, value in request.session.items():
if key.startswith('registration_'):
print(f"{key}: {value}")
print("================================")
# Checkout and create registration
success, result = registration_cart.checkout_registration_cart(request)
if success:
waiting_list_position = cart_data.get('waiting_list_position', -1)
if is_not_sqlite_backend():
email_service = TournamentEmailService()
email_service.send_registration_confirmation(
request,
tournament,
result, # team_registration
waiting_list_position
)
context['registration_successful'] = True
context['current_players'] = []
context['add_player_form'] = None # No more adding players after success
else:
messages.error(request, result)
else:
for field, errors in team_form.errors.items():
for error in errors:
messages.error(request, f"{field}: {error}")
context['team_form'] = team_form
elif 'proceed_to_payment' in request.POST:
team_form = TournamentRegistrationForm(request.POST)
if team_form.is_valid():
# Update cart with contact info
registration_cart.update_cart_contact_info(
request,
mobile_number=team_form.cleaned_data.get('mobile_number')
)
# Create payment session
try:
stripe.api_key = settings.STRIPE_SECRET_KEY
# Get user email if authenticated
customer_email = request.user.email if request.user.is_authenticated else None
# Create the Stripe checkout session
checkout_session_params = {
'payment_method_types': ['card'],
'line_items': [{
'price_data': {
'currency': 'eur',
'product_data': {
'name': f'{tournament.broadcast_display_name()} du {tournament.formatted_start_date()}',
'description': f'Lieu {tournament.event.club.name}',
},
'unit_amount': int(team_registration.team_fee() * 100), # Amount in cents
# 'unit_amount': int(tournament.entry_fee * 100), # Amount in cents
},
'quantity': 2,
}],
'mode': 'payment',
'success_url': request.build_absolute_uri(
reverse('tournament-payment-success', kwargs={'tournament_id': tournament_id})
),
'cancel_url': request.build_absolute_uri(
# Use the correct URL name from your urls.py
reverse('register_tournament', kwargs={'tournament_id': tournament_id})
),
'metadata': {
'tournament_id': tournament_id,
'registration_cart_id': cart_data['cart_id'],
'user_id': request.user.id if request.user.is_authenticated else None,
}
}
# Add customer_email if available
if customer_email:
checkout_session_params['customer_email'] = customer_email
# Create the checkout session
checkout_session = stripe.checkout.Session.create(**checkout_session_params)
# Store checkout session ID in session
request.session['stripe_checkout_session_id'] = checkout_session.id
request.session.modified = True
# Redirect to Stripe checkout
return redirect(checkout_session.url)
except Exception as e:
messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}")
else:
for field, errors in team_form.errors.items():
for error in errors:
messages.error(request, f"{field}: {error}")
context['team_form'] = team_form
# Debug session content before rendering
print("===== SESSION DUMP BEFORE RENDER =====")
for key, value in request.session.items():
if key.startswith('registration_'):
print(f"{key}: {value}")
print("================================")
return render(request, 'register_tournament.html', context)
@login_required
def tournament_payment_success(request, tournament_id):
# Get checkout session ID from session
checkout_session_id = request.session.get('stripe_checkout_session_id')
if not checkout_session_id:
messages.error(request, "Session de paiement introuvable.")
return redirect('tournament-info', tournament_id=tournament_id)
try:
# Verify payment status with Stripe
stripe.api_key = settings.STRIPE_SECRET_KEY
checkout_session = stripe.checkout.Session.retrieve(checkout_session_id)
if checkout_session.payment_status == 'paid':
# Check if this is a direct payment from tournament info page
team_registration_id = request.session.get('team_registration_id')
if team_registration_id:
# This is a direct payment for an existing registration
team_registration = get_object_or_404(TeamRegistration, id=team_registration_id)
player_registrations = PlayerRegistration.objects.filter(team_registration=team_registration)
for player_reg in player_registrations:
player_reg.payment_type = PlayerPaymentType.CREDIT_CARD
player_reg.registration_status = RegistrationStatus.CONFIRMED
player_reg.payment_id = checkout_session.payment_intent
player_reg.save()
# Mark team as paid
team_registration.set_paid(True)
# Clear session data
if 'stripe_checkout_session_id' in request.session:
del request.session['stripe_checkout_session_id']
if 'team_registration_id' in request.session:
del request.session['team_registration_id']
messages.success(request, "Paiement réussi et inscription confirmée !")
return redirect('tournament-info', tournament_id=tournament_id)
else:
# This is a payment during registration
# Get cart data
cart_data = registration_cart.get_registration_cart_data(request)
# Checkout and create registration
success, result = registration_cart.checkout_registration_cart(request)
if success:
# Get the team registration and mark as paid
team_registration = result # result is team_registration object
player_registrations = PlayerRegistration.objects.filter(team_registration=team_registration)
for player_reg in player_registrations:
player_reg.payment_type = PlayerPaymentType.CREDIT_CARD
player_reg.registration_status = RegistrationStatus.CONFIRMED
player_reg.payment_id = checkout_session.payment_intent
player_reg.save()
# Mark team as paid
team_registration.set_paid(True)
waiting_list_position = cart_data.get('waiting_list_position', -1)
if is_not_sqlite_backend():
email_service = TournamentEmailService()
email_service.send_registration_confirmation(
request,
get_object_or_404(Tournament, id=tournament_id),
team_registration,
waiting_list_position
)
# Clear session data
if 'stripe_checkout_session_id' in request.session:
del request.session['stripe_checkout_session_id']
messages.success(request, "Paiement réussi et inscription confirmée !")
return redirect('tournament-info', tournament_id=tournament_id)
else:
messages.error(request, f"Paiement réussi mais erreur lors de l'inscription: {result}")
else:
messages.error(request, "Le paiement n'a pas été complété.")
except Exception as e:
messages.error(request, f"Erreur lors de la vérification du paiement: {str(e)}")
return redirect('tournament-info', tournament_id=tournament_id)
@login_required
def cancel_registration(request, tournament_id):
"""Cancel the current registration process and clear the cart"""
registration_cart.clear_registration_cart(request)
try:
tournament = Tournament.objects.get(id=tournament_id)
tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
tournament.save()
except Tournament.DoesNotExist:
return False, "Tournoi introuvable."
messages.info(request, "Processus d'inscription annulé.")
return redirect('tournament-info', tournament_id=tournament_id)
@login_required @login_required
def unregister_tournament(request, tournament_id): def unregister_tournament(request, tournament_id):
tournament = get_object_or_404(Tournament, id=tournament_id) tournament = get_object_or_404(Tournament, id=tournament_id)
@ -1524,77 +1138,346 @@ def confirm_tournament_registration(request, tournament_id):
@login_required @login_required
def proceed_to_payment(request, tournament_id): def proceed_to_payment(request, tournament_id):
"""Handle direct payment from tournament info page"""
tournament = get_object_or_404(Tournament, id=tournament_id) tournament = get_object_or_404(Tournament, id=tournament_id)
team = tournament.get_user_team_registration(request.user)
# First check if the user is registered for this tournament if not team:
user_licence_id = request.user.licence_id messages.error(request, "Aucune inscription trouvée pour ce tournoi.")
if not user_licence_id:
messages.error(request, "Votre licence n'est pas associée à votre compte.")
return redirect('tournament-info', tournament_id=tournament_id) return redirect('tournament-info', tournament_id=tournament_id)
validator = LicenseValidator(user_licence_id) if team.is_paid():
stripped_license = validator.stripped_license messages.info(request, "Votre inscription est déjà payée.")
return redirect('tournament-info', tournament_id=tournament_id)
# Find the team registration try:
team_registration = TeamRegistration.objects.filter( # Create payment session
tournament=tournament, payment_service = PaymentService(request)
player_registrations__licence_id__icontains=stripped_license, checkout_session = payment_service.create_checkout_session(
walk_out=False tournament_id=str(tournament_id), # Convert UUID to string
).first() team_fee=team.get_remaining_fee(),
team_registration_id=str(team.id) # Convert UUID to string
)
if not team_registration: # Redirect to Stripe checkout
messages.error(request, "Vous n'êtes pas inscrit à ce tournoi.") return redirect(checkout_session.url)
except Exception as e:
print(f"Payment error: {str(e)}") # Add debug print
messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}")
return redirect('tournament-info', tournament_id=tournament_id) return redirect('tournament-info', tournament_id=tournament_id)
if team_registration.is_paid(): @login_required
messages.info(request, "Votre paiement a déjà été confirmé.") def tournament_payment_success(request, tournament_id):
"""Handle successful Stripe payment for tournament registration"""
# Get checkout session ID from session
checkout_session_id = request.session.get('stripe_checkout_session_id')
if not checkout_session_id:
messages.error(request, "Session de paiement introuvable.")
return redirect('tournament-info', tournament_id=tournament_id) return redirect('tournament-info', tournament_id=tournament_id)
# Create Stripe checkout session
try: try:
# Verify payment status with Stripe
stripe.api_key = settings.STRIPE_SECRET_KEY stripe.api_key = settings.STRIPE_SECRET_KEY
checkout_session = stripe.checkout.Session.retrieve(checkout_session_id)
# Create checkout session if checkout_session.payment_status == 'paid':
checkout_session = stripe.checkout.Session.create( # Process the payment success
payment_method_types=['card'], payment_service = PaymentService(request)
line_items=[{ success = payment_service.process_successful_payment(str(tournament_id), checkout_session)
'price_data': {
'currency': 'eur',
'product_data': {
'name': f'{tournament.broadcast_display_name()} du {tournament.formatted_start_date()}',
'description': f'Lieu {tournament.event.club.name}',
},
'unit_amount': int(team_registration.team_fee() * 100), # Amount in cents
},
'quantity': 2,
}],
mode='payment',
success_url=request.build_absolute_uri(
reverse('tournament-payment-success', kwargs={'tournament_id': tournament_id})
),
cancel_url=request.build_absolute_uri(
reverse('tournament-info', kwargs={'tournament_id': tournament_id})
),
metadata={
'tournament_id': tournament_id,
'team_registration_id': str(team_registration.id),
'user_id': request.user.id,
},
customer_email=request.user.email,
)
# Store checkout session ID in session
request.session['stripe_checkout_session_id'] = checkout_session.id
request.session['team_registration_id'] = str(team_registration.id)
request.session.modified = True
# Redirect to Stripe checkout if success:
return redirect(checkout_session.url) messages.success(request, "Paiement réussi et inscription confirmée !")
else:
messages.error(request, "Erreur lors du traitement du paiement.")
else:
messages.error(request, "Le paiement n'a pas été complété.")
except Exception as e: except Exception as e:
messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}") print(f"Payment processing error: {str(e)}") # Add debug print
messages.error(request, f"Erreur lors de la vérification du paiement: {str(e)}")
# Get the source page to determine where to redirect
source_page = request.session.get('payment_source_page', 'tournament_info')
# Clean up session variables
for key in ['stripe_checkout_session_id', 'team_registration_id', 'payment_source_page']:
if key in request.session:
del request.session[key]
# Redirect to the appropriate page
if source_page == 'register_tournament':
# For payments during registration, redirect back to tournament info
# since the registration is now complete
return redirect('tournament-info', tournament_id=tournament_id)
else:
# For direct payments, also go to tournament info
return redirect('tournament-info', tournament_id=tournament_id) return redirect('tournament-info', tournament_id=tournament_id)
@csrf_protect
def register_tournament(request, tournament_id):
tournament = get_object_or_404(Tournament, id=tournament_id)
cart_manager = RegistrationCartManager(request)
# Debug session content
print_session_debug(request, "SESSION DUMP AT START")
# Check if tournament is open for registration
if not tournament.enable_online_registration:
messages.error(request, "L'inscription en ligne n'est pas activée pour ce tournoi.")
return redirect('tournament-info', tournament_id=tournament_id)
# Check if user is already registered
if request.user.is_authenticated and request.user.licence_id and tournament.is_user_registered(request.user):
messages.info(request, "Vous êtes déjà inscrit à ce tournoi.")
return redirect('tournament-info', tournament_id=tournament_id)
# Only initialize a fresh cart for GET requests
# For POST requests, use the existing cart to maintain state
if request.method == 'GET':
# Check if we're returning from Stripe (don't reinitialize if we have a checkout session)
if 'stripe_checkout_session_id' in request.session:
# We're returning from Stripe, don't reinitialize the cart
pass
else:
# ALWAYS initialize a fresh cart when entering the registration page (GET request)
# This ensures no old cart data persists
cart_manager.initialize_cart(tournament_id)
# Auto-add the authenticated user with license
if request.user.is_authenticated and request.user.licence_id:
cart_manager.add_authenticated_user()
else:
# For POST, ensure tournament ID is correct
current_tournament_id = cart_manager.get_tournament_id()
if current_tournament_id != str(tournament_id):
cart_manager.initialize_cart(tournament_id)
# Re-add the authenticated user if they have a license
if request.user.is_authenticated and request.user.licence_id:
cart_manager.add_authenticated_user()
# Get cart data
cart_data = cart_manager.get_cart_data()
print(f"View - Cart Players Count: {len(cart_data['players'])}")
# Initialize context with cart data
context = {
'tournament': tournament,
'current_players': cart_data['players'],
'registration_successful': False,
'cart_data': cart_data
}
# Initialize forms
context['team_form'] = TournamentRegistrationForm(initial={
'email': request.user.email if request.user.is_authenticated else '',
'mobile_number': request.user.phone if request.user.is_authenticated else cart_data.get('mobile_number', '')
})
# Initialize the add player form
context['add_player_form'] = create_add_player_form(request, tournament, cart_data)
# Handle POST requests
if request.method == 'POST':
if 'add_player' in request.POST:
handle_add_player_request(request, tournament, cart_manager, context)
elif 'remove_player' in request.POST:
handle_remove_player_request(request, tournament, cart_manager, context)
elif 'register_team' in request.POST:
handle_register_team_request(request, tournament, cart_manager, context)
elif 'proceed_to_payment' in request.POST:
result = handle_payment_request(request, tournament, cart_manager, context, tournament_id)
if result:
return result # This is the redirect to Stripe checkout
# Debug session content before rendering
print_session_debug(request, "SESSION DUMP BEFORE RENDER")
return render(request, 'register_tournament.html', context)
def create_add_player_form(request, tournament, cart_data):
"""Create the appropriate add player form based on user status"""
# Special handling for authenticated user without license
if request.user.is_authenticated and not request.user.licence_id and not cart_data['players']:
# Setup form for user without license
initial_data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name
}
# If license is required, only show license field initially
if tournament.license_is_required:
add_player_form = AddPlayerForm(initial=initial_data)
add_player_form.user_without_licence = True
return add_player_form
else:
# If license not required, show all fields
return AddPlayerForm(initial=initial_data)
elif not cart_data['players'] or len(cart_data['players']) < tournament.minimum_player_per_team:
# Regular partner addition form
return AddPlayerForm()
return None
def handle_add_player_request(request, tournament, cart_manager, context):
"""Handle the 'add_player' POST action"""
add_player_form = AddPlayerForm(request.POST)
if add_player_form.is_valid():
success, message = cart_manager.add_player(add_player_form.cleaned_data)
if success:
messages.success(request, message)
# Refresh cart data
cart_data = cart_manager.get_cart_data()
context['current_players'] = cart_data['players']
# Prepare a fresh form for the next player if needed
if len(cart_data['players']) < tournament.minimum_player_per_team:
context['add_player_form'] = AddPlayerForm()
else:
# Remove the form if we've reached the team limit
context['add_player_form'] = None
else:
messages.error(request, message)
context['add_player_form'] = add_player_form
else:
for field, errors in add_player_form.errors.items():
for error in errors:
messages.error(request, f"{field}: {error}")
context['add_player_form'] = add_player_form
def handle_remove_player_request(request, tournament, cart_manager, context):
"""Handle the 'remove_player' POST action"""
success, message = cart_manager.remove_player()
if success:
messages.info(request, message)
# Refresh cart data
cart_data = cart_manager.get_cart_data()
context['current_players'] = cart_data['players']
# Update the add player form based on the new state
if not cart_data['players'] and request.user.is_authenticated:
if not request.user.licence_id and tournament.license_is_required:
initial_data = {
'first_name': request.user.first_name,
'last_name': request.user.last_name
}
add_player_form = AddPlayerForm(initial=initial_data)
add_player_form.user_without_licence = True
else:
add_player_form = AddPlayerForm()
context['add_player_form'] = add_player_form
else:
context['add_player_form'] = AddPlayerForm()
else:
messages.error(request, message)
def handle_register_team_request(request, tournament, cart_manager, context):
"""Handle the 'register_team' POST action"""
team_form = TournamentRegistrationForm(request.POST)
if team_form.is_valid():
# Debug print before checkout
cart_data = cart_manager.get_cart_data()
print(f"Before checkout - Players in cart: {len(cart_data['players'])}")
# Ensure the session data is correctly saved before proceeding
request.session.modified = True
request.session.save()
# Update cart with contact info
cart_manager.update_contact_info(
mobile_number=team_form.cleaned_data.get('mobile_number')
)
# Get fresh cart data again after updating contact info
fresh_cart_data = cart_manager.get_cart_data()
print(f"After contact update - Players in cart: {len(fresh_cart_data['players'])}")
# Debug session content
print_session_debug(request, "SESSION DUMP BEFORE CHECKOUT")
# Checkout and create registration
success, result = cart_manager.checkout()
if success:
waiting_list_position = cart_data.get('waiting_list_position', -1)
if is_not_sqlite_backend():
email_service = TournamentEmailService()
email_service.send_registration_confirmation(
request,
tournament,
result, # team_registration
waiting_list_position
)
context['registration_successful'] = True
context['current_players'] = []
context['add_player_form'] = None # No more adding players after success
else:
messages.error(request, result)
else:
for field, errors in team_form.errors.items():
for error in errors:
messages.error(request, f"{field}: {error}")
context['team_form'] = team_form
def handle_payment_request(request, tournament, cart_manager, context, tournament_id):
"""Handle the 'proceed_to_payment' POST action"""
team_form = TournamentRegistrationForm(request.POST)
if team_form.is_valid():
# Update cart with contact info
cart_manager.update_contact_info(
mobile_number=team_form.cleaned_data.get('mobile_number')
)
# Create payment session
try:
# Get cart data for payment metadata
cart_data = cart_manager.get_cart_data()
# Create and redirect to payment session
payment_service = PaymentService(request)
checkout_session = payment_service.create_checkout_session(
tournament_id=tournament_id,
team_fee=tournament.team_fee(), # Use the appropriate fee field
cart_data=cart_data
)
# Redirect to Stripe checkout
return redirect(checkout_session.url)
except Exception as e:
messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}")
else:
for field, errors in team_form.errors.items():
for error in errors:
messages.error(request, f"{field}: {error}")
context['team_form'] = team_form
return None
def print_session_debug(request, header):
"""Debug utility to print session contents"""
print(f"===== {header} =====")
for key, value in request.session.items():
if key.startswith('registration_'):
print(f"{key}: {value}")
print("================================")
@login_required
def cancel_registration(request, tournament_id):
"""Cancel the current registration process and clear the cart"""
cart_manager = RegistrationCartManager(request)
cart_manager.clear_cart()
try:
tournament = Tournament.objects.get(id=tournament_id)
tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
tournament.save()
except Tournament.DoesNotExist:
messages.error(request, "Tournoi introuvable.")
return redirect('tournaments-list')
messages.info(request, "Processus d'inscription annulé.")
return redirect('tournament-info', tournament_id=tournament_id)
class UserListExportView(LoginRequiredMixin, View): class UserListExportView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):

Loading…
Cancel
Save