-
+ {% if not event.presale_is_running %}
+
+ {% if event.presale_has_ended %}
+ {% blocktrans trimmed %}
+ The presale period for this event is over.
+ {% endblocktrans %}
+ {% elif event.settings.presale_start_show_date %}
+ {% blocktrans trimmed with date=event.presale_start|date time=event.presale_start|time %}
+ The presale for this event will start on {{ date }} at {{ time }}.
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans trimmed %}
+ The presale for this event has not yet started.
+ {% endblocktrans %}
+ {% endif %}
+
+ {% endif %}
+ {% if event.presale_is_running or event.settings.show_items_outside_presale_period %}
+
- {% else %}
-
-
-
{{ item.name }}
- {% if item.short_description %}
{{ item.short_description }}
{% endif %}
-
-
- {{ event.currency }} {{ item.price }}
- {% if item.tax_rate %}
-
{% blocktrans trimmed with rate=item.tax_rate %}
- incl. {{ rate }}% taxes
- {% endblocktrans %}
- {% endif %}
-
- {% if item.cached_availability.0 == 100 %}
-
-
-
- {% else %}
- {% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability.0 %}
- {% endif %}
-
-
- {% endif %}
- {% endfor %}
-
- {% endfor %}
-
-
+ {% endif %}
+ {% endfor %}
+
+ {% endfor %}
+ {% if event.presale_is_running %}
+
+ {% endif %}
+
+ {% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/src/pretix/presale/views/__init__.py b/src/pretix/presale/views/__init__.py
index 7223c86e8..02609cdad 100644
--- a/src/pretix/presale/views/__init__.py
+++ b/src/pretix/presale/views/__init__.py
@@ -4,8 +4,10 @@ from django.contrib.auth.views import redirect_to_login
from django.core.urlresolvers import reverse
from django.db.models import Q
+from django.http import HttpResponseBadRequest, HttpResponseForbidden
from django.utils.functional import cached_property
from django.utils.timezone import now
+from django.utils.translation import ugettext_lazy as _
from pretix.base.models import CartPosition
from pretix.base.signals import register_payment_providers
diff --git a/src/pretix/presale/views/cart.py b/src/pretix/presale/views/cart.py
index b87cdc569..8e298cb41 100644
--- a/src/pretix/presale/views/cart.py
+++ b/src/pretix/presale/views/cart.py
@@ -99,6 +99,13 @@ class CartAdd(EventViewMixin, CartActionMixin, View):
self.msg_some_unavailable = False
def post(self, request, *args, **kwargs):
+ if request.event.presale_start and now() < request.event.presale_start:
+ messages.error(request, _('The presale period not yet started.'))
+ return redirect(self.get_failure_url())
+ if request.event.presale_end and now() > request.event.presale_end:
+ messages.error(request, _('The presale period has ended.'))
+ return redirect(self.get_failure_url())
+
self.items = self._items_from_post_data()
# We do not use EventLoginRequiredMixin here, as we want to store stuff into the
diff --git a/src/pretix/presale/views/checkout.py b/src/pretix/presale/views/checkout.py
index 94c6d3dd4..584e9906d 100644
--- a/src/pretix/presale/views/checkout.py
+++ b/src/pretix/presale/views/checkout.py
@@ -355,10 +355,11 @@ class OrderConfirm(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, Ch
quota_ok = False
break
if quota_ok:
- cp = cp.clone()
- cartpos[i] = cp
- cp.expires = now() + timedelta(minutes=self.request.event.settings.get('reservation_time', as_type=int))
- cp.save()
+ if not self.request.event.presale_end or now() < self.request.event.presale_end:
+ cp = cp.clone()
+ cartpos[i] = cp
+ cp.expires = now() + timedelta(minutes=self.request.event.settings.get('reservation_time', as_type=int))
+ cp.save()
else:
cp.delete() # Sorry!
if not self.msg_some_unavailable: # Everything went well
diff --git a/src/tests/presale/test_event.py b/src/tests/presale/test_event.py
index b9a56ead8..97c85f195 100644
--- a/src/tests/presale/test_event.py
+++ b/src/tests/presale/test_event.py
@@ -1,6 +1,7 @@
import datetime
import time
from django.test import TestCase
+from django.utils.timezone import now
from pretix.base.models import Item, Organizer, Event, ItemCategory, Quota, Property, PropertyValue, ItemVariation, User
from tests.base import BrowserTest
@@ -186,3 +187,56 @@ class LoginTest(EventTestMixin, TestCase):
'/%s/%s/login' % (self.orga.slug, self.event.slug),
)
self.assertEqual(response.status_code, 200)
+
+
+class DeadlineTest(EventTestMixin, TestCase):
+
+ def setUp(self):
+ super().setUp()
+
+ def test_not_yet_started(self):
+ self.event.presale_start = now() + datetime.timedelta(days=1)
+ self.event.save()
+ response = self.client.get(
+ '/%s/%s/' % (self.orga.slug, self.event.slug)
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertIn('alert-info', response.rendered_content)
+ self.assertNotIn('checkout-button-row', response.rendered_content)
+ response = self.client.post(
+ '/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
+ follow=True
+ )
+ self.assertIn('alert-danger', response.rendered_content)
+ self.assertIn('not yet started', response.rendered_content)
+
+ def test_over(self):
+ self.event.presale_end = now() - datetime.timedelta(days=1)
+ self.event.save()
+ response = self.client.get(
+ '/%s/%s/' % (self.orga.slug, self.event.slug)
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertIn('alert-info', response.rendered_content)
+ self.assertNotIn('checkout-button-row', response.rendered_content)
+ response = self.client.post(
+ '/%s/%s/cart/add' % (self.orga.slug, self.event.slug),
+ follow=True
+ )
+ self.assertIn('alert-danger', response.rendered_content)
+ self.assertIn('is over', response.rendered_content)
+
+ def test_in_time(self):
+ self.event.presale_start = now() - datetime.timedelta(days=1)
+ self.event.presale_end = now() + datetime.timedelta(days=1)
+ self.event.save()
+ response = self.client.get(
+ '/%s/%s/' % (self.orga.slug, self.event.slug)
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertNotIn('alert-info', response.rendered_content)
+ self.assertIn('checkout-button-row', response.rendered_content)
+ response = self.client.post(
+ '/%s/%s/cart/add' % (self.orga.slug, self.event.slug)
+ )
+ self.assertNotEqual(response.status_code, 403)