From 01357fe682050e96e0b3f87e0a4b7474fdda5aba Mon Sep 17 00:00:00 2001 From: Mads Nylund <73914541+MadsNyl@users.noreply.github.com> Date: Mon, 6 Nov 2023 08:59:43 +0100 Subject: [PATCH] made it possible to add members before and after registration open time (#733) * made it possible to add members before and after registration open time * can add for paid event * format * format --- .../0056_registration_created_by_admin.py | 18 +++ app/content/models/registration.py | 13 +- app/content/serializers/registration.py | 1 + app/content/views/registration.py | 36 ++++- app/tests/conftest.py | 2 +- .../content/test_registration_integration.py | 127 +++++++++++++++++- 6 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 app/content/migrations/0056_registration_created_by_admin.py diff --git a/app/content/migrations/0056_registration_created_by_admin.py b/app/content/migrations/0056_registration_created_by_admin.py new file mode 100644 index 000000000..a46ec001f --- /dev/null +++ b/app/content/migrations/0056_registration_created_by_admin.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2023-11-04 10:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("content", "0055_remove_qrcode_url_qrcode_content"), + ] + + operations = [ + migrations.AddField( + model_name="registration", + name="created_by_admin", + field=models.BooleanField(default=False), + ), + ] diff --git a/app/content/models/registration.py b/app/content/models/registration.py index 69fec06c2..544ec5979 100644 --- a/app/content/models/registration.py +++ b/app/content/models/registration.py @@ -36,6 +36,7 @@ class Registration(BaseModel, BasePermissionModel): is_on_wait = models.BooleanField(default=False, verbose_name="waiting list") has_attended = models.BooleanField(default=False) allow_photo = models.BooleanField(default=True) + created_by_admin = models.BooleanField(default=False) class Meta: ordering = ("event", "created_at", "is_on_wait") @@ -128,11 +129,12 @@ def save(self, *args, **kwargs): return super().save(*args, **kwargs) def create(self): - if self.event.enforces_previous_strikes: + if self.event.enforces_previous_strikes and not self.created_by_admin: self._abort_for_unanswered_evaluations() self.strike_handler() self.clean() + self.is_on_wait = self.event.is_full if self.should_swap_with_non_prioritized_user(): @@ -216,6 +218,9 @@ def should_swap_with_non_prioritized_user(self): @property def is_prioritized(self): + if self.created_by_admin: + return True + if self.user.number_of_strikes >= 3 and self.event.enforces_previous_strikes: return False @@ -297,15 +302,15 @@ def clean(self): :raises ValidationError if the event or queue is closed. """ - if self.event.closed: + if self.event.closed and not self.created_by_admin: raise ValidationError( "Dette arrangementet er stengt du kan derfor ikke melde deg på" ) if not self.event.sign_up: raise ValidationError("Påmelding er ikke mulig") - if not self.registration_id: + if not self.registration_id and not self.created_by_admin: self.validate_start_and_end_registration_time() - if not self.check_answered_submission(): + if not self.check_answered_submission() and not self.created_by_admin: raise ValidationError( "Du må svare på spørreskjemaet før du kan melde deg på arrangementet" ) diff --git a/app/content/serializers/registration.py b/app/content/serializers/registration.py index 1b26287d6..61b6156c6 100644 --- a/app/content/serializers/registration.py +++ b/app/content/serializers/registration.py @@ -34,6 +34,7 @@ class Meta: "order", "has_paid_order", "wait_queue_number", + "created_by_admin", ) def get_survey_submission(self, obj): diff --git a/app/content/views/registration.py b/app/content/views/registration.py index ead291557..da8c8d3a1 100644 --- a/app/content/views/registration.py +++ b/app/content/views/registration.py @@ -1,3 +1,6 @@ +import uuid +from datetime import datetime + from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters, status @@ -20,8 +23,7 @@ from app.content.models import Event, Registration, User from app.content.serializers import RegistrationSerializer from app.content.util.event_utils import create_payment_order -from app.payment.models.order import Order -from app.payment.views.vipps_callback import vipps_callback +from app.payment.models import Order class RegistrationViewSet(APIRegistrationErrorsMixin, BaseViewSet): @@ -36,9 +38,6 @@ class RegistrationViewSet(APIRegistrationErrorsMixin, BaseViewSet): def get_queryset(self): event_id = self.kwargs.get("event_id", None) - order = Order.objects.filter(event=event_id).first() - if order: - vipps_callback(None, order.order_id) return Registration.objects.filter(event__pk=event_id).select_related("user") def _is_own_registration(self): @@ -152,7 +151,34 @@ def add_registration(self, request, *args, **kwargs): event = get_object_or_404(Event, id=event_id) user = get_object_or_404(User, user_id=user_id) + if not user.accepts_event_rules: + return Response( + { + "detail": "Brukeren må akseptere arrangementreglene i profilen sin først." + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + try: + if event.is_paid_event and not event.is_full: + Order.objects.create( + order_id=uuid.uuid4(), + user=user, + event=event, + payment_link=f"https://tihlde.org/arrangementer/{event_id}/", + expire_date=datetime.now(), + ) + except Exception as e: + capture_exception(e) + return Response( + { + "detail": "Det skjedde en feil med opprettelse av betalingsordre. Påmeldingen ble ikke fullført" + }, + status=status.HTTP_400_BAD_REQUEST, + ) + request.data["allow_photo"] = user.allows_photo_by_default + request.data["created_by_admin"] = True serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) diff --git a/app/tests/conftest.py b/app/tests/conftest.py index b3e915122..7528ffdc3 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -17,8 +17,8 @@ NewsFactory, PageFactory, ParentPageFactory, - QRCodeFactory, PriorityPoolFactory, + QRCodeFactory, RegistrationFactory, ShortLinkFactory, UserFactory, diff --git a/app/tests/content/test_registration_integration.py b/app/tests/content/test_registration_integration.py index 0049028a5..7f52f25da 100644 --- a/app/tests/content/test_registration_integration.py +++ b/app/tests/content/test_registration_integration.py @@ -822,7 +822,9 @@ def test_add_registration_to_event_as_admin(api_client, admin_user, event): @pytest.mark.django_db -def test_add_registration_to_event_as_admin_when_event_is_full(api_client, admin_user, member, event): +def test_add_registration_to_event_as_admin_when_event_is_full( + api_client, admin_user, member, event +): """ An admin should be able to add a registration to an event manually. If the event is full, the member should be added to the waiting list. @@ -872,6 +874,129 @@ def test_add_registration_to_event_as_admin_group_member(event, member, organize assert response.status_code == status.HTTP_201_CREATED +@pytest.mark.django_db +@pytest.mark.parametrize( + "organizer_name", + [ + AdminGroup.PROMO, + AdminGroup.NOK, + AdminGroup.KOK, + AdminGroup.SOSIALEN, + AdminGroup.INDEX, + ], +) +def test_add_registration_to_event_as_admin_group_member_when_event_closed( + event, member, organizer_name +): + """ + A member of NOK, Promo, Sosialen or KOK should be able to add a + registration to an event manually even though the event is closed. + """ + + event.closed = True + event.save() + + data = {"user": member.user_id, "event": event.id} + url = f"{_get_registration_url(event=event)}add/" + + client = get_api_client(user=member, group_name=organizer_name) + + response = client.post(url, data) + + assert response.status_code == status.HTTP_201_CREATED + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "organizer_name", + [ + AdminGroup.PROMO, + AdminGroup.NOK, + AdminGroup.KOK, + AdminGroup.SOSIALEN, + AdminGroup.INDEX, + ], +) +def test_add_registration_to_event_as_admin_group_member_before_registration_open( + event, member, organizer_name +): + """ + A member of NOK, Promo, Sosialen or KOK should be able to add a + registration to an event manually even though the registration has not opened. + """ + + event.start_registration_at = now() + timedelta(days=1) + event.save() + + data = {"user": member.user_id, "event": event.id} + url = f"{_get_registration_url(event=event)}add/" + + client = get_api_client(user=member, group_name=organizer_name) + + response = client.post(url, data) + + assert response.status_code == status.HTTP_201_CREATED + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "organizer_name", + [ + AdminGroup.PROMO, + AdminGroup.NOK, + AdminGroup.KOK, + AdminGroup.SOSIALEN, + AdminGroup.INDEX, + ], +) +def test_add_registration_to_event_as_admin_group_member_after_registration_closed( + event, member, organizer_name +): + """ + A member of NOK, Promo, Sosialen or KOK should be able to add a + registration to an event manually even though the registration has closed. + """ + + event.end_registration_at = now() - timedelta(days=1) + event.save() + + data = {"user": member.user_id, "event": event.id} + url = f"{_get_registration_url(event=event)}add/" + + client = get_api_client(user=member, group_name=organizer_name) + + response = client.post(url, data) + + assert response.status_code == status.HTTP_201_CREATED + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "organizer_name", + [ + AdminGroup.PROMO, + AdminGroup.NOK, + AdminGroup.KOK, + AdminGroup.SOSIALEN, + AdminGroup.INDEX, + ], +) +def test_add_registration_to_paid_event(paid_event, member, organizer_name): + """ + A member of NOK, Promo, Sosialen or KOK should be able to add a + registration to a paid event manually. A order with status "SALE" should be created. + """ + + data = {"user": member.user_id, "event": paid_event.event.id} + url = f"{_get_registration_url(event=paid_event.event)}add/" + + client = get_api_client(user=member, group_name=organizer_name) + + response = client.post(url, data) + + assert response.status_code == status.HTTP_201_CREATED + + @pytest.mark.django_db def test_add_registration_to_event_as_anonymous_user(default_client, event, member): """