mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Add sub-events and relative date settings (#503)
* Data model * little crud * SubEventItemForm etc * Drop SubEventItem.active, quota editor * Fix failing tests * First frontend stuff * Addons form stuff * Quota calculation * net price display on EventIndex * Add tests, solve some bugs * Correct quota selection in more places, consolidate pricing logic * Fix failing quota tests * Fix TypeError * Add tests for checkout * Fixed a bug in QuotaForm * Prevent immutable cart if a quota was removed from an item * Add tests for pricing * Handle waiting list * Filter in check-in list * Fixed import lost in rebase * Fix waiting list widget * Voucher management * Voucher redemption * Fix broken tests * Add subevents to OrderChangeManager * Create a subevent during event creation * Fix bulk voucher creation * Introduce subevent.active * Copy from for subevents * Show active in list * ICal download for subevents * Check start and end of presale * Failing tests / show cart logic * Test * Rebase migrations * REST API integration of sub-events * Integrate quota calculation into the traditional quota form * Make subevent argument to add_position optional * Log-display foo * pretixdroid and subevents * Filter by subevent * Add more tests * Some mor tests * Rebase fixes * More tests * Relative dates * Restrict selection in relative datetime widgets * Filter subevent list * Re-label has_subevents * Rebase fixes, subevents in calendar view * Performance and caching issues * Refactor calendar templates * Permission tests * Calendar fixes and month selection * subevent selection * Rename subevents to dates * Add tests for calendar views
This commit is contained in:
@@ -415,7 +415,7 @@ class ImportView(ListView):
|
||||
|
||||
if 'event' in self.kwargs:
|
||||
ctx['basetpl'] = 'pretixplugins/banktransfer/import_base.html'
|
||||
if self.request.event.settings.get('payment_term_last'):
|
||||
if not self.request.event.has_subevents and self.request.event.settings.get('payment_term_last'):
|
||||
if now() > self.request.event.payment_term_last:
|
||||
ctx['no_more_payments'] = True
|
||||
else:
|
||||
|
||||
@@ -4,7 +4,9 @@ from collections import OrderedDict
|
||||
|
||||
from django import forms
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.utils.translation import (
|
||||
pgettext, pgettext_lazy, ugettext as _, ugettext_lazy,
|
||||
)
|
||||
|
||||
from pretix.base.exporter import BaseExporter
|
||||
from pretix.base.models import Order, OrderPosition, Question
|
||||
@@ -21,7 +23,7 @@ class CSVCheckinList(BaseCheckinList):
|
||||
|
||||
@property
|
||||
def export_form_fields(self):
|
||||
return OrderedDict(
|
||||
d = OrderedDict(
|
||||
[
|
||||
('items',
|
||||
forms.ModelMultipleChoiceField(
|
||||
@@ -61,6 +63,14 @@ class CSVCheckinList(BaseCheckinList):
|
||||
)),
|
||||
]
|
||||
)
|
||||
if self.event.has_subevents:
|
||||
d['subevent'] = forms.ModelChoiceField(
|
||||
self.event.subevents.all(),
|
||||
label=pgettext_lazy('subevent', 'Date'),
|
||||
required=False,
|
||||
empty_label=pgettext_lazy('subevent', 'All dates')
|
||||
)
|
||||
return d
|
||||
|
||||
def render(self, form_data: dict):
|
||||
output = io.StringIO()
|
||||
@@ -81,6 +91,8 @@ class CSVCheckinList(BaseCheckinList):
|
||||
headers = [
|
||||
_('Order code'), _('Attendee name'), _('Product'), _('Price')
|
||||
]
|
||||
if form_data.get('subevent'):
|
||||
qs = qs.filter(subevent=form_data.get('subevent'))
|
||||
if form_data['paid_only']:
|
||||
qs = qs.filter(order__status=Order.STATUS_PAID)
|
||||
else:
|
||||
@@ -93,6 +105,9 @@ class CSVCheckinList(BaseCheckinList):
|
||||
if self.event.settings.attendee_emails_asked:
|
||||
headers.append(_('E-mail'))
|
||||
|
||||
if self.event.has_subevents:
|
||||
headers.append(pgettext('subevent', 'Date'))
|
||||
|
||||
for q in questions:
|
||||
headers.append(str(q.question))
|
||||
|
||||
@@ -111,6 +126,8 @@ class CSVCheckinList(BaseCheckinList):
|
||||
row.append(op.secret)
|
||||
if self.event.settings.attendee_emails_asked:
|
||||
row.append(op.attendee_email or (op.addon_to.attendee_email if op.addon_to else ''))
|
||||
if self.event.has_subevents:
|
||||
row.append(str(op.subevent))
|
||||
acache = {}
|
||||
for a in op.answers.all():
|
||||
acache[a.question_id] = str(a)
|
||||
|
||||
@@ -280,7 +280,7 @@ class Paypal(BasePaymentProvider):
|
||||
order.save()
|
||||
|
||||
def order_can_retry(self, order):
|
||||
return self._is_still_available()
|
||||
return self._is_still_available(order=order)
|
||||
|
||||
def order_prepare(self, request, order):
|
||||
self.init_api()
|
||||
|
||||
@@ -29,12 +29,32 @@
|
||||
The code tells the app all it needs about your event.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<div id="qrcodeCanvas"></div>
|
||||
<a href="?flush_key=1" class="btn btn-default">{% trans "Reset authentication token" %}</a>
|
||||
<script type="text/json" id="qrdata">
|
||||
{{ qrdata|safe }}
|
||||
{% if request.event.has_subevents %}
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
<p>
|
||||
{% if request.event.has_subevents %}
|
||||
<select name="subevent" class="form-control">
|
||||
<option value="">{% trans "Choose date" context "subevent" %}</option>
|
||||
{% for se in request.event.subevents.all %}
|
||||
<option value="{{ se.id }}"
|
||||
{% if request.GET.subevent|add:0 == se.id %}selected="selected"{% endif %}>
|
||||
{{ se.name }} – {{ se.get_date_range_display }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
<button class="btn btn-primary" type="submit">{% trans "Show configuration" %}</button>
|
||||
</p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if not request.event.has_subevents or subevent %}
|
||||
<div id="qrcodeCanvas"></div>
|
||||
<a href="?flush_key=1" class="btn btn-default">{% trans "Reset authentication token" %}</a>
|
||||
<script type="text/json" id="qrdata">
|
||||
{{ qrdata|safe }}
|
||||
|
||||
</script>
|
||||
</script>
|
||||
{% endif %}
|
||||
<script type="text/javascript" src="{% static "pretixplugins/pretixdroid/pretixdroid.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
from django.conf.urls import url
|
||||
from django.conf.urls import include, url
|
||||
|
||||
from . import views
|
||||
|
||||
pretixdroid_api_patterns = [
|
||||
url(r'^redeem/', views.ApiRedeemView.as_view(),
|
||||
name='api.redeem'),
|
||||
url(r'^search/', views.ApiSearchView.as_view(),
|
||||
name='api.search'),
|
||||
url(r'^download/', views.ApiDownloadView.as_view(),
|
||||
name='api.download'),
|
||||
url(r'^status/', views.ApiStatusView.as_view(),
|
||||
name='api.status'),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/pretixdroid/', views.ConfigView.as_view(),
|
||||
name='config'),
|
||||
url(r'^pretixdroid/api/(?P<organizer>[^/]+)/(?P<event>[^/]+)/redeem/', views.ApiRedeemView.as_view(),
|
||||
name='api.redeem'),
|
||||
url(r'^pretixdroid/api/(?P<organizer>[^/]+)/(?P<event>[^/]+)/search/', views.ApiSearchView.as_view(),
|
||||
name='api.search'),
|
||||
url(r'^pretixdroid/api/(?P<organizer>[^/]+)/(?P<event>[^/]+)/download/', views.ApiDownloadView.as_view(),
|
||||
name='api.download'),
|
||||
url(r'^pretixdroid/api/(?P<organizer>[^/]+)/(?P<event>[^/]+)/status/', views.ApiStatusView.as_view(),
|
||||
name='api.status'),
|
||||
url(r'^pretixdroid/api/(?P<organizer>[^/]+)/(?P<event>[^/]+)/(?P<subevent>\d+)/',
|
||||
include(pretixdroid_api_patterns)),
|
||||
url(r'^pretixdroid/api/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include(pretixdroid_api_patterns)),
|
||||
]
|
||||
|
||||
@@ -8,6 +8,7 @@ from django.db.models import Count, Q
|
||||
from django.http import (
|
||||
HttpResponseForbidden, HttpResponseNotFound, JsonResponse,
|
||||
)
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.timezone import now
|
||||
@@ -15,6 +16,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import TemplateView, View
|
||||
|
||||
from pretix.base.models import Checkin, Event, Order, OrderPosition
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.helpers.urls import build_absolute_uri
|
||||
from pretix.multidomain.urlreverse import (
|
||||
@@ -37,13 +39,26 @@ class ConfigView(EventPermissionRequiredMixin, TemplateView):
|
||||
allowed_chars=string.ascii_uppercase + string.ascii_lowercase + string.digits)
|
||||
self.request.event.settings.set('pretixdroid_key', key)
|
||||
|
||||
subevent = None
|
||||
url = build_absolute_uri('plugins:pretixdroid:api.redeem', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug
|
||||
})
|
||||
if self.request.event.has_subevents:
|
||||
if self.request.GET.get('subevent'):
|
||||
subevent = get_object_or_404(SubEvent, event=self.request.event, pk=self.request.GET['subevent'])
|
||||
url = build_absolute_uri('plugins:pretixdroid:api.redeem', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
'subevent': subevent.pk
|
||||
})
|
||||
|
||||
ctx['subevent'] = subevent
|
||||
|
||||
ctx['qrdata'] = json.dumps({
|
||||
'version': API_VERSION,
|
||||
'url': build_absolute_uri('plugins:pretixdroid:api.redeem', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug
|
||||
})[:-7], # the slice removes the redeem/ part at the end
|
||||
'key': key
|
||||
'url': url[:-7], # the slice removes the redeem/ part at the end
|
||||
'key': key,
|
||||
})
|
||||
return ctx
|
||||
|
||||
@@ -61,9 +76,19 @@ class ApiView(View):
|
||||
return HttpResponseNotFound('Unknown event')
|
||||
|
||||
if (not self.event.settings.get('pretixdroid_key')
|
||||
or self.event.settings.get('pretixdroid_key') != request.GET.get('key', '')):
|
||||
or self.event.settings.get('pretixdroid_key') != request.GET.get('key', '-unset-')):
|
||||
return HttpResponseForbidden('Invalid key')
|
||||
|
||||
self.subevent = None
|
||||
if self.event.has_subevents:
|
||||
if 'subevent' in kwargs:
|
||||
self.subevent = get_object_or_404(SubEvent, event=self.event, pk=kwargs['subevent'])
|
||||
else:
|
||||
return HttpResponseForbidden('No subevent selected.')
|
||||
else:
|
||||
if 'subevent' in kwargs:
|
||||
return HttpResponseForbidden('Subevents not enabled.')
|
||||
|
||||
return super().dispatch(request, **kwargs)
|
||||
|
||||
|
||||
@@ -85,7 +110,7 @@ class ApiRedeemView(ApiView):
|
||||
with transaction.atomic():
|
||||
created = False
|
||||
op = OrderPosition.objects.select_related('item', 'variation', 'order', 'addon_to').get(
|
||||
order__event=self.event, secret=secret
|
||||
order__event=self.event, secret=secret, subevent=self.subevent
|
||||
)
|
||||
if op.order.status == Order.STATUS_PAID or force:
|
||||
ci, created = Checkin.objects.get_or_create(position=op, defaults={
|
||||
@@ -161,6 +186,7 @@ class ApiSearchView(ApiView):
|
||||
& Q(
|
||||
Q(secret__istartswith=query) | Q(attendee_name__icontains=query) | Q(order__code__istartswith=query)
|
||||
)
|
||||
& Q(subevent=self.subevent)
|
||||
).annotate(checkin_cnt=Count('checkins'))[:25]
|
||||
|
||||
response['results'] = [serialize_op(op) for op in ops]
|
||||
@@ -177,7 +203,7 @@ class ApiDownloadView(ApiView):
|
||||
}
|
||||
|
||||
ops = OrderPosition.objects.select_related('item', 'variation', 'order', 'addon_to').filter(
|
||||
Q(order__event=self.event)
|
||||
Q(order__event=self.event) & Q(subevent=self.subevent)
|
||||
).annotate(checkin_cnt=Count('checkins'))
|
||||
response['results'] = [serialize_op(op) for op in ops]
|
||||
|
||||
@@ -186,25 +212,27 @@ class ApiDownloadView(ApiView):
|
||||
|
||||
class ApiStatusView(ApiView):
|
||||
def get(self, request, **kwargs):
|
||||
ev = self.subevent or self.event
|
||||
response = {
|
||||
'version': API_VERSION,
|
||||
'event': {
|
||||
'name': str(self.event),
|
||||
'name': str(ev.name),
|
||||
'slug': self.event.slug,
|
||||
'organizer': {
|
||||
'name': str(self.event.organizer),
|
||||
'slug': self.event.organizer.slug
|
||||
},
|
||||
'date_from': self.event.date_from,
|
||||
'date_to': self.event.date_to,
|
||||
'subevent': self.subevent.pk if self.subevent else str(self.event),
|
||||
'date_from': ev.date_from,
|
||||
'date_to': ev.date_to,
|
||||
'timezone': self.event.settings.timezone,
|
||||
'url': event_absolute_uri(self.event, 'presale:event.index')
|
||||
},
|
||||
'checkins': Checkin.objects.filter(
|
||||
position__order__event=self.event
|
||||
position__order__event=self.event, position__subevent=self.subevent
|
||||
).count(),
|
||||
'total': OrderPosition.objects.filter(
|
||||
order__event=self.event, order__status=Order.STATUS_PAID
|
||||
order__event=self.event, order__status=Order.STATUS_PAID, subevent=self.subevent
|
||||
).count()
|
||||
}
|
||||
|
||||
@@ -212,28 +240,32 @@ class ApiStatusView(ApiView):
|
||||
p['item']: p['cnt']
|
||||
for p in OrderPosition.objects.filter(
|
||||
order__event=self.event,
|
||||
order__status=Order.STATUS_PAID
|
||||
order__status=Order.STATUS_PAID,
|
||||
subevent=self.subevent
|
||||
).order_by().values('item').annotate(cnt=Count('id'))
|
||||
}
|
||||
op_by_variation = {
|
||||
p['variation']: p['cnt']
|
||||
for p in OrderPosition.objects.filter(
|
||||
order__event=self.event,
|
||||
order__status=Order.STATUS_PAID
|
||||
order__status=Order.STATUS_PAID,
|
||||
subevent=self.subevent
|
||||
).order_by().values('variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_item = {
|
||||
p['position__item']: p['cnt']
|
||||
for p in Checkin.objects.filter(
|
||||
position__order__event=self.event,
|
||||
position__order__status=Order.STATUS_PAID
|
||||
position__order__status=Order.STATUS_PAID,
|
||||
position__subevent=self.subevent
|
||||
).order_by().values('position__item').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_variation = {
|
||||
p['position__variation']: p['cnt']
|
||||
for p in Checkin.objects.filter(
|
||||
position__order__event=self.event,
|
||||
position__order__status=Order.STATUS_PAID
|
||||
position__order__status=Order.STATUS_PAID,
|
||||
position__subevent=self.subevent
|
||||
).order_by().values('position__variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ from django.contrib.staticfiles import finders
|
||||
from django.db.models import Sum
|
||||
from django.utils.formats import date_format, localize
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import pgettext, pgettext_lazy, ugettext as _
|
||||
|
||||
from pretix.base.exporter import BaseExporter
|
||||
from pretix.base.models import Order, OrderPosition
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.services.stats import order_overview
|
||||
|
||||
|
||||
@@ -161,6 +162,13 @@ class OverviewReport(Report):
|
||||
Paragraph(_('Orders by product'), headlinestyle),
|
||||
Spacer(1, 5 * mm)
|
||||
]
|
||||
if self.form_data.get('subevent'):
|
||||
try:
|
||||
subevent = self.event.subevents.get(pk=self.form_data.get('subevent'))
|
||||
except SubEvent.DoesNotExist:
|
||||
subevent = self.form_data.get('subevent')
|
||||
story.append(Paragraph(pgettext('subevent', 'Date: {}').format(subevent), self.get_style()))
|
||||
story.append(Spacer(1, 5 * mm))
|
||||
tdata = [
|
||||
[
|
||||
_('Product'), _('Canceled'), '', _('Refunded'), '', _('Expired'), '', _('Purchased'),
|
||||
@@ -180,7 +188,7 @@ class OverviewReport(Report):
|
||||
],
|
||||
]
|
||||
|
||||
items_by_category, total = order_overview(self.event)
|
||||
items_by_category, total = order_overview(self.event, subevent=self.form_data.get('subevent'))
|
||||
|
||||
for tup in items_by_category:
|
||||
if tup[0]:
|
||||
@@ -231,6 +239,18 @@ class OverviewReport(Report):
|
||||
story.append(table)
|
||||
return story
|
||||
|
||||
@property
|
||||
def export_form_fields(self) -> dict:
|
||||
d = OrderedDict()
|
||||
if self.event.has_subevents:
|
||||
d['subevent'] = forms.ModelChoiceField(
|
||||
self.event.subevents.all(),
|
||||
label=pgettext_lazy('subevent', 'Date'),
|
||||
required=False,
|
||||
empty_label=pgettext_lazy('subevent', 'All dates')
|
||||
)
|
||||
return d
|
||||
|
||||
|
||||
class OrderTaxListReport(Report):
|
||||
name = "ordertaxlist"
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
|
||||
from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
|
||||
|
||||
from pretix.base.forms import PlaceholderValidator
|
||||
from pretix.base.models import Order
|
||||
from pretix.base.models.event import SubEvent
|
||||
|
||||
|
||||
class MailForm(forms.Form):
|
||||
sendto = forms.MultipleChoiceField() # overridden later
|
||||
subject = forms.CharField(label=_("Subject"))
|
||||
message = forms.CharField(label=_("Message"))
|
||||
subevent = forms.ModelChoiceField(
|
||||
SubEvent.objects.none(),
|
||||
label=_('Only send to customers of'),
|
||||
required=False,
|
||||
empty_label=pgettext_lazy('subevent', 'All dates')
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
event = kwargs.pop('event')
|
||||
@@ -35,3 +42,7 @@ class MailForm(forms.Form):
|
||||
label=_("Send to"), widget=forms.CheckboxSelectMultiple,
|
||||
choices=choices
|
||||
)
|
||||
if event.has_subevents:
|
||||
self.fields['subevent'].queryset = event.subevents.all()
|
||||
else:
|
||||
del self.fields['subevent']
|
||||
|
||||
@@ -9,17 +9,20 @@
|
||||
{% for log in logs %}
|
||||
<li class="list-group-item logentry">
|
||||
<p class="meta">
|
||||
<span class="fa fa-clock-o"></span> {{ log.datetime|date:"SHORT_DATETIME_FORMAT" }}
|
||||
<span class="fa fa-clock-o fa-fw"></span> {{ log.datetime|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% if log.user %}
|
||||
<br/><span class="fa fa-user"></span> {{ log.user.get_full_name }}
|
||||
<br/><span class="fa fa-user fa-fw"></span> {{ log.user.get_full_name }}
|
||||
{% endif %}
|
||||
{% if log.display %}
|
||||
<br/><span class="fa fa-comment-o"></span> {{ log.display }}
|
||||
<br/><span class="fa fa-comment-o fa-fw"></span> {{ log.display }}
|
||||
{% endif %}
|
||||
<br/><span class="fa fa-shopping-cart"></span> {% trans "Sent to orders:" %}
|
||||
<br/><span class="fa fa-shopping-cart fa-fw"></span> {% trans "Sent to orders:" %}
|
||||
{% for status in log.parsed_data.sendto %}
|
||||
{{ status }}{% if forloop.revcounter > 1 %},{% endif %}
|
||||
{% endfor %}
|
||||
{% if log.pdata.subevent_obj %}
|
||||
<br/><span class="fa fa-calendar fa-fw"></span> {{ log.pdata.subevent_obj }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
{% for locale, value in log.pdata.locales.items %}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
<form class="form-horizontal" method="post" action="">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_field form.sendto layout='horizontal' %}
|
||||
{% if form.subevent %}
|
||||
{% bootstrap_field form.subevent layout='horizontal' %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.subject layout='horizontal' %}
|
||||
{% bootstrap_field form.message layout='horizontal' %}
|
||||
{% if request.method == "POST" %}
|
||||
|
||||
@@ -13,6 +13,7 @@ from django.views.generic import FormView, ListView
|
||||
|
||||
from pretix.base.i18n import LazyI18nString, language
|
||||
from pretix.base.models import InvoiceAddress, LogEntry, Order
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
@@ -43,6 +44,13 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
||||
'subject': LazyI18nString(logentry.parsed_data['subject']),
|
||||
'sendto': logentry.parsed_data['sendto'],
|
||||
}
|
||||
if logentry.parsed_data.get('subevent'):
|
||||
try:
|
||||
kwargs['initial']['subevent'] = self.request.event.subevents.get(
|
||||
pk=logentry.parsed_data['subevent']['id']
|
||||
)
|
||||
except SubEvent.DoesNotExist:
|
||||
pass
|
||||
except LogEntry.DoesNotExist:
|
||||
raise Http404(_('You supplied an invalid log entry ID'))
|
||||
return kwargs
|
||||
@@ -57,6 +65,8 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
||||
if 'overdue' in form.cleaned_data['sendto']:
|
||||
statusq |= Q(status=Order.STATUS_PENDING, expires__lt=now())
|
||||
orders = qs.filter(statusq)
|
||||
if form.cleaned_data.get('subevent'):
|
||||
orders = orders.filter(positions__subevent__in=(form.cleaned_data.get('subevent'),)).distinct()
|
||||
|
||||
tz = pytz.timezone(self.request.event.settings.timezone)
|
||||
|
||||
@@ -119,7 +129,7 @@ class SenderView(EventPermissionRequiredMixin, FormView):
|
||||
data={
|
||||
'subject': form.cleaned_data['subject'],
|
||||
'message': form.cleaned_data['message'],
|
||||
'recipient': o.email
|
||||
'recipient': o.email,
|
||||
}
|
||||
)
|
||||
except SendMailException:
|
||||
@@ -175,5 +185,10 @@ class EmailHistoryView(EventPermissionRequiredMixin, ListView):
|
||||
log.pdata['sendto'] = [
|
||||
status[s] for s in log.pdata['sendto']
|
||||
]
|
||||
if log.pdata.get('subevent'):
|
||||
try:
|
||||
log.pdata['subevent_obj'] = self.request.event.subevents.get(pk=log.pdata['subevent']['id'])
|
||||
except SubEvent.DoesNotExist:
|
||||
pass
|
||||
|
||||
return ctx
|
||||
|
||||
@@ -91,7 +91,7 @@ class Stripe(BasePaymentProvider):
|
||||
return template.render(ctx)
|
||||
|
||||
def order_can_retry(self, order):
|
||||
return self._is_still_available()
|
||||
return self._is_still_available(order=order)
|
||||
|
||||
def _charge_source(self, source, order):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user