import dateutil.parser from django.contrib import messages from django.db import transaction from django.db.models import Max, OuterRef, Subquery from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect from django.urls import reverse from django.utils.functional import cached_property from django.utils.timezone import is_aware, make_aware, now from django.utils.translation import ugettext_lazy as _ from django.views.generic import DeleteView, ListView from pytz import UTC from pretix.base.models import Checkin, Order, OrderPosition from pretix.base.models.checkin import CheckinList from pretix.control.forms.checkin import CheckinListForm from pretix.control.forms.filter import CheckInFilterForm from pretix.control.permissions import EventPermissionRequiredMixin from pretix.control.views import CreateView, PaginationMixin, UpdateView class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, ListView): model = Checkin context_object_name = 'entries' template_name = 'pretixcontrol/checkin/index.html' permission = 'can_view_orders' def get_queryset(self, filter=True): cqs = Checkin.objects.filter( position_id=OuterRef('pk'), list_id=self.list.pk ).order_by().values('position_id').annotate( m=Max('datetime') ).values('m') qs = OrderPosition.objects.filter( order__event=self.request.event, order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING] if self.list.include_pending else [Order.STATUS_PAID], subevent=self.list.subevent ).annotate( last_checked_in=Subquery(cqs) ).select_related('item', 'variation', 'order', 'addon_to') if not self.list.all_products: qs = qs.filter(item__in=self.list.limit_products.values_list('id', flat=True)) if filter and self.filter_form.is_valid(): qs = self.filter_form.filter_qs(qs) return qs @cached_property def filter_form(self): return CheckInFilterForm( data=self.request.GET, event=self.request.event, list=self.list ) def dispatch(self, request, *args, **kwargs): self.list = get_object_or_404(self.request.event.checkin_lists.all(), pk=kwargs.get("list")) return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['checkinlist'] = self.list ctx['filter_form'] = self.filter_form for e in ctx['entries']: if e.last_checked_in: if isinstance(e.last_checked_in, str): # Apparently only happens on SQLite e.last_checked_in_aware = make_aware(dateutil.parser.parse(e.last_checked_in), UTC) elif not is_aware(e.last_checked_in): # Apparently only happens on MySQL e.last_checked_in_aware = make_aware(e.last_checked_in, UTC) else: # This would be correct, so guess on which database it works… Yes, it's PostgreSQL. e.last_checked_in_aware = e.last_checked_in return ctx def post(self, request, *args, **kwargs): if "can_change_orders" not in request.eventpermset: messages.error(request, _('You do not have permission to perform this action.')) return redirect(reverse('control:event.orders.checkins', kwargs={ 'event': self.request.event.slug, 'organizer': self.request.event.organizer.slug }) + '?' + request.GET.urlencode()) positions = self.get_queryset(filter=False).filter( pk__in=request.POST.getlist('checkin') ) if request.POST.get('revert') == 'true': for op in positions: if op.order.status == Order.STATUS_PAID or (self.list.include_pending and op.order.status == Order.STATUS_PENDING): Checkin.objects.filter(position=op, list=self.list).delete() op.order.log_action('pretix.event.checkin.reverted', data={ 'position': op.id, 'positionid': op.positionid, 'list': self.list.pk, 'web': True }, user=request.user) op.order.touch() messages.success(request, _('The selected check-ins have been reverted.')) else: for op in positions: created = False if op.order.status == Order.STATUS_PAID or (self.list.include_pending and op.order.status == Order.STATUS_PENDING): ci, created = Checkin.objects.get_or_create(position=op, list=self.list, defaults={ 'datetime': now(), }) op.order.log_action('pretix.event.checkin', data={ 'position': op.id, 'positionid': op.positionid, 'first': created, 'forced': False, 'datetime': now(), 'list': self.list.pk, 'web': True }, user=request.user) messages.success(request, _('The selected tickets have been marked as checked in.')) return redirect(reverse('control:event.orders.checkinlists.show', kwargs={ 'event': self.request.event.slug, 'organizer': self.request.event.organizer.slug, 'list': self.list.pk }) + '?' + request.GET.urlencode()) class CheckinListList(EventPermissionRequiredMixin, PaginationMixin, ListView): model = CheckinList context_object_name = 'checkinlists' permission = 'can_view_orders' template_name = 'pretixcontrol/checkin/lists.html' def get_queryset(self): qs = self.request.event.checkin_lists.select_related('subevent').prefetch_related("limit_products") if self.request.GET.get("subevent", "") != "": s = self.request.GET.get("subevent", "") qs = qs.filter(subevent_id=s) return qs def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) clists = list(ctx['checkinlists']) for cl in clists: if cl.subevent: cl.subevent.event = self.request.event # re-use same event object to make sure settings are cached ctx['checkinlists'] = clists return ctx class CheckinListCreate(EventPermissionRequiredMixin, CreateView): model = CheckinList form_class = CheckinListForm template_name = 'pretixcontrol/checkin/list_edit.html' permission = 'can_change_event_settings' context_object_name = 'checkinlist' def get_success_url(self) -> str: return reverse('control:event.orders.checkinlists', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, }) @transaction.atomic def form_valid(self, form): form.instance.event = self.request.event messages.success(self.request, _('The new check-in list has been created.')) ret = super().form_valid(form) form.instance.log_action('pretix.event.checkinlist.added', user=self.request.user, data=dict(form.cleaned_data)) return ret def form_invalid(self, form): messages.error(self.request, _('We could not save your changes. See below for details.')) return super().form_invalid(form) class CheckinListUpdate(EventPermissionRequiredMixin, UpdateView): model = CheckinList form_class = CheckinListForm template_name = 'pretixcontrol/checkin/list_edit.html' permission = 'can_change_event_settings' context_object_name = 'checkinlist' def get_object(self, queryset=None) -> CheckinList: try: return self.request.event.checkin_lists.get( id=self.kwargs['list'] ) except CheckinList.DoesNotExist: raise Http404(_("The requested list does not exist.")) @transaction.atomic def form_valid(self, form): messages.success(self.request, _('Your changes have been saved.')) if form.has_changed(): self.object.log_action( 'pretix.event.checkinlist.changed', user=self.request.user, data={ k: form.cleaned_data.get(k) for k in form.changed_data } ) return super().form_valid(form) def get_success_url(self) -> str: return reverse('control:event.orders.checkinlists.show', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, 'list': self.object.pk }) def form_invalid(self, form): messages.error(self.request, _('We could not save your changes. See below for details.')) return super().form_invalid(form) class CheckinListDelete(EventPermissionRequiredMixin, DeleteView): model = CheckinList template_name = 'pretixcontrol/checkin/list_delete.html' permission = 'can_change_event_settings' context_object_name = 'checkinlist' def get_object(self, queryset=None) -> CheckinList: try: return self.request.event.checkin_lists.get( id=self.kwargs['list'] ) except CheckinList.DoesNotExist: raise Http404(_("The requested list does not exist.")) @transaction.atomic def delete(self, request, *args, **kwargs): self.object = self.get_object() success_url = self.get_success_url() self.object.checkins.all().delete() self.object.log_action(action='pretix.event.checkinlists.deleted', user=request.user) self.object.delete() messages.success(self.request, _('The selected list has been deleted.')) return HttpResponseRedirect(success_url) def get_success_url(self) -> str: return reverse('control:event.orders.checkinlists', kwargs={ 'organizer': self.request.event.organizer.slug, 'event': self.request.event.slug, })