mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Add button to reset entire check-in stack (Z#23188730) (#5312)
* Show print logs to admins * Add button to reset entire check-in stack (Z#23188730) * isort * Update src/pretix/control/templates/pretixcontrol/checkin/reset.html Co-authored-by: Richard Schreiber <schreiber@rami.io> * Update src/pretix/control/templates/pretixcontrol/checkin/reset.html Co-authored-by: Richard Schreiber <schreiber@rami.io> * Update src/pretix/control/templates/pretixcontrol/checkin/reset.html Co-authored-by: Richard Schreiber <schreiber@rami.io> * Update src/pretix/control/templates/pretixcontrol/checkin/lists.html Co-authored-by: Richard Schreiber <schreiber@rami.io> --------- Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
@@ -215,3 +215,9 @@ class CheckinListSimulatorForm(forms.Form):
|
||||
)
|
||||
self.fields['gate'].widget.choices = self.fields['gate'].choices
|
||||
self.fields['gate'].label = _('Gate')
|
||||
|
||||
|
||||
class CheckinResetForm(forms.Form):
|
||||
ok = forms.BooleanField(
|
||||
label=_("I am sure that the check-in state of the entire event should be reset.")
|
||||
)
|
||||
|
||||
@@ -722,6 +722,7 @@ class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
|
||||
'pretix.giftcards.transaction.manual': _('A manual transaction has been performed.'),
|
||||
'pretix.team.token.created': _('The token "{name}" has been created.'),
|
||||
'pretix.team.token.deleted': _('The token "{name}" has been revoked.'),
|
||||
'pretix.event.checkin.reset': _('The check-in and print log state has been reset.')
|
||||
})
|
||||
class CoreLogEntryType(LogEntryType):
|
||||
pass
|
||||
|
||||
@@ -83,6 +83,13 @@
|
||||
<a href="{% url "control:organizer.devices" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-tablet"></i> {% trans "Connected devices" %}</a>
|
||||
{% endif %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.reset" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-repeat"></span>
|
||||
{% trans "Reset check-in" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-quotas">
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{% extends "pretixcontrol/items/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block title %}{% trans "Reset check-in" %}{% endblock %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Reset check-in" %}</h1>
|
||||
<form action="" method="post" class="" data-asynctask>
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
With this feature, you can reset the entire check-in state of the event.
|
||||
This will delete all check-in records as well as all records of printed tickets or badges.
|
||||
We recommend to use this feature after testing your hardware setup but only before your
|
||||
event started, and you admitted any real attendees or printed any real badges or tickets.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p class="alert alert-danger">
|
||||
{% blocktrans trimmed count count=checkins %}
|
||||
This will permanently delete <strong>1 check-in</strong>.
|
||||
{% plural %}
|
||||
This will permanently delete <strong>{{ count }} check-ins</strong>.
|
||||
{% endblocktrans %}
|
||||
{% blocktrans trimmed count count=printlogs %}
|
||||
Additionally, <strong>1 print log</strong> will be deleted.
|
||||
{% plural %}
|
||||
Additionally, <strong>{{ count }} print logs</strong> will be deleted.
|
||||
{% endblocktrans %}
|
||||
<br>
|
||||
<strong>
|
||||
{% trans "This cannot be reverted!" %}
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The deleted entries will still show up in the "Order history" section, but for all other
|
||||
purposes the system will behave as if they never existed.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% bootstrap_form form layout="inline" %}
|
||||
<div class="form-group submit-group">
|
||||
<a href="{% url "control:event.orders.checkinlists" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default btn-cancel">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger btn-save">
|
||||
{% trans "Proceed with reset" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -500,6 +500,18 @@
|
||||
{% eventsignal event "pretix.control.signals.order_position_buttons" order=order position=line request=request %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if staff_session %}
|
||||
<div class="admin-only print-logs">
|
||||
{% for pl in line.print_logs.all %}
|
||||
<span class="fa fa-print"></span>
|
||||
{{ pl.datetime|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{{ pl.get_type_display }}
|
||||
({{ pl.source }}{% if pl.device %}, #{{ pl.device.device_id }}{% endif %})
|
||||
{% if not pl.successful %}<span class="fa fa-warning fa-fw"></span>{% endif %}
|
||||
<br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if line.issued_gift_cards %}
|
||||
<dl>
|
||||
{% for gc in line.issued_gift_cards.all %}
|
||||
|
||||
@@ -464,6 +464,7 @@ urlpatterns = [
|
||||
re_path(r'^checkins/$', checkin.CheckinListView.as_view(), name='event.orders.checkins'),
|
||||
re_path(r'^checkinlists/$', checkin.CheckinListList.as_view(), name='event.orders.checkinlists'),
|
||||
re_path(r'^checkinlists/add$', checkin.CheckinListCreate.as_view(), name='event.orders.checkinlists.add'),
|
||||
re_path(r'^checkinlists/reset$', checkin.CheckInResetView.as_view(), name='event.orders.checkinlists.reset'),
|
||||
re_path(r'^checkinlists/select2$', typeahead.checkinlist_select2, name='event.orders.checkinlists.select2'),
|
||||
re_path(r'^checkinlists/(?P<list>\d+)/$', checkin.CheckInListShow.as_view(), name='event.orders.checkinlists.show'),
|
||||
re_path(r'^checkinlists/(?P<list>\d+)/simulator$', checkin.CheckInListSimulator.as_view(), name='event.orders.checkinlists.simulator'),
|
||||
|
||||
@@ -50,15 +50,16 @@ from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.api.views.checkin import _redeem_process
|
||||
from pretix.base.media import MEDIA_TYPES
|
||||
from pretix.base.models import Checkin, Order, OrderPosition
|
||||
from pretix.base.models import Checkin, LogEntry, Order, OrderPosition
|
||||
from pretix.base.models.checkin import CheckinList
|
||||
from pretix.base.models.orders import PrintLog
|
||||
from pretix.base.services.checkin import (
|
||||
LazyRuleVars, _logic_annotate_for_graphic_explain,
|
||||
)
|
||||
from pretix.base.signals import checkin_created
|
||||
from pretix.base.views.tasks import AsyncPostView
|
||||
from pretix.base.views.tasks import AsyncFormView, AsyncPostView
|
||||
from pretix.control.forms.checkin import (
|
||||
CheckinListForm, CheckinListSimulatorForm,
|
||||
CheckinListForm, CheckinListSimulatorForm, CheckinResetForm,
|
||||
)
|
||||
from pretix.control.forms.filter import (
|
||||
CheckinFilterForm, CheckinListAttendeeFilterForm, CheckinListFilterForm,
|
||||
@@ -570,3 +571,55 @@ class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
|
||||
for q in self.result["questions"]:
|
||||
q["question"] = LazyI18nString(q["question"])
|
||||
return self.get(self.request, self.args, self.kwargs)
|
||||
|
||||
|
||||
class CheckInResetView(CheckInListQueryMixin, EventPermissionRequiredMixin, AsyncFormView):
|
||||
form_class = CheckinResetForm
|
||||
permission = "can_change_orders"
|
||||
template_name = "pretixcontrol/checkin/reset.html"
|
||||
|
||||
def get_error_url(self, *args):
|
||||
return reverse(
|
||||
"control:event.orders.checkinlists",
|
||||
kwargs={
|
||||
"event": self.request.event.slug,
|
||||
"organizer": self.request.organizer.slug,
|
||||
},
|
||||
)
|
||||
|
||||
def get_success_url(self, *args):
|
||||
return reverse(
|
||||
"control:event.orders.checkinlists",
|
||||
kwargs={
|
||||
"event": self.request.event.slug,
|
||||
"organizer": self.request.organizer.slug,
|
||||
},
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['checkins'] = Checkin.all.filter(list__event=self.request.event).count()
|
||||
ctx['printlogs'] = PrintLog.objects.filter(position__order__event=self.request.event).count()
|
||||
return ctx
|
||||
|
||||
def async_form_valid(self, task, form):
|
||||
with transaction.atomic():
|
||||
qs = Checkin.all.filter(list__event=self.request.event).select_related("position", "position__order")
|
||||
logentries = []
|
||||
for ci in qs:
|
||||
if ci.position:
|
||||
logentries.append(ci.position.order.log_action('pretix.event.checkin.reverted', data={
|
||||
'position': ci.position.id,
|
||||
'positionid': ci.position.positionid,
|
||||
'list': ci.list_id,
|
||||
'web': True
|
||||
}, user=self.request.user, save=False))
|
||||
|
||||
Order.objects.filter(pk__in=qs.values_list("position__order_id", flat=True)).update(last_modified=now())
|
||||
qs.delete()
|
||||
LogEntry.objects.bulk_create(logentries)
|
||||
|
||||
pl = PrintLog.objects.filter(position__order__event=self.request.event)
|
||||
pl.delete()
|
||||
self.request.event.log_action('pretix.event.checkin.reset', user=self.request.user)
|
||||
self.request.event.cache.clear()
|
||||
|
||||
@@ -83,6 +83,7 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.models.orders import (
|
||||
CancellationRequest, OrderFee, OrderPayment, OrderPosition, OrderRefund,
|
||||
PrintLog,
|
||||
)
|
||||
from pretix.base.models.tax import ask_for_vat_id
|
||||
from pretix.base.payment import PaymentException
|
||||
@@ -597,6 +598,7 @@ class OrderDetail(OrderView):
|
||||
'item__questions', 'issued_gift_cards', 'owned_gift_cards', 'linked_media',
|
||||
Prefetch('answers', queryset=QuestionAnswer.objects.prefetch_related('options').select_related('question')),
|
||||
Prefetch('all_checkins', queryset=Checkin.all.select_related('list').order_by('datetime')),
|
||||
Prefetch('print_logs', queryset=PrintLog.objects.select_related('device').order_by('datetime')),
|
||||
).order_by('positionid')
|
||||
|
||||
positions = []
|
||||
|
||||
@@ -636,6 +636,10 @@ details summary {
|
||||
.position-buttons {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.print-logs {
|
||||
padding-left: 20px;
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
|
||||
.pos-canceled * {
|
||||
color: $brand-danger;
|
||||
|
||||
Reference in New Issue
Block a user