瀏覽代碼

opencode ha implementado el sistema de invitación

Tino 3 月之前
父節點
當前提交
f465da70b0
共有 5 個文件被更改,包括 146 次插入6 次删除
  1. 18 1
      src/accounts/admin.py
  2. 54 1
      src/accounts/models.py
  3. 32 2
      src/accounts/views.py
  4. 1 0
      src/gestion_reservas/settings.py
  5. 41 2
      src/reymotausers/forms.py

+ 18 - 1
src/accounts/admin.py

@@ -1,3 +1,20 @@
 from django.contrib import admin
+from django.utils.html import format_html
+from .models import InvitationCode
 
-# Register your models here.
+
+@admin.register(InvitationCode)
+class InvitationCodeAdmin(admin.ModelAdmin):
+    list_display = ['code', 'created_at', 'expires_at', 'is_used', 'status_badge']
+    list_filter = ['is_used', 'created_at']
+    search_fields = ['code', 'used_by__email', 'created_by__email']
+    readonly_fields = ['code', 'created_at']
+
+    def status_badge(self, obj):
+        if obj.is_used:
+            return format_html('<span style="background:#dc3545;color:white;padding:2px 6px;">Usado</span>')
+        elif obj.is_valid():
+            return format_html('<span style="background:#28a745;color:white;padding:2px 6px;">Válido</span>')
+        else:
+            return format_html('<span style="background:#ffc107;color:black;padding:2px 6px;">Expirado</span>')
+    status_badge.short_description = 'Estado'

+ 54 - 1
src/accounts/models.py

@@ -1,3 +1,56 @@
 from django.db import models
+from django.utils import timezone
+import secrets
 
-# Create your models here.
+
+class InvitationCode(models.Model):
+    code = models.CharField(max_length=32, unique=True, editable=False)
+    created_at = models.DateTimeField(auto_now_add=True)
+    expires_at = models.DateTimeField()
+    is_used = models.BooleanField(default=False)
+    used_by = models.OneToOneField(
+        'reymotausers.ReyMotaUser',
+        on_delete=models.SET_NULL,
+        null=True,
+        blank=True,
+        related_name='used_invitation'
+    )
+    created_by = models.ForeignKey(
+        'reymotausers.ReyMotaUser',
+        on_delete=models.SET_NULL,
+        null=True,
+        related_name='created_invitations'
+    )
+
+    class Meta:
+        verbose_name = "Código de Invitación"
+        verbose_name_plural = "Códigos de Invitación"
+
+    def __str__(self):
+        return f"{self.code} (expira: {self.expires_at:%d/%m/%Y %H:%M})"
+
+    @classmethod
+    def generate_code(cls):
+        while True:
+            code = secrets.token_urlsafe(24)[:32]
+            if not cls.objects.filter(code=code).exists():
+                return code
+
+    @classmethod
+    def create_invitation(cls, created_by=None, hours_valid=24):
+        expires_at = timezone.now() + timezone.timedelta(hours=hours_valid)
+        return cls.objects.create(
+            code=cls.generate_code(),
+            expires_at=expires_at,
+            created_by=created_by
+        )
+
+    def is_valid(self):
+        if self.is_used or self.expires_at is None:
+            return False
+        return timezone.now() < self.expires_at
+
+    def mark_as_used(self, user):
+        self.is_used = True
+        self.used_by = user
+        self.save()

+ 32 - 2
src/accounts/views.py

@@ -1,12 +1,42 @@
-# accounts/views.py
-# from django.contrib.auth.forms import UserCreationForm
 from django.urls import reverse_lazy
 from django.views.generic import CreateView
+from django.shortcuts import get_object_or_404
+from django.contrib import messages
 
 from reymotausers.forms import ReyMotaUserCreationForm
+from accounts.models import InvitationCode
 
 
 class SignUpView(CreateView):
     form_class = ReyMotaUserCreationForm
     success_url = reverse_lazy("login")
     template_name = "registration/signup.html"
+
+    def get_initial(self):
+        initial = super().get_initial()
+        code = self.request.GET.get('code')
+        if code:
+            initial['invitation_code'] = code
+        return initial
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        code = self.request.GET.get('code')
+        if code:
+            try:
+                invitation = get_object_or_404(InvitationCode, code=code)
+                if invitation.is_used:
+                    messages.warning(self.request, 'Este código ya ha sido utilizado.')
+                    context['invitation_valid'] = False
+                elif not invitation.is_valid():
+                    messages.warning(self.request, 'El código ha expirado.')
+                    context['invitation_valid'] = False
+                else:
+                    context['invitation_valid'] = True
+            except Exception:
+                context['invitation_valid'] = False
+                messages.error(self.request, 'El código no es válido.')
+        else:
+            context['invitation_valid'] = False
+            messages.error(self.request, 'Se requiere un código de invitación.')
+        return context

+ 1 - 0
src/gestion_reservas/settings.py

@@ -50,6 +50,7 @@ INSTALLED_APPS = [
     'django.contrib.staticfiles',
 
     'eventos',
+    'accounts',
     'gestion_reservas',
     'reymotausers',
 

+ 41 - 2
src/reymotausers/forms.py

@@ -1,19 +1,58 @@
 from django import forms
 from django.contrib.auth.forms import UserCreationForm, UserChangeForm
+from django.core.exceptions import ValidationError
 
 from .models import ReyMotaUser
 
 
 class ReyMotaUserCreationForm(UserCreationForm):
+    invitation_code = forms.CharField(
+        max_length=32,
+        label="Código de Invitación",
+        help_text="Introduce el código que has recibido"
+    )
 
     class Meta:
         model = ReyMotaUser
-        fields = ("email", "nombre", "foto")
+        fields = ("email", "nombre", "foto", "invitation_code")
         labels = {'email': 'Dirección de correo'}
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        code = self.initial.get('invitation_code')
+        if code:
+            self.fields['invitation_code'].initial = code
+
+    def clean_invitation_code(self):
+        code = self.cleaned_data.get('invitation_code')
+        if not code:
+            raise ValidationError("El código de invitación es obligatorio.")
+
+        from accounts.models import InvitationCode
+        try:
+            invitation = InvitationCode.objects.get(code=code)
+            if not invitation.is_valid():
+                if invitation.is_used:
+                    raise ValidationError("Este código ya ha sido utilizado.")
+                else:
+                    raise ValidationError("El código ha expirado.")
+        except InvitationCode.DoesNotExist:
+            raise ValidationError("El código no es válido.")
+
+        return code
+
+    def save(self, commit=True):
+        user = super().save(commit=False)
+        if commit:
+            user.save()
+            code = self.cleaned_data.get('invitation_code')
+            invitation = InvitationCode.objects.get(code=code)
+            invitation.mark_as_used(user)
+        return user
+
 
 class ReyMotaUserChangeForm(UserChangeForm):
 
     class Meta:
         model = ReyMotaUser
-        fields = ("email", "foto")
+        fields = ("email", "foto")