Allow users to see the number of checkins (#2561)

Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
Raphael Michel
2022-03-30 18:03:05 +02:00
committed by GitHub
parent c23a3fcfcd
commit 22f3412ad0
5 changed files with 67 additions and 10 deletions

View File

@@ -1195,7 +1195,20 @@ DEFAULTS = {
help_text=_("If you ask for a phone number, explain why you do so and what you will use the phone number for.")
)
},
'show_checkin_number_user': {
'default': 'False',
'type': bool,
'serializer_class': serializers.BooleanField,
'form_class': forms.BooleanField,
'form_kwargs': dict(
label=_("Show number of check-ins to customer"),
help_text=_('With this option enabled, your customers will be able how many times they entered '
'the event. This is usually not necessary, but might be useful in combination with tickets '
'that are usable a specific number of times, so customers can see how many times they have '
'already been used. Exits or failed scans will not be counted, and the user will not see '
'the different check-in lists.'),
)
},
'ticket_download': {
'default': 'False',
'type': bool,

View File

@@ -523,6 +523,7 @@ class EventSettingsForm(SettingsForm):
'last_order_modification_date',
'allow_modifications_after_checkin',
'checkout_show_copy_answers_button',
'show_checkin_number_user',
'primary_color',
'theme_color_success',
'theme_color_danger',

View File

@@ -221,6 +221,7 @@
{% bootstrap_field sform.show_items_outside_presale_period layout="control" %}
{% bootstrap_field sform.last_order_modification_date layout="control" %}
{% bootstrap_field sform.allow_modifications_after_checkin layout="control" %}
{% bootstrap_field sform.show_checkin_number_user layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Display" %}</legend>

View File

@@ -31,6 +31,8 @@
</p>
{% if line.seat or line.voucher or line.subevent or line.used_membership%}
<dl class="dl-inline">
{% elif event.settings.show_checkin_number_user and line.checkin_count %}
<dl class="dl-inline">
{% endif %}
{% if line.seat %}
<div class="cart-icon-details">
@@ -94,8 +96,25 @@
</dd>
</div>
{% endif %}
{% if event.settings.show_checkin_number_user and line.checkin_count %}
<div class="cart-icon-details">
<dt class="sr-only">{% trans "Usage:" context "ticket_checkins" %}</dt>
<dd class="text-success">
<span class="fa fa-check-circle fa-fw text-success" aria-hidden="true"></span>
<strong>
{% blocktrans trimmed count count=line.checkin_count %}
This ticket has been used once.
{% plural %}
This ticket has been used {{ count }} times.
{% endblocktrans %}
</strong>
</dd>
</div>
{% endif %}
{% if line.seat or line.voucher or line.subevent or line.used_membership%}
</dl>
{% elif event.settings.show_checkin_number_user and line.checkin_count %}
</dl>
{% endif %}
{% if line.issued_gift_cards.exists %}

View File

@@ -47,7 +47,7 @@ from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.files import File
from django.db import transaction
from django.db.models import Exists, OuterRef, Q, Sum
from django.db.models import Count, Exists, OuterRef, Q, Subquery, Sum
from django.http import (
FileResponse, Http404, HttpResponseRedirect, JsonResponse,
)
@@ -60,7 +60,8 @@ from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import TemplateView, View
from pretix.base.models import (
CachedTicket, GiftCard, Invoice, Order, OrderPosition, Quota, TaxRule,
CachedTicket, Checkin, GiftCard, Invoice, Order, OrderPosition, Quota,
TaxRule,
)
from pretix.base.models.orders import (
CachedCombinedTicket, InvoiceAddress, OrderFee, OrderPayment, OrderRefund,
@@ -124,12 +125,13 @@ class OrderDetailMixin(NoSearchIndexViewMixin):
class OrderPositionDetailMixin(NoSearchIndexViewMixin):
@cached_property
def position(self):
p = OrderPosition.objects.filter(
qs = OrderPosition.objects.filter(
order__event=self.request.event,
addon_to__isnull=True,
order__code=self.kwargs['order'],
positionid=self.kwargs['position']
).select_related('order', 'order__event').first()
).select_related('order', 'order__event')
p = qs.first()
if p:
if p.web_secret.lower() == self.kwargs['secret'].lower():
return p
@@ -229,9 +231,21 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TicketPageMixin,
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
qs = self.order.positions.prefetch_related('issued_gift_cards').select_related('tax_rule')
if self.request.event.settings.show_checkin_number_user:
qs = qs.annotate(
checkin_count=Subquery(
Checkin.objects.filter(
successful=True, type=Checkin.TYPE_ENTRY, position_id=OuterRef('pk')
).order_by().values('position').annotate(c=Count('*')).values('c')
)
)
ctx['cart'] = self.get_cart(
answers=True, downloads=ctx['can_download'],
queryset=self.order.positions.prefetch_related('issued_gift_cards').select_related('tax_rule'),
answers=True,
downloads=ctx['can_download'],
queryset=qs,
order=self.order
)
ctx['tickets_with_download'] = [p for p in ctx['cart']['positions'] if p.generate_ticket]
@@ -328,14 +342,23 @@ class OrderPositionDetails(EventViewMixin, OrderPositionDetailMixin, CartMixin,
return buttons
def get_context_data(self, **kwargs):
qs = self.order.positions.select_related('tax_rule').filter(
Q(pk=self.position.pk) | Q(addon_to__id=self.position.pk)
)
if self.request.event.settings.show_checkin_number_user:
qs = qs.annotate(
checkin_count=Subquery(
Checkin.objects.filter(
successful=True, type=Checkin.TYPE_ENTRY, position_id=OuterRef('pk')
).order_by().values('position').annotate(c=Count('*')).values('c')
)
)
ctx = super().get_context_data(**kwargs)
ctx['can_download_multi'] = False
ctx['position'] = self.position
ctx['cart'] = self.get_cart(
answers=True, downloads=ctx['can_download'],
queryset=self.order.positions.select_related('tax_rule').filter(
Q(pk=self.position.pk) | Q(addon_to__id=self.position.pk)
),
queryset=qs,
order=self.order
)
ctx['tickets_with_download'] = [p for p in ctx['cart']['positions'] if p.generate_ticket]