mirror of
https://github.com/pretix/pretix.git
synced 2026-02-19 08:52:26 +00:00
Compare commits
8 Commits
pdf-not-re
...
add-accept
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c25c03f398 | ||
|
|
fbd8bbbeaa | ||
|
|
1c305e4b30 | ||
|
|
ea114b4f64 | ||
|
|
0342613635 | ||
|
|
743c4b796b | ||
|
|
8a7f54795e | ||
|
|
cb464ad597 |
@@ -92,7 +92,7 @@ dependencies = [
|
||||
"redis==7.1.*",
|
||||
"reportlab==4.4.*",
|
||||
"requests==2.32.*",
|
||||
"sentry-sdk==2.52.*",
|
||||
"sentry-sdk==2.53.*",
|
||||
"sepaxml==2.7.*",
|
||||
"stripe==7.9.*",
|
||||
"text-unidecode==1.*",
|
||||
@@ -110,7 +110,7 @@ dev = [
|
||||
"aiohttp==3.13.*",
|
||||
"coverage",
|
||||
"coveralls",
|
||||
"fakeredis==2.33.*",
|
||||
"fakeredis==2.34.*",
|
||||
"flake8==7.3.*",
|
||||
"freezegun",
|
||||
"isort==7.0.*",
|
||||
|
||||
@@ -188,11 +188,15 @@ class CheckinListViewSet(viewsets.ModelViewSet):
|
||||
clist = self.get_object()
|
||||
if serializer.validated_data.get('nonce'):
|
||||
if kwargs.get('position'):
|
||||
prev = kwargs['position'].all_checkins.filter(nonce=serializer.validated_data['nonce']).first()
|
||||
prev = kwargs['position'].all_checkins.filter(
|
||||
nonce=serializer.validated_data['nonce'],
|
||||
successful=False
|
||||
).first()
|
||||
else:
|
||||
prev = clist.checkins.filter(
|
||||
nonce=serializer.validated_data['nonce'],
|
||||
raw_barcode=serializer.validated_data['raw_barcode'],
|
||||
successful=False
|
||||
).first()
|
||||
if prev:
|
||||
# Ignore because nonce is already handled
|
||||
|
||||
@@ -259,7 +259,14 @@ class GiftCardViewSet(viewsets.ModelViewSet):
|
||||
action='pretix.giftcards.transaction.manual',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=merge_dicts(self.request.data, {'id': inst.pk, 'acceptor_id': self.request.organizer.id})
|
||||
data=merge_dicts(
|
||||
self.request.data,
|
||||
{
|
||||
'id': inst.pk,
|
||||
'acceptor_id': self.request.organizer.id,
|
||||
'acceptor_slug': self.request.organizer.slug
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@transaction.atomic()
|
||||
@@ -290,7 +297,11 @@ class GiftCardViewSet(viewsets.ModelViewSet):
|
||||
action='pretix.giftcards.transaction.manual',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data={'value': diff, 'acceptor_id': self.request.organizer.id}
|
||||
data={
|
||||
'value': diff,
|
||||
'acceptor_id': self.request.organizer.id,
|
||||
'acceptor_slug': self.request.organizer.slug
|
||||
}
|
||||
)
|
||||
|
||||
return inst
|
||||
@@ -320,7 +331,8 @@ class GiftCardViewSet(viewsets.ModelViewSet):
|
||||
data={
|
||||
'value': value,
|
||||
'text': text,
|
||||
'acceptor_id': self.request.organizer.id
|
||||
'acceptor_id': self.request.organizer.id,
|
||||
'acceptor_slug': self.request.organizer.slug
|
||||
}
|
||||
)
|
||||
return Response(GiftCardSerializer(gc, context=self.get_serializer_context()).data, status=status.HTTP_200_OK)
|
||||
|
||||
@@ -198,6 +198,7 @@ class ParametrizedGiftcardTransactionWebhookEvent(ParametrizedWebhookEvent):
|
||||
'notification_id': logentry.pk,
|
||||
'issuer_id': logentry.organizer_id,
|
||||
'acceptor_id': logentry.parsed_data.get('acceptor_id'),
|
||||
'acceptor_slug': logentry.parsed_data.get('acceptor_slug'),
|
||||
'giftcard': giftcard.pk,
|
||||
'action': logentry.action_type,
|
||||
}
|
||||
|
||||
@@ -148,10 +148,6 @@ class NumberedCanvas(Canvas):
|
||||
self.restoreState()
|
||||
|
||||
|
||||
class InvoiceNotReadyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BaseInvoiceRenderer:
|
||||
"""
|
||||
This is the base class for all invoice renderers.
|
||||
|
||||
@@ -132,7 +132,7 @@ class AllowIgnoreQuotaColumn(BooleanColumnMixin, ImportColumn):
|
||||
|
||||
class PriceModeColumn(ImportColumn):
|
||||
identifier = 'price_mode'
|
||||
verbose_name = gettext_lazy('Price mode')
|
||||
verbose_name = gettext_lazy('Price effect')
|
||||
default_value = None
|
||||
initial = 'static:none'
|
||||
|
||||
@@ -147,7 +147,7 @@ class PriceModeColumn(ImportColumn):
|
||||
elif value in reverse:
|
||||
return reverse[value]
|
||||
else:
|
||||
raise ValidationError(_("Could not parse {value} as a price mode, use one of {options}.").format(
|
||||
raise ValidationError(_("Could not parse {value} as a price effect, use one of {options}.").format(
|
||||
value=value, options=', '.join(d.keys())
|
||||
))
|
||||
|
||||
@@ -162,7 +162,7 @@ class ValueColumn(DecimalColumnMixin, ImportColumn):
|
||||
def clean(self, value, previous_values):
|
||||
value = super().clean(value, previous_values)
|
||||
if value and previous_values.get("price_mode") == "none":
|
||||
raise ValidationError(_("It is pointless to set a value without a price mode."))
|
||||
raise ValidationError(_("It is pointless to set a value without a price effect."))
|
||||
return value
|
||||
|
||||
def assign(self, value, obj: Voucher, **kwargs):
|
||||
|
||||
@@ -239,7 +239,7 @@ class Voucher(LoggedModel):
|
||||
)
|
||||
)
|
||||
price_mode = models.CharField(
|
||||
verbose_name=_("Price mode"),
|
||||
verbose_name=_("Price effect"),
|
||||
max_length=100,
|
||||
choices=PRICE_MODES,
|
||||
default='none'
|
||||
|
||||
@@ -1650,7 +1650,8 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
action='pretix.giftcards.transaction.payment',
|
||||
data={
|
||||
'value': trans.value,
|
||||
'acceptor_id': self.event.organizer.id
|
||||
'acceptor_id': self.event.organizer.id,
|
||||
'acceptor_slug': self.event.organizer.slug
|
||||
}
|
||||
)
|
||||
except PaymentException as e:
|
||||
@@ -1682,6 +1683,7 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
data={
|
||||
'value': refund.amount,
|
||||
'acceptor_id': self.event.organizer.id,
|
||||
'acceptor_slug': self.event.organizer.slug,
|
||||
'text': refund.comment,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -51,7 +51,6 @@ from django_scopes import scope, scopes_disabled
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.invoicing.pdf import InvoiceNotReadyException
|
||||
from pretix.base.invoicing.transmission import (
|
||||
get_transmission_types, transmission_providers,
|
||||
)
|
||||
@@ -505,7 +504,7 @@ def generate_invoice(order: Order, trigger_pdf=True):
|
||||
return invoice
|
||||
|
||||
|
||||
@app.task(base=TransactionAwareTask, throws=(InvoiceNotReadyException,))
|
||||
@app.task(base=TransactionAwareTask)
|
||||
def invoice_pdf_task(invoice: int):
|
||||
with scopes_disabled():
|
||||
i = Invoice.objects.get(pk=invoice)
|
||||
|
||||
@@ -253,7 +253,8 @@ def reactivate_order(order: Order, force: bool=False, user: User=None, auth=None
|
||||
auth=auth,
|
||||
data={
|
||||
'value': position.price,
|
||||
'acceptor_id': order.event.organizer.id
|
||||
'acceptor_id': order.event.organizer.id,
|
||||
'acceptor_slug': order.event.organizer.slug
|
||||
}
|
||||
)
|
||||
break
|
||||
@@ -563,6 +564,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
|
||||
data={
|
||||
'value': -position.price,
|
||||
'acceptor_id': order.event.organizer.id,
|
||||
'acceptor_slug': order.event.organizer.slug
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2457,7 +2459,8 @@ class OrderChangeManager:
|
||||
auth=self.auth,
|
||||
data={
|
||||
'value': -position.price,
|
||||
'acceptor_id': self.order.event.organizer.id
|
||||
'acceptor_id': self.order.event.organizer.id,
|
||||
'acceptor_slug': self.order.event.organizer.slug
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2483,7 +2486,8 @@ class OrderChangeManager:
|
||||
auth=self.auth,
|
||||
data={
|
||||
'value': -opa.position.price,
|
||||
'acceptor_id': self.order.event.organizer.id
|
||||
'acceptor_id': self.order.event.organizer.id,
|
||||
'acceptor_slug': self.order.event.organizer.slug
|
||||
}
|
||||
)
|
||||
|
||||
@@ -3453,6 +3457,7 @@ def signal_listener_issue_giftcards(sender: Event, order: Order, **kwargs):
|
||||
data={
|
||||
'value': trans.value,
|
||||
'acceptor_id': order.event.organizer.id,
|
||||
'acceptor_slug': order.event.organizer.slug
|
||||
}
|
||||
)
|
||||
any_giftcards = True
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
<h1>{% trans "Not found" %}</h1>
|
||||
<p>{% trans "I'm afraid we could not find the the resource you requested." %}</p>
|
||||
<p>{{ exception }}</p>
|
||||
<p class="links">
|
||||
<a id='goback' href='#'>{% trans "Take a step back" %}</a>
|
||||
</p>
|
||||
{% if request.user.is_staff and not staff_session %}
|
||||
<form action="{% url 'control:user.sudo' %}?next={{ request.path|add:"?"|add:request.GET.urlencode|urlencode }}" method="post">
|
||||
<p>
|
||||
|
||||
@@ -1850,7 +1850,8 @@ class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
data={
|
||||
'value': value,
|
||||
'text': request.POST.get('text'),
|
||||
'acceptor_id': self.request.organizer.id
|
||||
'acceptor_id': self.request.organizer.id,
|
||||
'acceptor_slug': self.request.organizer.slug
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
@@ -1913,7 +1914,8 @@ class GiftCardCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
user=self.request.user,
|
||||
data={
|
||||
'value': form.cleaned_data['value'],
|
||||
'acceptor_id': self.request.organizer.id
|
||||
'acceptor_id': self.request.organizer.id,
|
||||
'acceptor_slug': self.request.organizer.slug
|
||||
}
|
||||
)
|
||||
return redirect(reverse(
|
||||
|
||||
@@ -34,7 +34,10 @@ def set_cookie_without_samesite(request, response, key, *args, **kwargs):
|
||||
if not is_secure:
|
||||
# https://www.chromestatus.com/feature/5633521622188032
|
||||
return
|
||||
if should_send_same_site_none(request.headers.get('User-Agent', '')):
|
||||
|
||||
useragent = request.headers.get('User-Agent', '')
|
||||
|
||||
if should_send_same_site_none(useragent):
|
||||
# Chromium is rolling out SameSite=Lax as a default
|
||||
# https://www.chromestatus.com/feature/5088147346030592
|
||||
# This however breaks all pretix-in-an-iframe things, such as the pretix Widget.
|
||||
@@ -44,8 +47,29 @@ def set_cookie_without_samesite(request, response, key, *args, **kwargs):
|
||||
# This will only work on secure cookies as well
|
||||
# https://www.chromestatus.com/feature/5633521622188032
|
||||
response.cookies[key]['secure'] = is_secure
|
||||
# CHIPS
|
||||
response.cookies[key]['Partitioned'] = True
|
||||
|
||||
if can_send_partitioned_cookie(useragent):
|
||||
# CHIPS
|
||||
response.cookies[key]['Partitioned'] = True
|
||||
|
||||
|
||||
def can_send_partitioned_cookie(useragent):
|
||||
# Safari currently exhibits a bug where Partitioned cookies (CHIPS) are not
|
||||
# sent back to the originating site after multi-hop cross-site redirects,
|
||||
# breaking SSO login flows in pretix.
|
||||
#
|
||||
# Partitioned cookies were initially introduced in Safari 18.4, removed
|
||||
# again in 18.5 due to a bug, and reintroduced in Safari 26.2, where the
|
||||
# current issue is present.
|
||||
#
|
||||
# Once the Safari issue is fixed, this check should be refined to be
|
||||
# conditional on the affected versions only.
|
||||
#
|
||||
# WebKit issues:
|
||||
#
|
||||
# - https://bugs.webkit.org/show_bug.cgi?id=292975
|
||||
# - https://bugs.webkit.org/show_bug.cgi?id=306194
|
||||
return not is_safari(useragent)
|
||||
|
||||
|
||||
# Based on https://www.chromium.org/updates/same-site/incompatible-clients
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
<dt>{% trans "Reference code (important):" %}</dt><dd><b>{{ code }}</b></dd>
|
||||
<dt>{% trans "Amount:" %}</dt><dd>{{ amount|money:event.currency }}</dd>
|
||||
{% if settings.bank_details_type == "sepa" %}
|
||||
<dt>{% trans "Account holder" %}:</dt><dd>{{ settings.bank_details_sepa_name }}</dt>
|
||||
<dt>{% trans "IBAN" %}:</dt><dd>{{ settings.bank_details_sepa_iban|ibanformat }}</dt>
|
||||
<dt>{% trans "BIC" %}:</dt><dd>{{ settings.bank_details_sepa_bic }}</dt>
|
||||
<dt>{% trans "Bank" %}:</dt><dd>{{ settings.bank_details_sepa_bank }}</dt>
|
||||
<dt>{% trans "Account holder" %}:</dt><dd>{{ settings.bank_details_sepa_name }}</dd>
|
||||
<dt>{% trans "IBAN" %}:</dt><dd>{{ settings.bank_details_sepa_iban|ibanformat }}</dd>
|
||||
<dt>{% trans "BIC" %}:</dt><dd>{{ settings.bank_details_sepa_bic }}</dd>
|
||||
<dt>{% trans "Bank" %}:</dt><dd>{{ settings.bank_details_sepa_bank }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
{% if details %}
|
||||
@@ -38,4 +38,4 @@
|
||||
{% if payment_qr_codes %}
|
||||
{% include "pretixpresale/event/payment_qr_codes.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1177,6 +1177,30 @@ def test_store_failed(token_client, organizer, clist, event, order):
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_store_failed_after_success(token_client, organizer, clist, event, order):
|
||||
with scopes_disabled():
|
||||
p = order.positions.first()
|
||||
p.all_checkins.create(
|
||||
type=Checkin.TYPE_ENTRY,
|
||||
nonce='foobar',
|
||||
successful=True,
|
||||
list=clist,
|
||||
raw_barcode=p.secret
|
||||
)
|
||||
resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/failed_checkins/'.format(
|
||||
organizer.slug, event.slug, clist.pk,
|
||||
), {
|
||||
'raw_barcode': p.secret,
|
||||
'nonce': 'foobar',
|
||||
'position': p.pk,
|
||||
'error_reason': 'unpaid'
|
||||
}, format='json')
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
assert Checkin.all.filter(position=p).count() == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_redeem_unknown(token_client, organizer, clist, event, order):
|
||||
resp = _redeem(token_client, organizer, clist, 'unknown_secret', {'force': True})
|
||||
|
||||
@@ -170,7 +170,7 @@ def test_price_mode_validation(event, item, user):
|
||||
import_vouchers.apply(
|
||||
args=(event.pk, inputfile_factory().id, settings, 'en', user.pk)
|
||||
).get()
|
||||
assert 'It is pointless to set a value without a price mode.' in str(excinfo.value)
|
||||
assert 'It is pointless to set a value without a price effect.' in str(excinfo.value)
|
||||
|
||||
settings['price_mode'] = 'static:percent'
|
||||
import_vouchers.apply(
|
||||
|
||||
Reference in New Issue
Block a user