forked from CGM_Public/pretix_original
Overhaul of our check-in features (#1647)
This commit is contained in:
@@ -30,7 +30,15 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
def get_queryset(self, filter=True):
|
||||
cqs = Checkin.objects.filter(
|
||||
position_id=OuterRef('pk'),
|
||||
list_id=self.list.pk
|
||||
list_id=self.list.pk,
|
||||
type=Checkin.TYPE_ENTRY
|
||||
).order_by().values('position_id').annotate(
|
||||
m=Max('datetime')
|
||||
).values('m')
|
||||
cqs_exit = Checkin.objects.filter(
|
||||
position_id=OuterRef('pk'),
|
||||
list_id=self.list.pk,
|
||||
type=Checkin.TYPE_EXIT
|
||||
).order_by().values('position_id').annotate(
|
||||
m=Max('datetime')
|
||||
).values('m')
|
||||
@@ -38,13 +46,17 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
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),
|
||||
last_entry=Subquery(cqs),
|
||||
last_exit=Subquery(cqs_exit),
|
||||
auto_checked_in=Exists(
|
||||
Checkin.objects.filter(position_id=OuterRef('pk'), list_id=self.list.pk, auto_checked_in=True)
|
||||
)
|
||||
).select_related('item', 'variation', 'order', 'addon_to')
|
||||
if self.list.subevent:
|
||||
qs = qs.filter(
|
||||
subevent=self.list.subevent
|
||||
)
|
||||
|
||||
if not self.list.all_products:
|
||||
qs = qs.filter(item__in=self.list.limit_products.values_list('id', flat=True))
|
||||
@@ -69,19 +81,35 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['checkinlist'] = self.list
|
||||
ctx['seats'] = self.list.subevent.seating_plan if self.list.subevent else self.request.event.seating_plan
|
||||
if self.request.event.has_subevents:
|
||||
ctx['seats'] = (
|
||||
self.list.subevent.seating_plan_id if self.list.subevent
|
||||
else self.request.event.subevents.filter(seating_plan__isnull=False).exists()
|
||||
)
|
||||
else:
|
||||
ctx['seats'] = self.request.event.seating_plan_id
|
||||
ctx['filter_form'] = self.filter_form
|
||||
for e in ctx['entries']:
|
||||
if e.last_checked_in:
|
||||
if isinstance(e.last_checked_in, str):
|
||||
if e.last_entry:
|
||||
if isinstance(e.last_entry, 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):
|
||||
e.last_entry_aware = make_aware(dateutil.parser.parse(e.last_entry), UTC)
|
||||
elif not is_aware(e.last_entry):
|
||||
# Apparently only happens on MySQL
|
||||
e.last_checked_in_aware = make_aware(e.last_checked_in, UTC)
|
||||
e.last_entry_aware = make_aware(e.last_entry, 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
|
||||
e.last_entry_aware = e.last_entry
|
||||
if e.last_exit:
|
||||
if isinstance(e.last_exit, str):
|
||||
# Apparently only happens on SQLite
|
||||
e.last_exit_aware = make_aware(dateutil.parser.parse(e.last_exit), UTC)
|
||||
elif not is_aware(e.last_exit):
|
||||
# Apparently only happens on MySQL
|
||||
e.last_exit_aware = make_aware(e.last_exit, UTC)
|
||||
else:
|
||||
# This would be correct, so guess on which database it works… Yes, it's PostgreSQL.
|
||||
e.last_exit_aware = e.last_exit
|
||||
return ctx
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@@ -111,17 +139,22 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
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(),
|
||||
})
|
||||
t = Checkin.TYPE_EXIT if request.POST.get('checkout') == 'true' else Checkin.TYPE_ENTRY
|
||||
if self.list.allow_multiple_entries or t != Checkin.TYPE_ENTRY:
|
||||
ci = Checkin.objects.create(position=op, list=self.list, datetime=now(), type=t)
|
||||
created = True
|
||||
else:
|
||||
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(),
|
||||
'type': t,
|
||||
'list': self.list.pk,
|
||||
'web': True
|
||||
}, user=request.user)
|
||||
@@ -177,6 +210,11 @@ class CheckinListCreate(EventPermissionRequiredMixin, CreateView):
|
||||
permission = 'can_change_event_settings'
|
||||
context_object_name = 'checkinlist'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
r = super().dispatch(request, *args, **kwargs)
|
||||
r['Content-Security-Policy'] = 'script-src \'unsafe-eval\''
|
||||
return r
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.orders.checkinlists', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
@@ -204,6 +242,11 @@ class CheckinListUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
permission = 'can_change_event_settings'
|
||||
context_object_name = 'checkinlist'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
r = super().dispatch(request, *args, **kwargs)
|
||||
r['Content-Security-Policy'] = 'script-src \'unsafe-eval\''
|
||||
return r
|
||||
|
||||
def get_object(self, queryset=None) -> CheckinList:
|
||||
try:
|
||||
return self.request.event.checkin_lists.get(
|
||||
|
||||
@@ -268,8 +268,8 @@ def checkin_widget(sender, subevent=None, lazy=False, **kwargs):
|
||||
for cl in qs:
|
||||
widgets.append({
|
||||
'content': None if lazy else NUM_WIDGET.format(
|
||||
num='{}/{}'.format(cl.checkin_count, cl.position_count),
|
||||
text=_('Checked in – {list}').format(list=escape(cl.name))
|
||||
num='{}/{}'.format(cl.inside_count, cl.position_count),
|
||||
text=_('Present – {list}').format(list=escape(cl.name))
|
||||
),
|
||||
'lazy': 'checkin-{}'.format(cl.pk),
|
||||
'display_size': 'small',
|
||||
|
||||
@@ -38,9 +38,9 @@ from pretix.base.decimal import round_decimal
|
||||
from pretix.base.email import get_email_context
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedFile, CachedTicket, Invoice, InvoiceAddress,
|
||||
Item, ItemVariation, LogEntry, Order, QuestionAnswer, Quota,
|
||||
generate_position_secret, generate_secret,
|
||||
CachedCombinedTicket, CachedFile, CachedTicket, Checkin, Invoice,
|
||||
InvoiceAddress, Item, ItemVariation, LogEntry, Order, QuestionAnswer,
|
||||
Quota, generate_position_secret, generate_secret,
|
||||
)
|
||||
from pretix.base.models.orders import (
|
||||
CancellationRequest, OrderFee, OrderPayment, OrderPosition, OrderRefund,
|
||||
@@ -252,7 +252,7 @@ class OrderDetail(OrderView):
|
||||
).prefetch_related(
|
||||
'item__questions', 'issued_gift_cards',
|
||||
Prefetch('answers', queryset=QuestionAnswer.objects.prefetch_related('options').select_related('question')),
|
||||
'checkins', 'checkins__list'
|
||||
Prefetch('checkins', queryset=Checkin.objects.select_related('list').order_by('datetime')),
|
||||
).order_by('positionid')
|
||||
|
||||
positions = []
|
||||
|
||||
@@ -25,7 +25,7 @@ from pretix.base.models.items import (
|
||||
)
|
||||
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.control.forms.checkin import CheckinListForm
|
||||
from pretix.control.forms.checkin import SimpleCheckinListForm
|
||||
from pretix.control.forms.filter import SubEventFilterForm
|
||||
from pretix.control.forms.item import QuotaForm
|
||||
from pretix.control.forms.subevents import (
|
||||
@@ -192,11 +192,11 @@ class SubEventEditorMixin(MetaDataEditorMixin):
|
||||
'include_pending': False,
|
||||
}
|
||||
]
|
||||
extra = 1
|
||||
extra = 0
|
||||
|
||||
formsetclass = inlineformset_factory(
|
||||
SubEvent, CheckinList,
|
||||
form=CheckinListForm, formset=CheckinListFormSet,
|
||||
form=SimpleCheckinListForm, formset=CheckinListFormSet,
|
||||
can_order=False, can_delete=True, extra=extra,
|
||||
)
|
||||
if self.object:
|
||||
|
||||
@@ -13,8 +13,8 @@ from django.utils.timezone import make_aware
|
||||
from django.utils.translation import gettext as _, pgettext
|
||||
|
||||
from pretix.base.models import (
|
||||
EventMetaProperty, EventMetaValue, ItemMetaProperty, ItemMetaValue, Order,
|
||||
Organizer, User, Voucher,
|
||||
EventMetaProperty, EventMetaValue, ItemMetaProperty, ItemMetaValue,
|
||||
ItemVariation, Order, Organizer, User, Voucher,
|
||||
)
|
||||
from pretix.control.forms.event import EventWizardCopyForm
|
||||
from pretix.control.permissions import event_permission_required
|
||||
@@ -322,6 +322,68 @@ def quotas_select2(request, **kwargs):
|
||||
return JsonResponse(doc)
|
||||
|
||||
|
||||
@event_permission_required(None)
|
||||
def items_select2(request, **kwargs):
|
||||
query = request.GET.get('query', '')
|
||||
try:
|
||||
page = int(request.GET.get('page', '1'))
|
||||
except ValueError:
|
||||
page = 1
|
||||
|
||||
qs = request.event.items.filter(
|
||||
name__icontains=i18ncomp(query)
|
||||
).order_by('position')
|
||||
|
||||
total = qs.count()
|
||||
pagesize = 20
|
||||
offset = (page - 1) * pagesize
|
||||
doc = {
|
||||
'results': [
|
||||
{
|
||||
'id': e.pk,
|
||||
'text': str(e),
|
||||
}
|
||||
for e in qs[offset:offset + pagesize]
|
||||
],
|
||||
'pagination': {
|
||||
"more": total >= (offset + pagesize)
|
||||
}
|
||||
}
|
||||
return JsonResponse(doc)
|
||||
|
||||
|
||||
@event_permission_required(None)
|
||||
def variations_select2(request, **kwargs):
|
||||
query = request.GET.get('query', '')
|
||||
try:
|
||||
page = int(request.GET.get('page', '1'))
|
||||
except ValueError:
|
||||
page = 1
|
||||
|
||||
q = Q(item__event=request.event)
|
||||
for word in query.split():
|
||||
q &= Q(value__icontains=i18ncomp(word)) | Q(item__name__icontains=i18ncomp(ord))
|
||||
|
||||
qs = ItemVariation.objects.filter(q).order_by('item__position', 'item__name', 'position', 'value').select_related('item')
|
||||
|
||||
total = qs.count()
|
||||
pagesize = 20
|
||||
offset = (page - 1) * pagesize
|
||||
doc = {
|
||||
'results': [
|
||||
{
|
||||
'id': e.pk,
|
||||
'text': str(e.item) + " – " + str(e),
|
||||
}
|
||||
for e in qs[offset:offset + pagesize]
|
||||
],
|
||||
'pagination': {
|
||||
"more": total >= (offset + pagesize)
|
||||
}
|
||||
}
|
||||
return JsonResponse(doc)
|
||||
|
||||
|
||||
@event_permission_required(None)
|
||||
def category_select2(request, **kwargs):
|
||||
query = request.GET.get('query', '')
|
||||
|
||||
Reference in New Issue
Block a user