mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
* 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
635 lines
26 KiB
Python
635 lines
26 KiB
Python
import mimetypes
|
|
import os
|
|
|
|
from django.contrib import messages
|
|
from django.db import transaction
|
|
from django.db.models import Sum
|
|
from django.http import FileResponse, Http404, JsonResponse
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.utils.functional import cached_property
|
|
from django.utils.timezone import now
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.views.generic import TemplateView, View
|
|
|
|
from pretix.base.models import CachedTicket, Invoice, Order, OrderPosition
|
|
from pretix.base.models.orders import InvoiceAddress, QuestionAnswer
|
|
from pretix.base.payment import PaymentException
|
|
from pretix.base.services.invoices import (
|
|
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
|
|
)
|
|
from pretix.base.services.orders import cancel_order
|
|
from pretix.base.services.tickets import (
|
|
get_cachedticket_for_order, get_cachedticket_for_position,
|
|
)
|
|
from pretix.base.signals import register_ticket_outputs
|
|
from pretix.multidomain.urlreverse import eventreverse
|
|
from pretix.presale.forms.checkout import InvoiceAddressForm
|
|
from pretix.presale.views import CartMixin, EventViewMixin
|
|
from pretix.presale.views.async import AsyncAction
|
|
from pretix.presale.views.questions import QuestionsViewMixin
|
|
|
|
|
|
class OrderDetailMixin:
|
|
@cached_property
|
|
def order(self):
|
|
order = self.request.event.orders.filter(code=self.kwargs['order']).select_related('event').first()
|
|
if order:
|
|
if order.secret.lower() == self.kwargs['secret'].lower():
|
|
return order
|
|
else:
|
|
return None
|
|
else:
|
|
# Do a comparison as well to harden timing attacks
|
|
if 'abcdefghijklmnopq'.lower() == self.kwargs['secret'].lower():
|
|
return None
|
|
else:
|
|
return None
|
|
|
|
@cached_property
|
|
def payment_provider(self):
|
|
return self.request.event.get_payment_providers().get(self.order.payment_provider)
|
|
|
|
def get_order_url(self):
|
|
return eventreverse(self.request.event, 'presale:event.order', kwargs={
|
|
'order': self.order.code,
|
|
'secret': self.order.secret
|
|
})
|
|
|
|
|
|
class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TemplateView):
|
|
template_name = "pretixpresale/event/order.html"
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
self.kwargs = kwargs
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
@cached_property
|
|
def download_buttons(self):
|
|
buttons = []
|
|
responses = register_ticket_outputs.send(self.request.event)
|
|
for receiver, response in responses:
|
|
provider = response(self.request.event)
|
|
if not provider.is_enabled:
|
|
continue
|
|
buttons.append({
|
|
'text': provider.download_button_text or 'Download',
|
|
'identifier': provider.identifier,
|
|
})
|
|
return buttons
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['order'] = self.order
|
|
|
|
if self.request.event.settings.ticket_download_date:
|
|
ctx['ticket_download_date'] = self.order.ticket_download_date
|
|
ctx['can_download'] = (
|
|
self.request.event.settings.ticket_download
|
|
and (
|
|
self.request.event.settings.ticket_download_date is None
|
|
or now() > self.order.ticket_download_date
|
|
) and self.order.status == Order.STATUS_PAID
|
|
)
|
|
ctx['download_buttons'] = self.download_buttons
|
|
ctx['cart'] = self.get_cart(
|
|
answers=True, downloads=ctx['can_download'],
|
|
queryset=self.order.positions.all(),
|
|
payment_fee=self.order.payment_fee, payment_fee_tax_rate=self.order.payment_fee_tax_rate
|
|
)
|
|
ctx['invoices'] = list(self.order.invoices.all())
|
|
ctx['can_generate_invoice'] = invoice_qualified(self.order) and (
|
|
self.request.event.settings.invoice_generate == 'user'
|
|
)
|
|
|
|
if self.order.status == Order.STATUS_PENDING:
|
|
ctx['payment'] = self.payment_provider.order_pending_render(self.request, self.order)
|
|
ctx['can_retry'] = (
|
|
self.payment_provider.order_can_retry(self.order)
|
|
and self.payment_provider.is_enabled
|
|
and self.order._can_be_paid()
|
|
)
|
|
|
|
ctx['can_change_method'] = False
|
|
for provider in self.request.event.get_payment_providers().values():
|
|
if (provider.identifier != self.order.payment_provider and provider.is_enabled
|
|
and provider.order_change_allowed(self.order)):
|
|
ctx['can_change_method'] = True
|
|
break
|
|
|
|
elif self.order.status == Order.STATUS_PAID:
|
|
ctx['payment'] = self.payment_provider.order_paid_render(self.request, self.order)
|
|
ctx['can_retry'] = False
|
|
return ctx
|
|
|
|
|
|
class OrderPaymentStart(EventViewMixin, OrderDetailMixin, TemplateView):
|
|
"""
|
|
This is used if a payment is retried or the payment method is changed. It shows the payment
|
|
provider's form that asks for payment details (e.g. CC number).
|
|
"""
|
|
template_name = "pretixpresale/event/order_pay.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
if (self.order.status not in (Order.STATUS_PENDING, Order.STATUS_EXPIRED)
|
|
or not self.payment_provider.order_can_retry(self.order)
|
|
or not self.payment_provider.is_enabled):
|
|
messages.error(request, _('The payment for this order cannot be continued.'))
|
|
return redirect(self.get_order_url())
|
|
|
|
term_last = self.order.payment_term_last
|
|
if term_last and now() > term_last:
|
|
messages.error(request, _('The payment is too late to be accepted.'))
|
|
return redirect(self.get_order_url())
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
resp = self.payment_provider.order_prepare(request, self.order)
|
|
if 'payment_change_{}'.format(self.order.pk) in request.session:
|
|
del request.session['payment_change_{}'.format(self.order.pk)]
|
|
if isinstance(resp, str):
|
|
return redirect(resp)
|
|
elif resp is True:
|
|
return redirect(self.get_confirm_url())
|
|
else:
|
|
return self.get(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['order'] = self.order
|
|
ctx['form'] = self.form
|
|
return ctx
|
|
|
|
@cached_property
|
|
def form(self):
|
|
return self.payment_provider.payment_form_render(self.request)
|
|
|
|
def get_confirm_url(self):
|
|
return eventreverse(self.request.event, 'presale:event.order.pay.confirm', kwargs={
|
|
'order': self.order.code,
|
|
'secret': self.order.secret
|
|
})
|
|
|
|
|
|
class OrderPaymentConfirm(EventViewMixin, OrderDetailMixin, TemplateView):
|
|
"""
|
|
This is used if a payment is retried or the payment method is changed. It is shown after the
|
|
payment details have been entered and allows the user to confirm and review the details. On
|
|
submitting this view, the payment is performed.
|
|
"""
|
|
template_name = "pretixpresale/event/order_pay_confirm.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
can_do = self.payment_provider.order_can_retry(self.order) or 'payment_change_{}'.format(self.order.pk) in request.session
|
|
if not can_do or not self.payment_provider.is_enabled:
|
|
messages.error(request, _('The payment for this order cannot be continued.'))
|
|
return redirect(self.get_order_url())
|
|
if (not self.payment_provider.payment_is_valid_session(request)
|
|
or not self.payment_provider.is_enabled):
|
|
messages.error(request, _('The payment information you entered was incomplete.'))
|
|
return redirect(self.get_payment_url())
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
try:
|
|
resp = self.payment_provider.payment_perform(request, self.order)
|
|
except PaymentException as e:
|
|
messages.error(request, str(e))
|
|
return redirect(self.get_order_url())
|
|
if 'payment_change_{}'.format(self.order.pk) in request.session:
|
|
del request.session['payment_change_{}'.format(self.order.pk)]
|
|
return redirect(resp or self.get_order_url())
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['order'] = self.order
|
|
ctx['payment'] = self.payment_provider.checkout_confirm_render(self.request)
|
|
ctx['payment_provider'] = self.payment_provider
|
|
return ctx
|
|
|
|
def get_payment_url(self):
|
|
return eventreverse(self.request.event, 'presale:event.order.pay', kwargs={
|
|
'order': self.order.code,
|
|
'secret': self.order.secret
|
|
})
|
|
|
|
|
|
class OrderPaymentComplete(EventViewMixin, OrderDetailMixin, View):
|
|
"""
|
|
This is used for the first try of a payment. This means the user just entered payment
|
|
details and confirmed them during the order process and we don't need to show them again,
|
|
we just need to perform the payment.
|
|
"""
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
if (not self.payment_provider.payment_is_valid_session(request) or
|
|
not self.payment_provider.is_enabled):
|
|
messages.error(request, _('The payment information you entered was incomplete.'))
|
|
return redirect(self.get_payment_url())
|
|
|
|
term_last = self.order.payment_term_last
|
|
if term_last and now() > term_last:
|
|
messages.error(request, _('The payment is too late to be accepted.'))
|
|
return redirect(self.get_order_url())
|
|
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
try:
|
|
resp = self.payment_provider.payment_perform(request, self.order)
|
|
except PaymentException as e:
|
|
messages.error(request, str(e))
|
|
return redirect(self.get_order_url())
|
|
|
|
if self.order.status == Order.STATUS_PAID:
|
|
return redirect(resp or self.get_order_url() + '?paid=yes')
|
|
else:
|
|
return redirect(resp or self.get_order_url() + '?thanks=yes')
|
|
|
|
def get_payment_url(self):
|
|
return eventreverse(self.request.event, 'presale:event.order.pay', kwargs={
|
|
'order': self.order.code,
|
|
'secret': self.order.secret
|
|
})
|
|
|
|
|
|
class OrderPayChangeMethod(EventViewMixin, OrderDetailMixin, TemplateView):
|
|
template_name = 'pretixpresale/event/order_pay_change.html'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
if self.order.status not in (Order.STATUS_PENDING, Order.STATUS_EXPIRED):
|
|
messages.error(request, _('The payment method for this order cannot be changed.'))
|
|
return redirect(self.get_order_url())
|
|
|
|
term_last = self.order.payment_term_last
|
|
if term_last and now() > term_last:
|
|
messages.error(request, _('The payment is too late to be accepted.'))
|
|
return redirect(self.get_order_url())
|
|
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_payment_url(self):
|
|
return eventreverse(self.request.event, 'presale:event.order.pay', kwargs={
|
|
'order': self.order.code,
|
|
'secret': self.order.secret
|
|
})
|
|
|
|
@cached_property
|
|
def _total_order_value(self):
|
|
return self.order.positions.aggregate(sum=Sum('price'))['sum']
|
|
|
|
@cached_property
|
|
def provider_forms(self):
|
|
providers = []
|
|
for provider in self.request.event.get_payment_providers().values():
|
|
if provider.identifier == self.order.payment_provider:
|
|
continue
|
|
if not provider.is_enabled or not provider.order_change_allowed(self.order):
|
|
continue
|
|
fee = provider.calculate_fee(self._total_order_value)
|
|
providers.append({
|
|
'provider': provider,
|
|
'fee': fee,
|
|
'fee_diff': fee - self.order.payment_fee,
|
|
'fee_diff_abs': abs(fee - self.order.payment_fee),
|
|
'total': abs(self._total_order_value + fee),
|
|
'form': provider.payment_form_render(self.request)
|
|
})
|
|
return providers
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
self.request = request
|
|
for p in self.provider_forms:
|
|
if p['provider'].identifier == request.POST.get('payment', ''):
|
|
request.session['payment'] = p['provider'].identifier
|
|
request.session['payment_change_{}'.format(self.order.pk)] = '1'
|
|
|
|
new_fee = p['provider'].calculate_fee(self._total_order_value)
|
|
self.order.payment_provider = p['provider'].identifier
|
|
self.order.payment_fee = new_fee
|
|
self.order.total = self._total_order_value + new_fee
|
|
self.order._calculate_tax()
|
|
|
|
resp = p['provider'].order_prepare(request, self.order)
|
|
if resp:
|
|
with transaction.atomic():
|
|
self.order.log_action('pretix.event.order.payment.changed', {
|
|
'old_fee': self.order.payment_fee,
|
|
'new_fee': new_fee,
|
|
'old_provider': self.order.payment_provider,
|
|
'new_provider': p['provider'].identifier
|
|
})
|
|
self.order.save()
|
|
|
|
i = self.order.invoices.filter(is_cancellation=False).last()
|
|
if i:
|
|
generate_cancellation(i)
|
|
generate_invoice(self.order)
|
|
if isinstance(resp, str):
|
|
return redirect(resp)
|
|
elif resp is True:
|
|
return redirect(self.get_confirm_url())
|
|
else:
|
|
return self.get(request, *args, **kwargs)
|
|
messages.error(self.request, _("Please select a payment method."))
|
|
return self.get(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['order'] = self.order
|
|
ctx['providers'] = self.provider_forms
|
|
return ctx
|
|
|
|
def get_confirm_url(self):
|
|
return eventreverse(self.request.event, 'presale:event.order.pay.confirm', kwargs={
|
|
'order': self.order.code,
|
|
'secret': self.order.secret
|
|
})
|
|
|
|
|
|
class OrderInvoiceCreate(EventViewMixin, OrderDetailMixin, View):
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
if self.request.event.settings.get('invoice_generate') != 'user' or not invoice_qualified(self.order):
|
|
messages.error(self.request, _('You cannot generate an invoice for this order.'))
|
|
elif self.order.invoices.exists():
|
|
messages.error(self.request, _('An invoice for this order already exists.'))
|
|
else:
|
|
i = generate_invoice(self.order)
|
|
self.order.log_action('pretix.event.order.invoice.generated', data={
|
|
'invoice': i.pk
|
|
})
|
|
messages.success(self.request, _('The invoice has been generated.'))
|
|
return redirect(self.get_order_url())
|
|
|
|
|
|
class OrderModify(EventViewMixin, OrderDetailMixin, QuestionsViewMixin, TemplateView):
|
|
template_name = "pretixpresale/event/order_modify.html"
|
|
|
|
def _positions_for_questions(self):
|
|
return self.positions
|
|
|
|
@cached_property
|
|
def positions(self):
|
|
return list(self.order.positions.select_related(
|
|
'item', 'variation'
|
|
).prefetch_related(
|
|
'variation', 'item__questions', 'answers'
|
|
))
|
|
|
|
@cached_property
|
|
def invoice_address(self):
|
|
try:
|
|
return self.order.invoice_address
|
|
except InvoiceAddress.DoesNotExist:
|
|
return InvoiceAddress(order=self.order)
|
|
|
|
@cached_property
|
|
def invoice_form(self):
|
|
return InvoiceAddressForm(data=self.request.POST if self.request.method == "POST" else None,
|
|
event=self.request.event,
|
|
instance=self.invoice_address)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
failed = not self.save() or not self.invoice_form.is_valid()
|
|
if failed:
|
|
messages.error(self.request,
|
|
_("We had difficulties processing your input. Please review the errors below."))
|
|
return self.get(request, *args, **kwargs)
|
|
self.invoice_form.save()
|
|
self.order.log_action('pretix.event.order.modified')
|
|
if self.invoice_form.has_changed():
|
|
success_message = ('Your invoice address has been updated. Please contact us if you need us '
|
|
'to regenerate your invoice.')
|
|
messages.success(self.request, _(success_message))
|
|
|
|
CachedTicket.objects.filter(order_position__order=self.order).delete()
|
|
return redirect(self.get_order_url())
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
self.kwargs = kwargs
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
if not self.order.can_modify_answers:
|
|
messages.error(request, _('You cannot modify this order'))
|
|
return redirect(self.get_order_url())
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['order'] = self.order
|
|
ctx['formgroups'] = self.formdict.items()
|
|
ctx['invoice_form'] = self.invoice_form
|
|
return ctx
|
|
|
|
|
|
class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView):
|
|
template_name = "pretixpresale/event/order_cancel.html"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.request = request
|
|
self.kwargs = kwargs
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
if self.order.status != Order.STATUS_PENDING or not self.order.can_user_cancel:
|
|
messages.error(request, _('You cannot cancel this order.'))
|
|
return redirect(self.get_order_url())
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['order'] = self.order
|
|
return ctx
|
|
|
|
|
|
class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
|
|
task = cancel_order
|
|
known_errortypes = ['OrderError']
|
|
|
|
def get_success_url(self, value):
|
|
return self.get_order_url()
|
|
|
|
def get_error_url(self):
|
|
return self.get_order_url()
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
if not self.order.can_user_cancel:
|
|
messages.error(request, _('You cannot cancel this order.'))
|
|
return redirect(self.get_order_url())
|
|
return self.do(self.order.pk)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['order'] = self.order
|
|
return ctx
|
|
|
|
def get_success_message(self, value):
|
|
return _('The order has been canceled.')
|
|
|
|
|
|
class AnswerDownload(EventViewMixin, OrderDetailMixin, View):
|
|
def get(self, request, *args, **kwargs):
|
|
answid = kwargs.get('answer')
|
|
answer = get_object_or_404(QuestionAnswer, orderposition__order=self.order, id=answid)
|
|
if not answer.file:
|
|
return Http404()
|
|
|
|
ftype, _ = mimetypes.guess_type(answer.file.name)
|
|
resp = FileResponse(answer.file, content_type=ftype or 'application/binary')
|
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}"'.format(
|
|
self.request.event.slug.upper(), self.order.code,
|
|
answer.orderposition.positionid,
|
|
os.path.basename(answer.file.name).split('.', 1)[1]
|
|
)
|
|
return resp
|
|
|
|
|
|
class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
|
|
|
def get_self_url(self):
|
|
return eventreverse(self.request.event,
|
|
'presale:event.order.download' if 'position' in self.kwargs
|
|
else 'presale:event.order.download.combined',
|
|
kwargs=self.kwargs)
|
|
|
|
@cached_property
|
|
def output(self):
|
|
responses = register_ticket_outputs.send(self.request.event)
|
|
for receiver, response in responses:
|
|
provider = response(self.request.event)
|
|
if provider.identifier == self.kwargs.get('output'):
|
|
return provider
|
|
|
|
@cached_property
|
|
def order_position(self):
|
|
try:
|
|
return self.order.positions.get(pk=self.kwargs.get('position'))
|
|
except OrderPosition.DoesNotExist:
|
|
return None
|
|
|
|
def error(self, msg):
|
|
messages.error(self.request, msg)
|
|
if "ajax" in self.request.POST or "ajax" in self.request.GET:
|
|
return JsonResponse({
|
|
'ready': True,
|
|
'success': False,
|
|
'redirect': self.get_order_url(),
|
|
'message': msg,
|
|
})
|
|
return redirect(self.get_order_url())
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if not self.output or not self.output.is_enabled:
|
|
return self.error(_('You requested an invalid ticket output type.'))
|
|
if not self.order or ('position' in kwargs and not self.order_position):
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
if self.order.status != Order.STATUS_PAID:
|
|
return self.error(_('Order is not paid.'))
|
|
if (not self.request.event.settings.ticket_download
|
|
or (self.request.event.settings.ticket_download_date is not None
|
|
and now() < self.order.ticket_download_date)):
|
|
return self.error(_('Ticket download is not (yet) enabled.'))
|
|
if 'position' in kwargs and (self.order_position.addon_to and not self.request.event.settings.ticket_download_addons):
|
|
return self.error(_('Ticket download is not enabled for add-on products.'))
|
|
if 'position' in kwargs and (not self.order_position.item.admission and not self.request.event.settings.ticket_download_nonadm):
|
|
return self.error(_('Ticket download is not enabled for non-admission products.'))
|
|
|
|
if 'position' in kwargs:
|
|
return self._download_position()
|
|
else:
|
|
return self._download_order()
|
|
|
|
def _download_order(self):
|
|
ct = get_cachedticket_for_order(self.order, self.output.identifier)
|
|
|
|
if 'ajax' in self.request.GET:
|
|
return JsonResponse({
|
|
'ready': bool(ct and ct.file),
|
|
'success': False,
|
|
'redirect': self.get_self_url()
|
|
})
|
|
elif not ct.file:
|
|
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
|
else:
|
|
resp = FileResponse(ct.file.file, content_type=ct.type)
|
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}{}"'.format(
|
|
self.request.event.slug.upper(), self.order.code, self.output.identifier, ct.extension
|
|
)
|
|
return resp
|
|
|
|
def _download_position(self):
|
|
ct = get_cachedticket_for_position(self.order_position, self.output.identifier)
|
|
|
|
if 'ajax' in self.request.GET:
|
|
return JsonResponse({
|
|
'ready': bool(ct and ct.file),
|
|
'success': False,
|
|
'redirect': self.get_self_url()
|
|
})
|
|
elif not ct.file:
|
|
return render(self.request, "pretixbase/cachedfiles/pending.html", {})
|
|
else:
|
|
resp = FileResponse(ct.file.file, content_type=ct.type)
|
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}{}"'.format(
|
|
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
|
|
self.output.identifier, ct.extension
|
|
)
|
|
return resp
|
|
|
|
|
|
class InvoiceDownload(EventViewMixin, OrderDetailMixin, View):
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if not self.order:
|
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
|
|
|
try:
|
|
invoice = Invoice.objects.get(
|
|
event=self.request.event,
|
|
order=self.order,
|
|
id=self.kwargs['invoice']
|
|
)
|
|
except Invoice.DoesNotExist:
|
|
raise Http404(_('This invoice has not been found'))
|
|
|
|
if not invoice.file:
|
|
invoice_pdf(invoice.pk)
|
|
invoice = Invoice.objects.get(pk=invoice.pk)
|
|
|
|
if not invoice.file:
|
|
# This happens if we have celery installed and the file will be generated in the background
|
|
messages.warning(request, _('The invoice file has not yet been generated, we will generate it for you '
|
|
'now. Please try again in a few seconds.'))
|
|
return redirect(self.get_order_url())
|
|
|
|
resp = FileResponse(invoice.file.file, content_type='application/pdf')
|
|
resp['Content-Disposition'] = 'attachment; filename="{}.pdf"'.format(invoice.number)
|
|
return resp
|