From c4d2b0bff76c57c8ffca86552af9796400d77498 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 27 Oct 2022 21:54:43 +0200 Subject: [PATCH] Fix handling of default ticket layouts during event cloning --- src/pretix/api/serializers/event.py | 2 +- src/pretix/api/views/event.py | 12 +- .../ticketoutputpdf/test_defaults_and_copy.py | 183 ++++++++++++++++++ 3 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 src/tests/plugins/ticketoutputpdf/test_defaults_and_copy.py diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 2e8ba1dc12..ab32f42dbb 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -411,7 +411,7 @@ class CloneEventSerializer(EventSerializer): has_subevents = validated_data.pop('has_subevents', None) tz = validated_data.pop('timezone', None) sales_channels = validated_data.pop('sales_channels', None) - new_event = super().create(validated_data) + new_event = super().create({**validated_data, 'plugins': None}) event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first() new_event.copy_data_from(event) diff --git a/src/pretix/api/views/event.py b/src/pretix/api/views/event.py index 98963f3294..734298c8b7 100644 --- a/src/pretix/api/views/event.py +++ b/src/pretix/api/views/event.py @@ -241,13 +241,17 @@ class EventViewSet(viewsets.ModelViewSet): except Event.DoesNotExist: raise ValidationError('Event to copy from was not found') + # Ensure that .installed() is only called when we NOT clone + plugins = serializer.validated_data.pop('plugins', None) + serializer.validated_data['plugins'] = None + new_event = serializer.save(organizer=self.request.organizer) if copy_from: new_event.copy_data_from(copy_from) - if 'plugins' in serializer.validated_data: - new_event.set_active_plugins(serializer.validated_data['plugins']) + if plugins: + new_event.set_active_plugins(plugins) if 'is_public' in serializer.validated_data: new_event.is_public = serializer.validated_data['is_public'] if 'testmode' in serializer.validated_data: @@ -262,6 +266,10 @@ class EventViewSet(viewsets.ModelViewSet): else: serializer.instance.set_defaults() + if plugins: + new_event.set_active_plugins(plugins) + new_event.save(update_fields=['plugins']) + serializer.instance.log_action( 'pretix.event.added', user=self.request.user, diff --git a/src/tests/plugins/ticketoutputpdf/test_defaults_and_copy.py b/src/tests/plugins/ticketoutputpdf/test_defaults_and_copy.py new file mode 100644 index 0000000000..8f555ee930 --- /dev/null +++ b/src/tests/plugins/ticketoutputpdf/test_defaults_and_copy.py @@ -0,0 +1,183 @@ +# +# This file is part of pretix (Community Edition). +# +# Copyright (C) 2014-2020 Raphael Michel and contributors +# Copyright (C) 2020-2021 rami.io GmbH and contributors +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General +# Public License as published by the Free Software Foundation in version 3 of the License. +# +# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are +# applicable granting you additional permissions and placing additional restrictions on your usage of this software. +# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive +# this file, see . +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along with this program. If not, see +# . +# + +import pytest +from django.test import override_settings +from django.utils.timezone import now +from django_scopes import scopes_disabled + +from pretix.base.models import Event, Item, Organizer, Team, User + + +@pytest.fixture +def env(): + o = Organizer.objects.create(name='Dummy', slug='dummy') + event = Event.objects.create( + organizer=o, name='Dummy', slug='dummy', + date_from=now(), plugins='pretix.plugins.ticketoutputpdf' + ) + user = User.objects.create_user('dummy@dummy.dummy', 'dummy') + t = Team.objects.create(organizer=event.organizer, can_create_events=True, can_change_event_settings=True, can_change_items=True) + t.members.add(user) + t.limit_events.add(event) + item1 = Item.objects.create(event=event, name="Ticket", default_price=23) + tl = event.ticket_layouts.create(name="Foo", default=True, layout='[]') + return event, user, tl, item1 + + +@pytest.mark.django_db +@override_settings(PRETIX_PLUGINS_DEFAULT="pretix.plugins.ticketoutputpdf") +def test_api_clone_query(env, client): + client.login(email='dummy@dummy.dummy', password='dummy') + r = client.post('/api/v1/organizers/{}/events/?clone_from={}'.format(env[0].organizer.slug, env[0].slug), data={ + "name": {"en": "Cloned"}, + "slug": "cloned", + "live": False, + "testmode": False, + "currency": "EUR", + "date_from": "2017-12-27T10:00:00Z", + "plugins": ["pretix.plugins.ticketoutputpdf"], + }, content_type='application/json') + assert r.status_code == 201 + with scopes_disabled(): + e = Event.objects.get(slug="cloned") + assert e.ticket_layouts.count() == 1 + assert e.ticket_layouts.get().name == "Foo" + + +@pytest.mark.django_db +@override_settings(PRETIX_PLUGINS_DEFAULT="pretix.plugins.ticketoutputpdf") +def test_api_clone_path(env, client): + client.login(email='dummy@dummy.dummy', password='dummy') + r = client.post('/api/v1/organizers/{}/events/{}/clone/'.format(env[0].organizer.slug, env[0].slug), data={ + "name": {"en": "Cloned"}, + "slug": "cloned", + "live": False, + "testmode": False, + "currency": "EUR", + "date_from": "2017-12-27T10:00:00Z", + "plugins": ["pretix.plugins.ticketoutputpdf"], + }, content_type='application/json') + assert r.status_code == 201 + with scopes_disabled(): + e = Event.objects.get(slug="cloned") + assert e.ticket_layouts.count() == 1 + assert e.ticket_layouts.get().name == "Foo" + + +@pytest.mark.django_db +@override_settings(PRETIX_PLUGINS_DEFAULT="pretix.plugins.ticketoutputpdf") +def test_api_create(env, client): + client.login(email='dummy@dummy.dummy', password='dummy') + r = client.post('/api/v1/organizers/{}/events/'.format(env[0].organizer.slug), data={ + "name": {"en": "Cloned"}, + "slug": "cloned", + "live": False, + "testmode": False, + "currency": "EUR", + "date_from": "2017-12-27T10:00:00Z", + "plugins": ["pretix.plugins.ticketoutputpdf"], + }, content_type='application/json') + assert r.status_code == 201 + with scopes_disabled(): + e = Event.objects.get(slug="cloned") + assert e.ticket_layouts.count() == 1 + assert e.ticket_layouts.get().name == "Default layout" + + +@pytest.mark.django_db +@override_settings(PRETIX_PLUGINS_DEFAULT="pretix.plugins.ticketoutputpdf") +def test_control_clone(env, client): + client.login(email='dummy@dummy.dummy', password='dummy') + client.post(f'/control/events/add?clone={env[0].pk}', { + 'event_wizard-current_step': 'foundation', + 'event_wizard-prefix': 'event_wizard', + 'foundation-organizer': env[0].organizer.pk, + 'foundation-locales': ('en', 'de') + }) + client.post(f'/control/events/add?clone={env[0].pk}', { + 'event_wizard-current_step': 'basics', + 'event_wizard-prefix': 'event_wizard', + 'basics-name_0': '33C3', + 'basics-name_1': '33C3', + 'basics-slug': 'cloned', + 'basics-date_from_0': '2016-12-27', + 'basics-date_from_1': '10:00:00', + 'basics-date_to_0': '2016-12-30', + 'basics-date_to_1': '19:00:00', + 'basics-location_0': 'Hamburg', + 'basics-location_1': 'Hamburg', + 'basics-currency': 'EUR', + 'basics-tax_rate': '19.00', + 'basics-locale': 'en', + 'basics-timezone': 'Europe/Berlin', + 'basics-presale_start_0': '2016-11-01', + 'basics-presale_start_1': '10:00:00', + 'basics-presale_end_0': '2016-11-30', + 'basics-presale_end_1': '18:00:00', + }) + with scopes_disabled(): + e = Event.objects.get(slug="cloned") + assert e.ticket_layouts.count() == 1 + assert e.ticket_layouts.get().name == "Foo" + + +@pytest.mark.django_db +@override_settings(PRETIX_PLUGINS_DEFAULT="pretix.plugins.ticketoutputpdf") +def test_control_create(env, client): + client.login(email='dummy@dummy.dummy', password='dummy') + client.post('/control/events/add', { + 'event_wizard-current_step': 'foundation', + 'event_wizard-prefix': 'event_wizard', + 'foundation-organizer': env[0].organizer.pk, + 'foundation-locales': ('en', 'de') + }) + client.post('/control/events/add', { + 'event_wizard-current_step': 'basics', + 'event_wizard-prefix': 'event_wizard', + 'basics-name_0': '33C3', + 'basics-name_1': '33C3', + 'basics-slug': 'cloned', + 'basics-date_from_0': '2016-12-27', + 'basics-date_from_1': '10:00:00', + 'basics-date_to_0': '2016-12-30', + 'basics-date_to_1': '19:00:00', + 'basics-location_0': 'Hamburg', + 'basics-location_1': 'Hamburg', + 'basics-currency': 'EUR', + 'basics-tax_rate': '19.00', + 'basics-locale': 'en', + 'basics-timezone': 'Europe/Berlin', + 'basics-presale_start_0': '2016-11-01', + 'basics-presale_start_1': '10:00:00', + 'basics-presale_end_0': '2016-11-30', + 'basics-presale_end_1': '18:00:00', + }) + client.post('/control/events/add', { + 'event_wizard-current_step': 'copy', + 'event_wizard-prefix': 'event_wizard', + 'copy-copy_from_event': '' + }) + with scopes_disabled(): + e = Event.objects.get(slug="cloned") + assert e.ticket_layouts.count() == 1 + assert e.ticket_layouts.get().name == "Default layout"