Repository: pretix/pretix

Issue:
Error message if an event series has no date
Hi!

I am using the latest "stable" pretix release from today. When an event series has no scheduled dates, opening the "event selector" (top left corner) leads into an "internal server error".

![image](https://user-images.githubusercontent.com/6865786/31251718-3ffd282e-aa1f-11e7-9288-7efbd30c3a68.png)


![image](https://user-images.githubusercontent.com/6865786/31251655-00bd4c2a-aa1f-11e7-9a57-e3370bb292e7.png)

With best regards
Jan Felix


Discussion:
@raphaelm Can I take a shot at this?
@aimanparvaiz Sure, go ahead! :)
Hey @jfwiebe 
I am trying to reproduce this issue but haven't been successful yet, I will share the steps I have taken please let me know if I am missing something here.
1. I created a series of events (event 7, event 7.1, event 7.2)with a start date and no end date.
2. I clicked on Dates in the left column and also at the top left and it didn't throw any kind of error.
3. I also tried the same when there was only 1 event in the series (only event 7) and I didnt see any errors either. 
I am attaching a screenshot here, I believe I might be missing some steps here to reproduce this error please throw some light on it if possible. 
I am using latest master for this code as of 10/23/2017.
![screen shot 2017-10-23 at 10 03 59 am](https://user-images.githubusercontent.com/671161/31902620-4cd6571e-b7da-11e7-9437-8be9fbc5c9e8.png)

Hi,

sorry if my report was not clear enough. To reproduce the error, create an event series and then delete all dates. Now click on the event selector and you‘ll get the error.

With best regards
Jan Felix


> Am 23.10.2017 um 19:10 schrieb Aiman Parvaiz <notifications@github.com>:
> 
> Hey @jfwiebe
> I am trying to reproduce this issue but haven't been successful yet, I will share the steps I have taken please let me know if I am missing something here.
> 
> I created a series of events (event 7, event 7.2, event 7.3)with a start date and no end date.
> I clicked on Dates in the left column and also at the top left and it didn't throw any kind of error.
> I also tried the same when there was only 1 event in the series (only event 7) and I didnt see any errors either.
> I am attaching a screenshot here, I believe I might be missing some steps here to reproduce this error please throw some light on it if possible.
> I am using latest master for this code as of 10/23/2017.
> 
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or mute the thread.
> 

Thanks for getting back @jfwiebe. Ok, trying what you mentioned didnt produce error but something else did, let me list my steps. 
1. I deleted all dates under event7.
2. Clicked on Dates in the left menu and also on the top left, none of this broke anything (see screen shot attached)
3. On clicking pretix.de (at the top left corner) though led to this error:
```
[23/Oct/2017 23:51:10] "GET /static/pretixbase/img/favicon.ico HTTP/1.1" 200 1150
ERROR 2017-10-23 23:51:12,876 django.request exception Internal Server Error: /control/
Traceback (most recent call last):
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/aimanparvaiz/PycharmProjects/pretix/src/pretix/control/views/dashboards.py", line 415, in user_index
    for r, result in user_dashboard_widgets.send(request, user=request.user):
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 193, in send
    for receiver in self._live_receivers(sender)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 193, in <listcomp>
    for receiver in self._live_receivers(sender)
  File "/Users/aimanparvaiz/PycharmProjects/pretix/src/pretix/control/views/dashboards.py", line 339, in user_event_widgets
    (event.min_from).astimezone(tz),
AttributeError: 'NoneType' object has no attribute 'astimezone'
Internal Server Error: /control/
Traceback (most recent call last):
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/aimanparvaiz/PycharmProjects/pretix/src/pretix/control/views/dashboards.py", line 415, in user_index
    for r, result in user_dashboard_widgets.send(request, user=request.user):
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 193, in send
    for receiver in self._live_receivers(sender)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 193, in <listcomp>
    for receiver in self._live_receivers(sender)
  File "/Users/aimanparvaiz/PycharmProjects/pretix/src/pretix/control/views/dashboards.py", line 339, in user_event_widgets
    (event.min_from).astimezone(tz),
AttributeError: 'NoneType' object has no attribute 'astimezone'
ERROR 2017-10-23 23:51:12,876 django.request exception Internal Server Error: /control/
Traceback (most recent call last):
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/aimanparvaiz/PycharmProjects/pretix/src/pretix/control/views/dashboards.py", line 415, in user_index
    for r, result in user_dashboard_widgets.send(request, user=request.user):
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 193, in send
    for receiver in self._live_receivers(sender)
  File "/Users/aimanparvaiz/PycharmProjects/env/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 193, in <listcomp>
    for receiver in self._live_receivers(sender)
  File "/Users/aimanparvaiz/PycharmProjects/pretix/src/pretix/control/views/dashboards.py", line 339, in user_event_widgets
    (event.min_from).astimezone(tz),
AttributeError: 'NoneType' object has no attribute 'astimezone'
```

Is this what we are trying to reproduce and fix?
![screen shot 2017-10-23 at 2 13 04 pm](https://user-images.githubusercontent.com/671161/31913707-d68bfd70-b7fc-11e7-9e83-3d3592ecbcb2.png)
![screen shot 2017-10-23 at 2 13 18 pm](https://user-images.githubusercontent.com/671161/31913712-d9994b58-b7fc-11e7-92c4-c7c4889c1ff5.png)

Yes indeed, this is the error I am talking about.
(In my first report, I stated that it happens on the date selector, but that was wrong - I'm sorry!)
No worries, now since I have reproduced it, I will start working on it.
Thanks

Resolving patch:
diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py
--- a/src/pretix/base/models/event.py
+++ b/src/pretix/base/models/event.py
@@ -639,6 +639,9 @@ def meta_data(self):
         data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
         return data
 
+    def allow_delete(self):
+        return self.event.subevents.count() > 1
+
     def delete(self, *args, **kwargs):
         super().delete(*args, **kwargs)
         if self.event:
diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py
--- a/src/pretix/control/views/subevents.py
+++ b/src/pretix/control/views/subevents.py
@@ -101,10 +101,13 @@ def delete(self, request, *args, **kwargs):
         self.object = self.get_object()
         success_url = self.get_success_url()
 
-        if self.get_object().orderposition_set.count() > 0:
+        if self.object.orderposition_set.count() > 0:
             messages.error(request, pgettext_lazy('subevent', 'A date can not be deleted if orders already have been '
                                                   'placed.'))
             return HttpResponseRedirect(self.get_success_url())
+        elif not self.object.allow_delete():  # checking if this is the last date in the event series
+            messages.error(request, pgettext_lazy('subevent', 'The last date of an event series can not be deleted.'))
+            return HttpResponseRedirect(self.get_success_url())
         else:
             self.object.log_action('pretix.subevent.deleted', user=self.request.user)
             self.object.delete()


Test patch:
diff --git a/src/tests/control/test_events.py b/src/tests/control/test_events.py
--- a/src/tests/control/test_events.py
+++ b/src/tests/control/test_events.py
@@ -8,7 +8,7 @@
 from tests.base import SoupTest, extract_form_fields
 
 from pretix.base.models import (
-    Event, Order, OrderPosition, Organizer, Team, User,
+    Event, Order, OrderPosition, Organizer, SubEvent, Team, User,
 )
 from pretix.base.models.items import SubEventItem
 from pretix.testutils.mock import mocker_context
@@ -594,6 +594,7 @@ def setUp(self):
         self.client.login(email='dummy@dummy.dummy', password='dummy')
 
         self.subevent1 = self.event1.subevents.create(name='SE1', date_from=now())
+        self.subevent2 = self.event1.subevents.create(name='SE2', date_from=now())
 
     def test_list(self):
         doc = self.get_doc('/control/event/ccc/30c3/subevents/')
@@ -699,7 +700,11 @@ def test_delete(self):
         assert doc.select("button")
         doc = self.post_doc('/control/event/ccc/30c3/subevents/%d/delete' % self.subevent1.pk, {})
         assert doc.select(".alert-success")
-        assert not SubEventItem.objects.filter(pk=self.subevent1.pk).exists()
+        # deleting the second event
+        doc = self.post_doc('/control/event/ccc/30c3/subevents/%d/delete' % self.subevent2.pk, {})
+        assert doc.select(".alert-danger")
+        assert SubEvent.objects.filter(pk=self.subevent2.pk).exists()
+        assert not SubEvent.objects.filter(pk=self.subevent1.pk).exists()
 
     def test_delete_with_orders(self):
         o = Order.objects.create(