Compare commits

..

1 Commits

Author SHA1 Message Date
Richard Schreiber
b56caaed91 Make customer identifier unique per organizer 2022-05-12 17:35:45 +02:00
18 changed files with 433 additions and 673 deletions

View File

@@ -609,17 +609,13 @@ Fetching individual orders
Order ticket download
---------------------
.. versionchanged:: 4.10
The API now supports ticket downloads for pending orders if allowed by the event settings.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/download/(output)/
Download tickets for an order, identified by its order code. Depending on the chosen output, the response might
be a ZIP file, PDF file or something else. The order details response contains a list of output options for this
particular order.
Tickets can only be downloaded if ticket downloads are active and depending on event settings the order is either paid or pending. Note that in some cases the
Tickets can be only downloaded if the order is paid and if ticket downloads are active. Note that in some cases the
ticket file might not yet have been created. In that case, you will receive a status code :http:statuscode:`409` and
you are expected to retry the request after a short period of waiting.
@@ -1639,10 +1635,6 @@ Fetching individual positions
Order position ticket download
------------------------------
.. versionchanged:: 4.10
The API now supports ticket downloads for pending orders if allowed by the event settings.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/download/(output)/
Download tickets for one order position, identified by its internal ID.
@@ -1654,7 +1646,7 @@ Order position ticket download
The referenced URL can provide a download or a regular, human-viewable website - so it is advised to open this URL
in a webbrowser and leave it up to the user to handle the result.
Tickets can only be downloaded if ticket downloads are active and depending on event settings the order is either paid or pending. Also, depending on event
Tickets can be only downloaded if the order is paid and if ticket downloads are active. Also, depending on event
configuration downloads might be only unavailable for add-on products or non-admission products.
Note that in some cases the ticket file might not yet have been created. In that case, you will receive a status
code :http:statuscode:`409` and you are expected to retry the request after a short period of waiting.

View File

@@ -157,7 +157,6 @@ class CheckinListViewSet(viewsets.ModelViewSet):
list=self.get_object(),
successful=False,
forced=True,
force_sent=True,
device=self.request.auth if isinstance(self.request.auth, Device) else None,
gate=self.request.auth.gate if isinstance(self.request.auth, Device) else None,
**kwargs,

View File

@@ -261,11 +261,8 @@ class OrderViewSet(viewsets.ModelViewSet):
provider = self._get_output_provider(output)
order = self.get_object()
if order.status in (Order.STATUS_CANCELED, Order.STATUS_EXPIRED):
raise PermissionDenied("Downloads are not available for canceled or expired orders.")
if order.status == Order.STATUS_PENDING and not request.event.settings.ticket_download_pending:
raise PermissionDenied("Downloads are not available for pending orders.")
if order.status != Order.STATUS_PAID:
raise PermissionDenied("Downloads are not available for unpaid orders.")
ct = CachedCombinedTicket.objects.filter(
order=order, provider=provider.identifier, file__isnull=False
@@ -1122,11 +1119,8 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
provider = self._get_output_provider(output)
pos = self.get_object()
if pos.order.status in (Order.STATUS_CANCELED, Order.STATUS_EXPIRED):
raise PermissionDenied("Downloads are not available for canceled or expired orders.")
if pos.order.status == Order.STATUS_PENDING and not request.event.settings.ticket_download_pending:
raise PermissionDenied("Downloads are not available for pending orders.")
if pos.order.status != Order.STATUS_PAID:
raise PermissionDenied("Downloads are not available for unpaid orders.")
if not pos.generate_ticket:
raise PermissionDenied("Downloads are not enabled for this product.")

View File

@@ -196,16 +196,10 @@ class SecretKeySettingsWidget(forms.TextInput):
attrs.update({
'autocomplete': 'new-password' # see https://bugs.chromium.org/p/chromium/issues/detail?id=370363#c7
})
self.__reflect_value = False
super().__init__(attrs)
def value_from_datadict(self, data, files, name):
value = super().value_from_datadict(data, files, name)
self.__reflect_value = value and value != SECRET_REDACTED
return value
def get_context(self, name, value, attrs):
if value and not self.__reflect_value:
if value:
value = SECRET_REDACTED
return super().get_context(name, value, attrs)

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.2.12 on 2022-04-29 13:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0215_customer_organizer_identifier_unique'),
]
operations = [
migrations.AddField(
model_name='checkin',
name='force_sent',
field=models.BooleanField(default=False, null=True),
),
]

View File

@@ -326,13 +326,7 @@ class Checkin(models.Model):
type = models.CharField(max_length=100, choices=CHECKIN_TYPES, default=TYPE_ENTRY)
nonce = models.CharField(max_length=190, null=True, blank=True)
# Whether or not the scan was made offline
force_sent = models.BooleanField(default=False, null=True, blank=True)
# Whether the scan was made offline AND would have not been possible online
forced = models.BooleanField(default=False)
device = models.ForeignKey(
'pretixbase.Device', related_name='checkins', on_delete=models.PROTECT, null=True, blank=True
)

View File

@@ -796,7 +796,6 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
gate=device.gate if device else None,
nonce=nonce,
forced=force and (not entry_allowed or from_revoked_secret),
force_sent=force,
raw_barcode=raw_barcode,
)
op.order.log_action('pretix.event.checkin', data={

View File

@@ -36,7 +36,6 @@ from django.utils import timezone, translation
from django.utils.timezone import get_current_timezone
from django.utils.translation import get_language, gettext as _
from django.views.generic import FormView
from redis import ResponseError
from pretix.base.models import User
from pretix.base.services.tasks import ProfiledEventTask
@@ -69,11 +68,6 @@ class AsyncMixin:
res.get(timeout=timeout, propagate=False)
except celery.exceptions.TimeoutError:
pass
except ResponseError:
# There is a long-standing concurrency issue in either celery or redis-py that hasn't been fixed
# yet. Instead of crashing, we can ignore it and the client will retry their request and hopefully
# it is fixed next time.
logger.warning('Ignored ResponseError in AsyncResult.get()')
except ConnectionError:
# Redis probably just restarted, let's just report not ready and retry next time
data = self._ajax_response_data()

View File

@@ -32,7 +32,6 @@
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import copy
import os
from decimal import Decimal
from urllib.parse import urlencode
@@ -424,10 +423,9 @@ class ItemCreateForm(I18nModelForm):
if self.cleaned_data.get('has_variations'):
if self.cleaned_data.get('copy_from') and self.cleaned_data.get('copy_from').has_variations:
for variation in self.cleaned_data['copy_from'].variations.all():
v = copy.copy(variation)
v.pk = None
v.item = instance
v.save()
ItemVariation.objects.create(item=instance, value=variation.value, active=variation.active,
position=variation.position, default_price=variation.default_price,
description=variation.description, original_price=variation.original_price)
else:
ItemVariation.objects.create(
item=instance, value=__('Standard')

View File

@@ -80,17 +80,13 @@
{% elif c.forced and c.successful %}
<span class="fa fa-fw fa-warning" data-toggle="tooltip"
title="{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Additional entry scan: {{ date }}{% endblocktrans %}"></span>
{% elif c.force_sent %}
<span class="fa fa-fw fa-cloud-upload" data-toggle="tooltip"
title="{% blocktrans trimmed with date=c.created|date:'SHORT_DATETIME_FORMAT' %}Offline scan. Upload time: {{ date }}{% endblocktrans %}"></span>
{% elif c.forced and not c.successful %}
<br>
<small class="text-muted">{% trans "Failed in offline mode" %}</small>
{% elif c.auto_checked_in %}
<span class="fa fa-fw fa-magic" data-toggle="tooltip"
title="{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically checked in: {{ date }}{% endblocktrans %}"></span>
{% endif %}
{% if c.forced and not c.successful %}
<br>
<small class="text-muted">{% trans "Failed in offline mode" %}</small>
{% endif %}
</td>
<td>
{% if c.type == "exit" %}<span class="fa fa-fw fa-sign-out"></span>{% endif %}

View File

@@ -5,8 +5,8 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-28 16:44+0000\n"
"PO-Revision-Date: 2022-05-16 19:00+0000\n"
"Last-Translator: Dennis Lichtenthäler <lichtenthaeler@rami.io>\n"
"PO-Revision-Date: 2022-04-28 18:04+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
">\n"
"Language: de\n"
@@ -24436,7 +24436,7 @@ msgid ""
"methods such as giropay, iDEAL, Alipay,and many more."
msgstr ""
"Akzeptieren Sie Zahlungen über Stripe, einen weltweit beliebten "
"Zahlungsdienstleister. Stripe unterstützt Zahlungen per Kreditkarte sowie "
"Zahlungsdienstleister. PayPal unterstützt Zahlungen per Kreditkarte sowie "
"viele lokale Zahlungsarten wie z.B. giropay, iDEAL, Alipay, und viele mehr."
#: pretix/plugins/stripe/forms.py:40

View File

@@ -4,8 +4,8 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-28 16:44+0000\n"
"PO-Revision-Date: 2022-05-13 14:36+0000\n"
"Last-Translator: Jonathan Berger <drskullster@gmail.com>\n"
"PO-Revision-Date: 2022-05-09 15:43+0000\n"
"Last-Translator: hmontheline <Helias.mackay@goetheanum.ch>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
">\n"
"Language: fr\n"
@@ -1291,8 +1291,10 @@ msgstr "Date de commande"
#: pretix/base/exporters/orderlist.py:444
#: pretix/base/exporters/orderlist.py:547
#: pretix/plugins/checkinlists/exporters.py:469
#, fuzzy
#| msgid "Order date"
msgid "Order time"
msgstr "Heure de commande"
msgstr "Date de commande"
#: pretix/base/exporters/orderlist.py:280
#, fuzzy
@@ -1540,44 +1542,60 @@ msgstr "Tickets"
#: pretix/base/exporters/orderlist.py:577 pretix/base/orderimport.py:671
#: pretix/plugins/checkinlists/exporters.py:472
#, fuzzy
#| msgid "Client ID"
msgid "Seat ID"
msgstr "Numéro de siège"
msgstr "Numéro de client"
#: pretix/base/exporters/orderlist.py:578
#: pretix/plugins/checkinlists/exporters.py:473
#, fuzzy
#| msgid "Team name"
msgid "Seat name"
msgstr "Nom du siège"
msgstr "Nom de l'équipe"
#: pretix/base/exporters/orderlist.py:579
#: pretix/plugins/checkinlists/exporters.py:474
#, fuzzy
#| msgid "Team name"
msgid "Seat zone"
msgstr "Zone du siège"
msgstr "Nom de l'équipe"
#: pretix/base/exporters/orderlist.py:580
#: pretix/plugins/checkinlists/exporters.py:475
#, fuzzy
#| msgid "Client ID"
msgid "Seat row"
msgstr "Rangée du siège"
msgstr "Numéro de client"
#: pretix/base/exporters/orderlist.py:581
#: pretix/plugins/checkinlists/exporters.py:476
#, fuzzy
#| msgid "Team name"
msgid "Seat number"
msgstr "Numéro de siège"
msgstr "Nom de l'équipe"
#: pretix/base/exporters/orderlist.py:582
#, fuzzy
#| msgid "Order code"
msgid "Order comment"
msgstr "Commentaire de commande"
msgstr "Code de commande"
#: pretix/base/exporters/orderlist.py:750
msgid "Order payments and refunds"
msgstr "Commandes et remboursements"
#: pretix/base/exporters/orderlist.py:758
#, fuzzy
#| msgid "Payment date"
msgid "Payment states"
msgstr "États de paiement"
msgstr "Date de paiement"
#: pretix/base/exporters/orderlist.py:766
#, fuzzy
#| msgid "Check-in status"
msgid "Refund states"
msgstr "Etats des remboursements"
msgstr "Statut d'enregistrement"
#: pretix/base/exporters/orderlist.py:791
#: pretix/base/exporters/orderlist.py:930
@@ -1784,8 +1802,10 @@ msgstr "Remises par cartes-cadeaux"
#: pretix/base/exporters/orderlist.py:969
#: pretix/control/templates/pretixcontrol/giftcards/payment.html:10
#, fuzzy
#| msgid "Voucher details"
msgid "Issuer"
msgstr "Emetteur"
msgstr "Détails du bon"
#: pretix/base/exporters/orderlist.py:997 pretix/control/navigation.py:523
#: pretix/control/templates/pretixcontrol/organizers/edit.html:84
@@ -2019,8 +2039,10 @@ msgid "Please do not use special characters in names."
msgstr "Veuillez entrer votre nom."
#: pretix/base/forms/questions.py:257
#, fuzzy
#| msgid "Please enter your name."
msgid "Please enter a shorter name."
msgstr "Veuillez entrer un nom plus court."
msgstr "Veuillez entrer votre nom."
#: pretix/base/forms/questions.py:274
#, fuzzy

File diff suppressed because it is too large Load Diff

View File

@@ -618,7 +618,6 @@ class CheckinLogList(ListExporter):
_('Product'),
_('Name'),
_('Device'),
_('Offline'),
_('Offline override'),
_('Automatically checked in'),
_('Gate'),
@@ -665,7 +664,6 @@ class CheckinLogList(ListExporter):
str(ci.position.item) if ci.position else (str(ci.raw_item) if ci.raw_item else ''),
(ci.position.attendee_name or ia.name) if ci.position else '',
str(ci.device) if ci.device else '',
_('Yes') if ci.force_sent is True else (_('No') if ci.force_sent is False else '?'),
_('Yes') if ci.forced else _('No'),
_('Yes') if ci.auto_checked_in else _('No'),
str(ci.gate or ''),

View File

@@ -70,7 +70,6 @@ function async_task_check_error(jqXHR, textStatus, errorThrown) {
jqXHR.responseText.indexOf("<body"),
jqXHR.responseText.indexOf("</body")
));
setup_basics($("body"));
form_handlers($("body"));
setup_collapsible_details($("body"));
window.setTimeout(function () { $(window).scrollTop(0) }, 200)
@@ -153,7 +152,6 @@ function async_task_error(jqXHR, textStatus, errorThrown) {
if (respdom.filter('#page-wrapper') && $('#page-wrapper').length) {
$("#page-wrapper").html(respdom.find("#page-wrapper").html());
setup_basics($("#page-wrapper"));
form_handlers($("#page-wrapper"));
setup_collapsible_details($("#page-wrapper"));
$(document).trigger("pretix:bind-forms");
@@ -163,7 +161,6 @@ function async_task_error(jqXHR, textStatus, errorThrown) {
jqXHR.responseText.indexOf("<body"),
jqXHR.responseText.indexOf("</body")
));
setup_basics($("body"));
form_handlers($("body"));
setup_collapsible_details($("body"));
$(document).trigger("pretix:bind-forms");

View File

@@ -918,7 +918,7 @@ $(function () {
height: 196
}
);
$div.append($("<div>").text($(this).attr("data-qrcode").slice(0, 10)).get(0).innerHTML + "…<br>");
$div.append($(this).attr("data-qrcode").slice(0, 10) + "…<br>");
$div.append(gettext("Click to close"));
$div.slideDown(200);
$div.click(function (e) {

View File

@@ -174,63 +174,6 @@ var form_handlers = function (el) {
}
};
function setup_basics(el) {
el.find("input[data-toggle=radiocollapse]").change(function () {
$($(this).attr("data-parent")).find(".collapse.in").collapse('hide');
$($(this).attr("data-target")).collapse('show');
});
el.find(".js-only").removeClass("js-only");
el.find(".js-hidden").hide();
el.find("div.collapsed").removeClass("collapsed").addClass("collapse");
el.find(".has-error, .alert-danger").each(function () {
$(this).closest("div.panel-collapse").collapse("show");
});
el.find(".has-error").first().each(function(){
if ($(this).is(':input')) this.focus();
else $(":input", this).get(0).focus();
});
el.find(".alert-danger").first().each(function() {
var content = $("<ul></ul>").click(function(e) {
var input = $(e.target.hash).get(0);
if (input) input.focus();
input.scrollIntoView({block: "center"});
e.preventDefault();
});
$(".has-error").each(function() {
var target = target = $(":input", this);
var desc = $("#" + target.attr("aria-describedby").split(' ', 1)[0]);
// multi-input fields have a role=group with aria-labelledby
var label = this.hasAttribute("aria-labelledby") ? $("#" + this.getAttribute("aria-labelledby")) : $("[for="+target.attr("id")+"]");
var $li = $("<li>");
$li.text(": " + desc.text())
$li.prepend($("<a>").attr("href", "#" + target.attr("id")).text(label.get(0).childNodes[0].nodeValue))
content.append($li);
});
$(this).append(content);
});
el.find("[data-click-to-load]").on("click", function(e) {
var target = document.getElementById(this.getAttribute("data-click-to-load"));
target.src = this.href;
target.focus();
e.preventDefault();
});
el.find('[data-toggle="tooltip"]').tooltip();
// AddOns
el.find('.addon-variation-description').hide();
el.find('.toggle-variation-description').click(function () {
$(this).parent().find('.addon-variation-description').slideToggle();
});
el.find('input[type=radio][description]').change(function () {
if ($(this).prop("checked")) {
$(this).parent().parent().find('.addon-variation-description').stop().slideDown();
}
});
}
$(function () {
"use strict";
@@ -248,7 +191,48 @@ $(function () {
if (!$input.prop("checked")) $input.prop('checked', true).trigger("change");
});
setup_basics($("body"));
$("input[data-toggle=radiocollapse]").change(function () {
$($(this).attr("data-parent")).find(".collapse.in").collapse('hide');
$($(this).attr("data-target")).collapse('show');
});
$(".js-only").removeClass("js-only");
$(".js-hidden").hide();
$("div.collapsed").removeClass("collapsed").addClass("collapse");
$(".has-error, .alert-danger").each(function () {
$(this).closest("div.panel-collapse").collapse("show");
});
$(".has-error").first().each(function(){
if ($(this).is(':input')) this.focus();
else $(":input", this).get(0).focus();
});
$(".alert-danger").first().each(function() {
var content = $("<ul></ul>").click(function(e) {
var input = $(e.target.hash).get(0);
if (input) input.focus();
input.scrollIntoView({block: "center"});
e.preventDefault();
});
$(".has-error").each(function() {
var target = target = $(":input", this);
var desc = $("#" + target.attr("aria-describedby").split(' ', 1)[0]);
// multi-input fields have a role=group with aria-labelledby
var label = this.hasAttribute("aria-labelledby") ? $("#" + this.getAttribute("aria-labelledby")) : $("[for="+target.attr("id")+"]");
var $li = $("<li>");
$li.text(": " + desc.text())
$li.prepend($("<a>").attr("href", "#" + target.attr("id")).text(label.get(0).childNodes[0].nodeValue))
content.append($li);
});
$(this).append(content);
});
$("[data-click-to-load]").on("click", function(e) {
var target = document.getElementById(this.getAttribute("data-click-to-load"));
target.src = this.href;
target.focus();
e.preventDefault();
});
$(".overlay-remove").on("click", function() {
$(this).closest(".contains-overlay").find(".overlay").fadeOut();
});
@@ -260,8 +244,21 @@ $(function () {
$("#voucher-toggle").slideUp();
});
$('[data-toggle="tooltip"]').tooltip();
$("#ajaxerr").on("click", ".ajaxerr-close", ajaxErrDialog.hide);
// AddOns
$('.addon-variation-description').hide();
$('.toggle-variation-description').click(function () {
$(this).parent().find('.addon-variation-description').slideToggle();
});
$('input[type=radio][description]').change(function () {
if ($(this).prop("checked")) {
$(this).parent().parent().find('.addon-variation-description').stop().slideDown();
}
});
// Copy answers
$(".js-copy-answers").click(function (e) {
e.preventDefault();

View File

@@ -814,7 +814,6 @@ def test_forced_flag_set_if_required(token_client, organizer, clist, event, orde
), {'force': True}, format='json')
with scopes_disabled():
assert not p.checkins.order_by('pk').last().forced
assert p.checkins.order_by('pk').last().force_sent
assert resp.status_code == 201
assert resp.data['status'] == 'ok'
resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'.format(
@@ -822,7 +821,6 @@ def test_forced_flag_set_if_required(token_client, organizer, clist, event, orde
), {'force': True}, format='json')
with scopes_disabled():
assert p.checkins.order_by('pk').last().forced
assert p.checkins.order_by('pk').last().force_sent
assert resp.status_code == 201
assert resp.data['status'] == 'ok'
@@ -1186,7 +1184,6 @@ def test_redeem_unknown_revoked_force(token_client, organizer, clist, event, ord
assert resp.data["status"] == "ok"
with scopes_disabled():
assert Checkin.objects.last().forced
assert Checkin.objects.last().force_sent
@pytest.mark.django_db