From e019cea98445f8e7f7038ade47121f88431b3e41 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 11 Apr 2025 11:48:55 +0200 Subject: [PATCH] clean up --- api/serializers.py | 12 +- padelclub_backend/settings_app.py | 2 + tournaments/admin.py | 4 +- ...tomuser_enable_online_payments_and_more.py | 28 +++++ ...tournament_is_staff_tournament_and_more.py | 28 +++++ ..._playerregistration_registration_status.py | 18 +++ ...disable_ranking_federal_ruling_and_more.py | 43 +++++++ ...tomuser_enable_online_payments_and_more.py | 27 +++++ ...ment_tournament_is_corporate_tournament.py | 18 +++ tournaments/models/custom_user.py | 24 +++- tournaments/models/enums.py | 17 +++ tournaments/models/player_registration.py | 10 +- tournaments/models/team_registration.py | 23 ++-- tournaments/models/tournament.py | 34 +++--- tournaments/repositories.py | 81 ------------- tournaments/services/email_service.py | 2 +- tournaments/services/payment_service.py | 111 +++++++++++------- .../services/tournament_registration.py | 4 +- tournaments/tasks.py | 2 +- tournaments/views.py | 3 +- 20 files changed, 319 insertions(+), 172 deletions(-) create mode 100644 tournaments/migrations/0122_customuser_enable_online_payments_and_more.py create mode 100644 tournaments/migrations/0123_tournament_is_staff_tournament_and_more.py create mode 100644 tournaments/migrations/0124_alter_playerregistration_registration_status.py create mode 100644 tournaments/migrations/0125_customuser_disable_ranking_federal_ruling_and_more.py create mode 100644 tournaments/migrations/0126_remove_customuser_enable_online_payments_and_more.py create mode 100644 tournaments/migrations/0127_rename_is_staff_tournament_tournament_is_corporate_tournament.py delete mode 100644 tournaments/repositories.py diff --git a/api/serializers.py b/api/serializers.py index c761b5f..c0b081a 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -6,7 +6,7 @@ from django.conf import settings # email from django.template.loader import render_to_string -from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode +from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes from django.core.mail import EmailMessage from django.contrib.sites.shortcuts import get_current_site @@ -15,7 +15,7 @@ from api.tokens import account_activation_token from shared.cryptography import encryption_util from tournaments.models.draw_log import DrawLog -from tournaments.models.enums import UserOrigin +from tournaments.models.enums import UserOrigin, RegistrationPaymentMode class EncryptedUserField(serializers.Field): def to_representation(self, value): @@ -78,6 +78,14 @@ class UserSerializer(serializers.ModelSerializer): loser_bracket_match_format_preference=validated_data.get('loser_bracket_match_format_preference'), loser_bracket_mode=validated_data.get('loser_bracket_mode'), origin=UserOrigin.APP, + user_role=None, + registration_payment_mode=validated_data.get('registration_payment_mode', RegistrationPaymentMode.DISABLED), + umpire_custom_mail=validated_data.get('umpire_custom_mail'), + umpire_custom_contact=validated_data.get('umpire_custom_contact'), + umpire_custom_phone=validated_data.get('umpire_custom_phone'), + hide_umpire_mail=validated_data.get('hide_umpire_mail', False), + hide_umpire_phone=validated_data.get('hide_umpire_phone', True), + disable_ranking_federal_ruling=validated_data.get('disable_ranking_federal_ruling', False) ) if not settings.DEBUG: diff --git a/padelclub_backend/settings_app.py b/padelclub_backend/settings_app.py index e69f82e..7a82bc5 100644 --- a/padelclub_backend/settings_app.py +++ b/padelclub_backend/settings_app.py @@ -58,3 +58,5 @@ SHOP_MANAGERS = [ ] SHOP_SITE_ROOT_URL = 'https://padelclub.app' SHOP_SUPPORT_EMAIL = 'shop@padelclub.app' + +STRIPE_FEE = 0.0075 diff --git a/tournaments/admin.py b/tournaments/admin.py index e06bb99..88a1ba1 100644 --- a/tournaments/admin.py +++ b/tournaments/admin.py @@ -15,11 +15,11 @@ class CustomUserAdmin(UserAdmin): model = CustomUser search_fields = ['username', 'email', 'phone', 'first_name', 'last_name', 'licence_id'] - list_display = ['email', 'first_name', 'last_name', 'username', 'licence_id', 'date_joined', 'latest_event_club_name', 'is_active', 'event_count', 'origin'] + list_display = ['email', 'first_name', 'last_name', 'username', 'registration_payment_mode', 'licence_id', 'date_joined', 'latest_event_club_name', 'is_active', 'event_count', 'origin'] list_filter = ['is_active', 'origin'] ordering = ['-date_joined'] fieldsets = [ - (None, {'fields': ['id', 'username', 'email', 'password', 'first_name', 'last_name', 'is_active', + (None, {'fields': ['id', 'username', 'email', 'password', 'first_name', 'last_name', 'is_active', 'registration_payment_mode', 'clubs', 'country', 'phone', 'licence_id', 'umpire_code', 'summons_message_body', 'summons_message_signature', 'summons_available_payment_methods', 'summons_display_format', 'summons_display_entry_fee', 'summons_use_full_custom_message', diff --git a/tournaments/migrations/0122_customuser_enable_online_payments_and_more.py b/tournaments/migrations/0122_customuser_enable_online_payments_and_more.py new file mode 100644 index 0000000..f868cb4 --- /dev/null +++ b/tournaments/migrations/0122_customuser_enable_online_payments_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1 on 2025-04-10 19:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0121_tournament_stripe_account_id'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='enable_online_payments', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='customuser', + name='user_role', + field=models.IntegerField(blank=True, choices=[(0, 'Juge-Arbitre'), (1, 'Club Owner'), (2, 'Player')], null=True), + ), + migrations.AddField( + model_name='tournament', + name='enable_time_to_confirm', + field=models.BooleanField(default=False), + ), + ] diff --git a/tournaments/migrations/0123_tournament_is_staff_tournament_and_more.py b/tournaments/migrations/0123_tournament_is_staff_tournament_and_more.py new file mode 100644 index 0000000..99e80b2 --- /dev/null +++ b/tournaments/migrations/0123_tournament_is_staff_tournament_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1 on 2025-04-11 05:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0122_customuser_enable_online_payments_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='tournament', + name='is_staff_tournament', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='tournament', + name='is_template', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='customuser', + name='is_staff', + field=models.BooleanField(default=False), + ), + ] diff --git a/tournaments/migrations/0124_alter_playerregistration_registration_status.py b/tournaments/migrations/0124_alter_playerregistration_registration_status.py new file mode 100644 index 0000000..d7b0f97 --- /dev/null +++ b/tournaments/migrations/0124_alter_playerregistration_registration_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1 on 2025-04-11 05:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0123_tournament_is_staff_tournament_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='playerregistration', + name='registration_status', + field=models.IntegerField(choices=[(0, 'Waiting'), (1, 'Pending'), (2, 'Confirmed'), (3, 'Canceled')], default=0), + ), + ] diff --git a/tournaments/migrations/0125_customuser_disable_ranking_federal_ruling_and_more.py b/tournaments/migrations/0125_customuser_disable_ranking_federal_ruling_and_more.py new file mode 100644 index 0000000..c925e37 --- /dev/null +++ b/tournaments/migrations/0125_customuser_disable_ranking_federal_ruling_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.1 on 2025-04-11 07:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0124_alter_playerregistration_registration_status'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='disable_ranking_federal_ruling', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='customuser', + name='hide_umpire_mail', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='customuser', + name='hide_umpire_phone', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='customuser', + name='umpire_custom_contact', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='customuser', + name='umpire_custom_mail', + field=models.EmailField(blank=True, max_length=254, null=True), + ), + migrations.AddField( + model_name='customuser', + name='umpire_custom_phone', + field=models.CharField(blank=True, max_length=15, null=True), + ), + ] diff --git a/tournaments/migrations/0126_remove_customuser_enable_online_payments_and_more.py b/tournaments/migrations/0126_remove_customuser_enable_online_payments_and_more.py new file mode 100644 index 0000000..917dcd2 --- /dev/null +++ b/tournaments/migrations/0126_remove_customuser_enable_online_payments_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1 on 2025-04-11 08:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0125_customuser_disable_ranking_federal_ruling_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='customuser', + name='enable_online_payments', + ), + migrations.AddField( + model_name='customuser', + name='registration_payment_mode', + field=models.IntegerField(choices=[(0, 'No Payment'), (1, 'Direct Payment'), (2, 'No Service Fee')], default=0), + ), + migrations.AlterField( + model_name='customuser', + name='is_staff', + field=models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status'), + ), + ] diff --git a/tournaments/migrations/0127_rename_is_staff_tournament_tournament_is_corporate_tournament.py b/tournaments/migrations/0127_rename_is_staff_tournament_tournament_is_corporate_tournament.py new file mode 100644 index 0000000..3fbd279 --- /dev/null +++ b/tournaments/migrations/0127_rename_is_staff_tournament_tournament_is_corporate_tournament.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1 on 2025-04-11 08:26 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0126_remove_customuser_enable_online_payments_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='tournament', + old_name='is_staff_tournament', + new_name='is_corporate_tournament', + ), + ] diff --git a/tournaments/models/custom_user.py b/tournaments/models/custom_user.py index 3137948..a94cdb8 100644 --- a/tournaments/models/custom_user.py +++ b/tournaments/models/custom_user.py @@ -3,7 +3,9 @@ from django.apps import apps from django.contrib.auth.models import AbstractUser from django.utils.timezone import now +from django.conf import settings from . import club, enums +from .enums import RegistrationPaymentMode import uuid class CustomUser(AbstractUser): @@ -40,6 +42,15 @@ class CustomUser(AbstractUser): origin = models.IntegerField(default=enums.UserOrigin.ADMIN, choices=enums.UserOrigin.choices, null=True, blank=True) should_synchronize = models.BooleanField(default=False) + user_role = models.IntegerField(choices=enums.UserRole.choices, null=True, blank=True) + registration_payment_mode = models.IntegerField(default=RegistrationPaymentMode.DISABLED, choices=RegistrationPaymentMode.choices) + umpire_custom_mail = models.EmailField(null=True, blank=True) + umpire_custom_contact = models.CharField(max_length=200, null=True, blank=True) + umpire_custom_phone = models.CharField(max_length=15, null=True, blank=True) + hide_umpire_mail = models.BooleanField(default=False) + hide_umpire_phone = models.BooleanField(default=True) + disable_ranking_federal_ruling = models.BooleanField(default=False) + ### ### ### ### ### ### ### ### ### ### ### WARNING ### ### ### ### ### ### ### ### ### ### ### WARNING : Any added field MUST be inserted in the method below: fields_for_update() ### ### ### ### ### ### ### ### ### ### ### ### WARNING ### ### ### ### ### ### ### ### ### ### @@ -51,7 +62,10 @@ class CustomUser(AbstractUser): 'summons_message_body', 'summons_message_signature', 'summons_available_payment_methods', 'summons_display_format', 'summons_display_entry_fee', 'summons_use_full_custom_message', 'match_formats_default_duration', 'bracket_match_format_preference', - 'group_stage_match_format_preference', 'loser_bracket_match_format_preference', 'device_id', 'loser_bracket_mode', 'origin', 'agents', 'should_synchronize'] + 'group_stage_match_format_preference', 'loser_bracket_match_format_preference', 'device_id', 'loser_bracket_mode', + 'origin', 'agents', 'should_synchronize', 'user_role', 'registration_payment_mode', + 'umpire_custom_mail', 'umpire_custom_contact', 'umpire_custom_phone', 'hide_umpire_mail', 'hide_umpire_phone', + 'disable_ranking_federal_ruling'] def __str__(self): return self.username @@ -78,3 +92,11 @@ class CustomUser(AbstractUser): return None return None + + def effective_commission_rate(self): + if self.registration_payment_mode == RegistrationPaymentMode.STRIPE: + return settings.STRIPE_FEE + if self.registration_payment_mode == RegistrationPaymentMode.NO_FEE: + return 0.0000 + if self.registration_payment_mode == RegistrationPaymentMode.CORPORATE: + return 0.0000 diff --git a/tournaments/models/enums.py b/tournaments/models/enums.py index d7c7dc2..61ba26c 100644 --- a/tournaments/models/enums.py +++ b/tournaments/models/enums.py @@ -248,3 +248,20 @@ class UserOrigin(models.IntegerChoices): ADMIN = 0, 'Admin' SITE = 1, 'Site' APP = 2, 'App' + +class UserRole(models.IntegerChoices): + JAP = 0, 'Juge-Arbitre' + CLUB_OWNER = 1, 'Club Owner' + PLAYER = 2, 'Player' + +class RegistrationStatus(models.IntegerChoices): + WAITING = 0, 'Waiting' + PENDING = 1, 'Pending' + CONFIRMED = 2, 'Confirmed' + CANCELED = 3, 'Canceled' + +class RegistrationPaymentMode(models.IntegerChoices): + DISABLED = 0, 'Disabled' + CORPORATE = 1, 'Corporate' + NO_FEE = 2, 'No Service Fee' + STRIPE = 3, 'Stripe' diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index e907e94..be0b947 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -1,15 +1,9 @@ from django.db import models from . import SideStoreModel, TeamRegistration, PlayerSexType, PlayerDataSource, PlayerPaymentType, OnlineRegistrationStatus +from .enums import RegistrationStatus import uuid from django.utils import timezone -class RegistrationStatus(models.TextChoices): - WAITING = 'WAITING', 'Waiting' - PENDING = 'PENDING', 'Pending' - CONFIRMED = 'CONFIRMED', 'Confirmed' - CANCELED = 'CANCELED', 'Canceled' - - class PlayerRegistration(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) team_registration = models.ForeignKey(TeamRegistration, on_delete=models.SET_NULL, related_name='player_registrations', null=True) @@ -45,7 +39,7 @@ class PlayerRegistration(SideStoreModel): coach = models.BooleanField(default=False) registered_online = models.BooleanField(default=False) time_to_confirm = models.DateTimeField(null=True, blank=True) - registration_status = models.CharField(max_length=20, choices=RegistrationStatus.choices, default=RegistrationStatus.WAITING) + registration_status = models.IntegerField(choices=RegistrationStatus.choices, default=RegistrationStatus.WAITING) payment_id = models.CharField(max_length=255, blank=True, null=True) def delete_dependencies(self): diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index f884d10..f1958da 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -2,6 +2,8 @@ from django.db import models from . import SideStoreModel, Tournament, GroupStage, Match import uuid from django.utils import timezone +from .enums import RegistrationStatus +from .player_enums import PlayerPaymentType class TeamRegistration(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) @@ -309,15 +311,16 @@ class TeamRegistration(SideStoreModel): return "" def set_time_to_confirm(self, ttc): - from .player_registration import RegistrationStatus for p in self.player_registrations.all(): if p.registered_online: p.time_to_confirm = ttc - p.registration_status = RegistrationStatus.PENDING + if ttc is None: + p.registration_status = RegistrationStatus.WAITING + else: + p.registration_status = RegistrationStatus.PENDING p.save() def needs_confirmation(self): - from .player_registration import RegistrationStatus """Check if this team needs to confirm their registration""" # Check if any player has status PENDING and is registered online return any(p.registration_status == RegistrationStatus.PENDING and p.registered_online @@ -328,17 +331,17 @@ class TeamRegistration(SideStoreModel): deadlines = [p.time_to_confirm for p in self.player_registrations.all() if p.time_to_confirm is not None] return max(deadlines) if deadlines else None - def confirm_registration(self): + def confirm_registration(self, payment_intent_id=None): """Confirm the team's registration after being moved from waiting list""" now = timezone.now() - from .player_registration import RegistrationStatus - # Update all players in the team for player in self.player_registrations.all(): - if player.time_to_confirm is not None: - player.time_to_confirm = None - player.registration_status = RegistrationStatus.CONFIRMED - player.save() + player.time_to_confirm = None + player.payment_id = payment_intent_id + if payment_intent_id is not None: + player.payment_type = PlayerPaymentType.CREDIT_CARD + player.registration_status = RegistrationStatus.CONFIRMED + player.save() # Save confirmation date self.confirmation_date = now diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index d0f202a..d411e4c 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -84,6 +84,9 @@ class Tournament(BaseModel): enable_online_payment_refund = models.BooleanField(default=False) refund_date_limit = models.DateTimeField(null=True, blank=True) # Equivalent to Date? = nil stripe_account_id = models.CharField(max_length=255, blank=True, null=True) + enable_time_to_confirm = models.BooleanField(default=False) + is_corporate_tournament = models.BooleanField(default=False) + is_template = models.BooleanField(default=False) def delete_dependencies(self): for team_registration in self.team_registrations.all(): @@ -1481,32 +1484,32 @@ class Tournament(BaseModel): datetime: The confirmation deadline datetime """ # Skip if feature not enabled - # if not tournament.has_time_to_confirm: - # return None + if self.enable_time_to_confirm is False: + return None if waiting_list_count == 0: return None # Configuration rules TIME_PROXIMITY_RULES = { - 48: 30, # within 48h → 30 min - 24: 20, # within 24h → 20 min - 72: 60, # within 72h → 60 min - "default": 120 + 24: 30, # within 24h → 30 min + 48: 60, # within 48h → 60 min + 72: 120, # within 72h → 120 min + "default": 240 } WAITING_LIST_RULES = { - 15: 15, # 15+ teams → 15 min - 10: 30, # 10+ teams → 30 min - 5: 60, # 5+ teams → 60 min - "default": 120 + 30: 30, # 30+ teams → 30 min + 20: 60, # 20+ teams → 60 min + 10: 120, # 10+ teams → 120 min + "default": 240 } BUSINESS_RULES = { "hours": { "start": 8, # 8:00 - "end": 21, # 20:00 - "default_confirmation_hour": 10 # When extending to next day + "end": 21, # 21:00 + "default_confirmation_hour": 8 # When extending to next day }, "days": { "working_days": [0, 1, 2, 3, 4, 5, 6], # Monday = 0, Friday = 4 @@ -1519,7 +1522,7 @@ class Tournament(BaseModel): 24: True, # If ≤ 24h until tournament: ignore business hours 12: True # If ≤ 12h until tournament: ignore all restrictions }, - "minimum_response_time": 15 # minutes + "minimum_response_time": 30 # minutes } # 1. Get current time in tournament's timezone @@ -1614,9 +1617,6 @@ class Tournament(BaseModel): return raw_deadline - - - @property def week_day(self): """Return the weekday name (e.g., 'Monday')""" @@ -1727,7 +1727,7 @@ class Tournament(BaseModel): return None def should_request_payment(self): - if self.entry_fee is not None and self.entry_fee > 0 and self.enable_online_payment and self.stripe_account_id is not None: + if self.enable_online_payment: return True else: return False diff --git a/tournaments/repositories.py b/tournaments/repositories.py deleted file mode 100644 index d30f636..0000000 --- a/tournaments/repositories.py +++ /dev/null @@ -1,81 +0,0 @@ -from .models import TeamRegistration, PlayerRegistration -from .models.player_enums import PlayerSexType, PlayerDataSource -from .models.enums import FederalCategory -from tournaments.utils.licence_validator import LicenseValidator - -class TournamentRegistrationRepository: - @staticmethod - def create_team_registration(tournament, registration_date): - team_registration = TeamRegistration.objects.create( - tournament=tournament, - registration_date=registration_date - ) - return team_registration - - @staticmethod - def create_player_registrations(request, team_registration, players_data, team_form_data): - stripped_license = None - if request.user.is_authenticated and request.user.licence_id: - stripped_license = LicenseValidator(request.user.licence_id).stripped_license - - for player_data in players_data: - is_captain = False - player_licence_id = player_data['licence_id'] - if player_licence_id and stripped_license: - if stripped_license.lower() in player_licence_id.lower(): - is_captain = True - - sex, rank, computed_rank = TournamentRegistrationRepository._compute_rank_and_sex( - team_registration.tournament, - player_data - ) - - print("create_player_registrations", player_data.get('last_name'), sex, rank, computed_rank) - data_source = None - if player_data.get('found_in_french_federation', False) == True: - data_source = PlayerDataSource.FRENCH_FEDERATION - - player_registration = PlayerRegistration.objects.create( - team_registration=team_registration, - captain=is_captain, - source=data_source, - registered_online=True, - first_name=player_data.get('first_name'), - last_name=player_data.get('last_name'), - points=player_data.get('points'), - assimilation=player_data.get('assimilation'), - tournament_played=player_data.get('tournament_count'), - ligue_name=player_data.get('ligue_name'), - club_name=player_data.get('club_name'), - birthdate=player_data.get('birth_year'), - sex=sex, - rank=rank, - computed_rank=computed_rank, - licence_id=player_data['licence_id'], - email=player_data.get('email'), - phone_number=player_data.get('mobile_number'), - ) - - player_registration.save() - - team_registration.set_weight() - team_registration.save() - - @staticmethod - def _compute_rank_and_sex(tournament, player_data): - is_woman = player_data.get('is_woman', False) - rank = player_data.get('rank', None) - if rank is None: - computed_rank = 100000 - else: - computed_rank = rank - - sex = PlayerSexType.MALE - if is_woman: - sex = PlayerSexType.FEMALE - if tournament.federal_category == FederalCategory.MEN: - computed_rank = str(int(computed_rank) + - FederalCategory.female_in_male_assimilation_addition(int(rank))) - - print("_compute_rank_and_sex", sex, rank, computed_rank) - return sex, rank, computed_rank diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index c167065..beca95d 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -1,6 +1,6 @@ from django.core.mail import EmailMessage from enum import Enum -from ..models.player_registration import RegistrationStatus +from ..models.enums import RegistrationStatus from ..models.tournament import TeamSortingType import django.utils.timezone diff --git a/tournaments/services/payment_service.py b/tournaments/services/payment_service.py index 9053ce1..86f00f2 100644 --- a/tournaments/services/payment_service.py +++ b/tournaments/services/payment_service.py @@ -4,7 +4,7 @@ from django.urls import reverse import stripe from ..models import TeamRegistration, PlayerRegistration, Tournament -from ..models.player_registration import PlayerPaymentType, RegistrationStatus +from ..models.player_registration import PlayerPaymentType from .email_service import TournamentEmailService from .tournament_registration import RegistrationCartManager from ..utils.extensions import is_not_sqlite_backend @@ -29,15 +29,6 @@ class PaymentService: if not tournament.should_request_payment(): raise Exception("Les paiements ne sont pas activés pour ce tournoi.") - # Get the umpire's Stripe account ID - stripe_account_id = tournament.stripe_account_id - if not stripe_account_id: - raise Exception("L'arbitre n'a pas configuré son compte Stripe.") - - # Calculate commission - commission_rate = tournament.effective_commission_rate() / 100 - platform_amount = int((team_fee * commission_rate) * 100) # Commission in cents - # Get user email if authenticated customer_email = self.request.user.email if self.request.user.is_authenticated else None @@ -54,35 +45,72 @@ class PaymentService: ) # 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}', + if tournament.is_corporate_tournament: + # Direct charge without transfers when umpire is platform owner + 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 }, - '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, - 'payment_intent_data': { - 'application_fee_amount': platform_amount, - 'transfer_data': { - 'destination': stripe_account_id, - }, - }, - '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', + '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), + 'user_id': str(self.request.user.id) if self.request.user.is_authenticated else None, + 'source_page': 'tournament_info' if team_registration_id else 'register_tournament', + 'is_corporate_tournament': 'true' + } } + else: + # Get the umpire's Stripe account ID + stripe_account_id = tournament.stripe_account_id + if not stripe_account_id: + raise Exception("L'arbitre n'a pas configuré son compte Stripe.") + + # Calculate commission + commission_rate = tournament.event.creator.effective_commission_rate() + platform_amount = int((team_fee * commission_rate) * 100) # Commission in cents + + 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, + 'payment_intent_data': { + 'application_fee_amount': platform_amount, + 'transfer_data': { + 'destination': stripe_account_id, + }, + }, + '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 @@ -207,14 +235,7 @@ class PaymentService: 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() - + team_registration.confirm_registration(payment_intent_id) return True def process_refund(self, team_registration_id): diff --git a/tournaments/services/tournament_registration.py b/tournaments/services/tournament_registration.py index 0bbcdb3..a237b31 100644 --- a/tournaments/services/tournament_registration.py +++ b/tournaments/services/tournament_registration.py @@ -4,7 +4,7 @@ import datetime from ..models import PlayerRegistration, TeamRegistration, Tournament from ..utils.licence_validator import LicenseValidator from ..utils.player_search import get_player_name_from_csv -from ..models.enums import FederalCategory +from ..models.enums import FederalCategory, RegistrationStatus from ..models.player_enums import PlayerSexType, PlayerDataSource class RegistrationCartManager: @@ -371,7 +371,7 @@ class RegistrationCartManager: licence_id=player_data.get('licence_id'), email=player_data.get('email'), phone_number=player_data.get('mobile_number', mobile_number), - registration_status='CONFIRMED' if self.session.get('waiting_list_position', 0) < 0 else 'WAITING' + registration_status=RegistrationStatus.CONFIRMED if self.session.get('waiting_list_position', 0) < 0 else RegistrationStatus.WAITING ) # Calculate and set team weight diff --git a/tournaments/tasks.py b/tournaments/tasks.py index 3cf2d3b..443700d 100644 --- a/tournaments/tasks.py +++ b/tournaments/tasks.py @@ -3,7 +3,7 @@ from django.utils import timezone from django.db import transaction from .models import PlayerRegistration -from .models.player_registration import RegistrationStatus +from .models.enums import RegistrationStatus from .services.email_service import TournamentEmailService, TeamEmailType @background(schedule=1) # Run every 30 minutes (30*60 seconds) diff --git a/tournaments/views.py b/tournaments/views.py index 39a2652..b898507 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -26,8 +26,7 @@ from django.core.files.storage import default_storage from django.core.files.base import ContentFile from django.views.generic import View from django.db.models import Q -from .models import Club, Tournament, CustomUser, Event, Round, Match, TeamScore, TeamRegistration, PlayerRegistration, UserOrigin, PlayerPaymentType -from .models.player_registration import RegistrationStatus +from .models import Club, Tournament, CustomUser, Event, Round, Match, TeamScore, TeamRegistration, PlayerRegistration, UserOrigin from datetime import datetime, timedelta import time import json