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.") 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': { 'ticket_download': {
'default': 'False', 'default': 'False',
'type': bool, 'type': bool,

View File

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

View File

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

View File

@@ -31,6 +31,8 @@
</p> </p>
{% if line.seat or line.voucher or line.subevent or line.used_membership%} {% if line.seat or line.voucher or line.subevent or line.used_membership%}
<dl class="dl-inline"> <dl class="dl-inline">
{% elif event.settings.show_checkin_number_user and line.checkin_count %}
<dl class="dl-inline">
{% endif %} {% endif %}
{% if line.seat %} {% if line.seat %}
<div class="cart-icon-details"> <div class="cart-icon-details">
@@ -94,8 +96,25 @@
</dd> </dd>
</div> </div>
{% endif %} {% 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%} {% if line.seat or line.voucher or line.subevent or line.used_membership%}
</dl> </dl>
{% elif event.settings.show_checkin_number_user and line.checkin_count %}
</dl>
{% endif %} {% endif %}
{% if line.issued_gift_cards.exists %} {% 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.exceptions import ValidationError
from django.core.files import File from django.core.files import File
from django.db import transaction 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 ( from django.http import (
FileResponse, Http404, HttpResponseRedirect, JsonResponse, 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 django.views.generic import TemplateView, View
from pretix.base.models import ( 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 ( from pretix.base.models.orders import (
CachedCombinedTicket, InvoiceAddress, OrderFee, OrderPayment, OrderRefund, CachedCombinedTicket, InvoiceAddress, OrderFee, OrderPayment, OrderRefund,
@@ -124,12 +125,13 @@ class OrderDetailMixin(NoSearchIndexViewMixin):
class OrderPositionDetailMixin(NoSearchIndexViewMixin): class OrderPositionDetailMixin(NoSearchIndexViewMixin):
@cached_property @cached_property
def position(self): def position(self):
p = OrderPosition.objects.filter( qs = OrderPosition.objects.filter(
order__event=self.request.event, order__event=self.request.event,
addon_to__isnull=True, addon_to__isnull=True,
order__code=self.kwargs['order'], order__code=self.kwargs['order'],
positionid=self.kwargs['position'] positionid=self.kwargs['position']
).select_related('order', 'order__event').first() ).select_related('order', 'order__event')
p = qs.first()
if p: if p:
if p.web_secret.lower() == self.kwargs['secret'].lower(): if p.web_secret.lower() == self.kwargs['secret'].lower():
return p return p
@@ -229,9 +231,21 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TicketPageMixin,
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
ctx = super().get_context_data(**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( ctx['cart'] = self.get_cart(
answers=True, downloads=ctx['can_download'], answers=True,
queryset=self.order.positions.prefetch_related('issued_gift_cards').select_related('tax_rule'), downloads=ctx['can_download'],
queryset=qs,
order=self.order order=self.order
) )
ctx['tickets_with_download'] = [p for p in ctx['cart']['positions'] if p.generate_ticket] 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 return buttons
def get_context_data(self, **kwargs): 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 = super().get_context_data(**kwargs)
ctx['can_download_multi'] = False ctx['can_download_multi'] = False
ctx['position'] = self.position ctx['position'] = self.position
ctx['cart'] = self.get_cart( ctx['cart'] = self.get_cart(
answers=True, downloads=ctx['can_download'], answers=True, downloads=ctx['can_download'],
queryset=self.order.positions.select_related('tax_rule').filter( queryset=qs,
Q(pk=self.position.pk) | Q(addon_to__id=self.position.pk)
),
order=self.order order=self.order
) )
ctx['tickets_with_download'] = [p for p in ctx['cart']['positions'] if p.generate_ticket] ctx['tickets_with_download'] = [p for p in ctx['cart']['positions'] if p.generate_ticket]