forked from CGM_Public/pretix_original
Enforce start and end time of presale (#15)
This commit is contained in:
@@ -482,6 +482,20 @@ class Event(Versionable):
|
||||
"""
|
||||
return SettingsProxy(self, type=EventSetting, parent=self.organizer)
|
||||
|
||||
@property
|
||||
def presale_has_ended(self):
|
||||
if self.presale_end and now() > self.presale_end:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def presale_is_running(self):
|
||||
if self.presale_start and now() < self.presale_start:
|
||||
return False
|
||||
if self.presale_end and now() > self.presale_end:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class EventPermission(Versionable):
|
||||
"""
|
||||
|
||||
@@ -41,6 +41,14 @@ DEFAULTS = {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
},
|
||||
'presale_start_show_date': {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
},
|
||||
'show_items_outside_presale_period': {
|
||||
'default': 'True',
|
||||
'type': bool
|
||||
},
|
||||
'timezone': {
|
||||
'default': settings.TIME_ZONE,
|
||||
'type': str
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
<fieldset>
|
||||
<legend>{% trans "Timeline" %}</legend>
|
||||
{% bootstrap_field form.presale_start layout="horizontal" %}
|
||||
{% bootstrap_field sform.presale_start_show_date layout="horizontal" %}
|
||||
{% bootstrap_field form.presale_end layout="horizontal" %}
|
||||
{% bootstrap_field sform.show_items_outside_presale_period layout="horizontal" %}
|
||||
{% bootstrap_field sform.payment_term_days layout="horizontal" %}
|
||||
{% bootstrap_field sform.payment_term_last layout="horizontal" %}
|
||||
{% bootstrap_field sform.payment_term_accept_late layout="horizontal" %}
|
||||
|
||||
@@ -55,6 +55,16 @@ class EventSettingsForm(SettingsForm):
|
||||
label='Payment term in days',
|
||||
help_text=_("The number of days after placing an order the user has to pay to preserve his reservation."),
|
||||
)
|
||||
show_items_outside_presale_period = forms.BooleanField(
|
||||
label=_("Show items outside presale period"),
|
||||
help_text=_("Show item details before presale has started and after presale has ended"),
|
||||
required=False
|
||||
)
|
||||
presale_start_show_date = forms.BooleanField(
|
||||
label=_("Show start date"),
|
||||
help_text=_("Show the presale start date before presale has started"),
|
||||
required=False
|
||||
)
|
||||
payment_term_last = forms.DateTimeField(
|
||||
label='Last date of payments',
|
||||
help_text=_("The last date any payments are accepted. This has precedence over the number of "
|
||||
|
||||
@@ -30,97 +30,118 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post"
|
||||
action="{% url "presale:event.cart.add" organizer=request.event.organizer.slug event=request.event.slug %}?next={{ request.path_info|urlencode }}">
|
||||
{% csrf_token %}
|
||||
{% for tup in items_by_category %}
|
||||
<section>
|
||||
{% if tup.0 %}<h3>{{ tup.0.name }}</h3>{% endif %}
|
||||
{% for item in tup.1 %}
|
||||
{% if item.has_variations %}
|
||||
<div class="item-with-variations">
|
||||
<div class="row-fluid product-row headline">
|
||||
{% if not event.presale_is_running %}
|
||||
<div class="alert alert-info">
|
||||
{% 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 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if event.presale_is_running or event.settings.show_items_outside_presale_period %}
|
||||
<form method="post"
|
||||
action="{% url "presale:event.cart.add" organizer=request.event.organizer.slug event=request.event.slug %}?next={{ request.path_info|urlencode }}">
|
||||
{% csrf_token %}
|
||||
{% for tup in items_by_category %}
|
||||
<section>
|
||||
{% if tup.0 %}<h3>{{ tup.0.name }}</h3>{% endif %}
|
||||
{% for item in tup.1 %}
|
||||
{% if item.has_variations %}
|
||||
<div class="item-with-variations">
|
||||
<div class="row-fluid product-row headline">
|
||||
<div class="col-md-8 col-xs-12">
|
||||
<a href="javascript:void();" data-toggle="variations">
|
||||
<strong>{{ item.name }}</strong>
|
||||
</a>
|
||||
{% if item.short_description %}<p>{{ item.short_description }}</p>{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 price">
|
||||
{% blocktrans trimmed with minprice=item.min_price|floatformat:2 currency=event.currency %}
|
||||
from {{ currency }} {{ minprice }}
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 availability-box">
|
||||
<a href="javascript:void();" data-toggle="variations" class="js-only">
|
||||
{% trans "Show variants" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="variations">
|
||||
{% for var in item.available_variations %}
|
||||
<div class="row-fluid product-row variation">
|
||||
<div class="col-md-8 col-xs-12">
|
||||
{{ var }}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 price">
|
||||
{{ event.currency }} {{ var.price|floatformat:2 }}
|
||||
{% if item.tax_rate %}
|
||||
<br /><small>{% blocktrans trimmed with rate=item.tax_rate %}
|
||||
incl. {{ rate }}% taxes
|
||||
{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if var.cached_availability.0 == 100 %}
|
||||
<div class="col-md-2 col-xs-6 availability-box available">
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ var.cached_availability.1 }}"
|
||||
name="variation_{{ item.identity }}_{{ var.variation.identity }}">
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "pretixpresale/event/fragment_availability.html" with avail=var.cached_availability.0 %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="row-fluid product-row simple">
|
||||
<div class="col-md-8 col-xs-12">
|
||||
<a href="javascript:void();" data-toggle="variations">
|
||||
<strong>{{ item.name }}</strong>
|
||||
</a>
|
||||
<strong>{{ item.name }}</strong>
|
||||
{% if item.short_description %}<p>{{ item.short_description }}</p>{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 price">
|
||||
{% blocktrans trimmed with minprice=item.min_price|floatformat:2 currency=event.currency %}
|
||||
from {{ currency }} {{ minprice }}
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 availability-box">
|
||||
<a href="javascript:void();" data-toggle="variations" class="js-only">
|
||||
{% trans "Show variants" %}
|
||||
</a>
|
||||
{{ event.currency }} {{ item.price }}
|
||||
{% if item.tax_rate %}
|
||||
<br /><small>{% blocktrans trimmed with rate=item.tax_rate %}
|
||||
incl. {{ rate }}% taxes
|
||||
{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if item.cached_availability.0 == 100 %}
|
||||
<div class="col-md-2 col-xs-6 availability-box available">
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ item.cached_availability.1 }}" name="item_{{ item.identity }}">
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability.0 %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="variations">
|
||||
{% for var in item.available_variations %}
|
||||
<div class="row-fluid product-row variation">
|
||||
<div class="col-md-8 col-xs-12">
|
||||
{{ var }}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 price">
|
||||
{{ event.currency }} {{ var.price|floatformat:2 }}
|
||||
{% if item.tax_rate %}
|
||||
<br /><small>{% blocktrans trimmed with rate=item.tax_rate %}
|
||||
incl. {{ rate }}% taxes
|
||||
{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if var.cached_availability.0 == 100 %}
|
||||
<div class="col-md-2 col-xs-6 availability-box available">
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ var.cached_availability.1 }}"
|
||||
name="variation_{{ item.identity }}_{{ var.variation.identity }}">
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "pretixpresale/event/fragment_availability.html" with avail=var.cached_availability.0 %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="row-fluid product-row simple">
|
||||
<div class="col-md-8 col-xs-12">
|
||||
<strong>{{ item.name }}</strong>
|
||||
{% if item.short_description %}<p>{{ item.short_description }}</p>{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 price">
|
||||
{{ event.currency }} {{ item.price }}
|
||||
{% if item.tax_rate %}
|
||||
<br /><small>{% blocktrans trimmed with rate=item.tax_rate %}
|
||||
incl. {{ rate }}% taxes
|
||||
{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if item.cached_availability.0 == 100 %}
|
||||
<div class="col-md-2 col-xs-6 availability-box available">
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ item.cached_availability.1 }}" name="item_{{ item.identity }}">
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "pretixpresale/event/fragment_availability.html" with avail=item.cached_availability.0 %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
<div class="row-fluid checkout-button-row">
|
||||
<div class="col-md-4 col-md-offset-8">
|
||||
<button class="btn btn-block btn-primary btn-lg" type="submit">
|
||||
<i class="fa fa-shopping-cart"></i> {% trans "Add to cart" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% if event.presale_is_running %}
|
||||
<div class="row-fluid checkout-button-row">
|
||||
<div class="col-md-4 col-md-offset-8">
|
||||
<button class="btn btn-block btn-primary btn-lg" type="submit">
|
||||
<i class="fa fa-shopping-cart"></i> {% trans "Add to cart" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user