Compare commits

...
Author SHA1 Message Date
Phin Wolkwitz c79644e002 [wip] Add migration to activate plugin, add plugin active check before run (23243588) 2026-09-22 18:58:07 +02:00
Raphael Michel a826b0a1bd Customer area: Hide password change link for SSO users (fixes #6572) (#6580) 2026-09-22 15:22:43 +02:00
Raphael Michel 7d7474c07d Refund form: Do not allow individual negative amounts (#6579) 2026-09-22 12:47:06 +02:00
Raphael Michel 58023381b9 Merge branch 'refund-offset-perm-check' into 'master'
Offset refund: Do not allow to offset to order without access to

See merge request pretix/pretix!52
2026-09-22 12:46:20 +02:00
Raphael Michel e5c44e7aed Offset refund: Do not allow to offset to order without access to 2026-09-22 12:46:20 +02:00
Raphael Michel d77a179606 2FA: Show info on throttled request (#6581)
* 2FA: Show info on throttled request

* Round up the time
2026-09-22 10:38:26 +02:00
Raphael Michel 29614db1e2 InvoiceShredder: Fix shredding of new fields (#6577)
* InvoiceShredder: Fix shredding of new fields

* Update src/pretix/base/migrations/0311_fix_unshredded_invoices.py
2026-09-22 10:37:58 +02:00
Raphael Michel 365051cc21 Event metadata: Fix server-side validation of write protection (#6578) 2026-09-22 10:00:15 +02:00
Raphael Michel 5c6b0eef6f Plugin configuration: Allow organizers to see all events (#6576) 2026-09-22 09:51:18 +02:00
Raphael Michel bc0a6b662b Checkout: Fix VAT ID revalidation after country change (#6575) 2026-09-22 09:51:10 +02:00
dependabot[bot]andRichard Schreiber 0bc1aed1a0 Update pypdf requirement from ==6.11.* to ==6.19.* (#6570)
* Update pypdf requirement from ==6.11.* to ==6.18.*

Updates the requirements on [pypdf](https://github.com/py-pdf/pypdf) to permit the latest version.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.11.0...6.18.1)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.18.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyproject.toml

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-09-22 09:38:28 +02:00
Rita Gimenez 0eaa5a081c Translations: Update Catalan
Currently translated at 29.4% (1892 of 6419 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ca/

powered by weblate
2026-09-21 14:56:27 +02:00
Nate Horst 688c434c4f Translations: Update Thai
Currently translated at 37.1% (2387 of 6419 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/th/

powered by weblate
2026-09-21 14:56:27 +02:00
dependabot[bot]andRaphael Michel c0474604a1 Update isort requirement from ==8.0.* to ==9.0.* (#6510)
* Update isort requirement from ==8.0.* to ==9.0.*

Updates the requirements on [isort](https://github.com/PyCQA/isort) to permit the latest version.
- [Release notes](https://github.com/PyCQA/isort/releases)
- [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PyCQA/isort/compare/8.0.0...9.0.1)

---
updated-dependencies:
- dependency-name: isort
  dependency-version: 9.0.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* Run isort

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raphael Michel <michel@pretix.eu>
2026-09-21 14:56:14 +02:00
dependabot[bot] 1741d08c2c Update cryptography requirement from >=50.0.0 to >=50.0.1 (#6509)
Updates the requirements on [cryptography](https://github.com/pyca/cryptography) to permit the latest version.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/50.0.0...50.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 50.0.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 14:14:06 +02:00
dependabot[bot] 6bbd808aaa Bump postcss-selector-parser from 7.1.1 to 7.1.5 (#6513)
Bumps [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser) from 7.1.1 to 7.1.5.
- [Release notes](https://github.com/postcss/postcss-selector-parser/releases)
- [Changelog](https://github.com/postcss/postcss-selector-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss-selector-parser/compare/v7.1.1...7.1.5)

---
updated-dependencies:
- dependency-name: postcss-selector-parser
  dependency-version: 7.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 14:13:46 +02:00
dependabot[bot] aa90c5b3b1 Bump @humanfs/node from 0.16.7 to 0.16.8 (#6517)
Bumps [@humanfs/node](https://github.com/humanwhocodes/humanfs/tree/HEAD/packages/node) from 0.16.7 to 0.16.8.
- [Release notes](https://github.com/humanwhocodes/humanfs/releases)
- [Changelog](https://github.com/humanwhocodes/humanfs/blob/main/packages/node/CHANGELOG.md)
- [Commits](https://github.com/humanwhocodes/humanfs/commits/node-v0.16.8/packages/node)

---
updated-dependencies:
- dependency-name: "@humanfs/node"
  dependency-version: 0.16.8
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 14:13:39 +02:00
dependabot[bot] bfbabe0e58 Update fakeredis requirement from ==2.37.* to ==2.38.* (#6563)
Updates the requirements on [fakeredis](https://github.com/cunla/fakeredis-py) to permit the latest version.
- [Release notes](https://github.com/cunla/fakeredis-py/releases)
- [Commits](https://github.com/cunla/fakeredis-py/compare/v2.37.0...v2.38.0)

---
updated-dependencies:
- dependency-name: fakeredis
  dependency-version: 2.38.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 14:08:32 +02:00
19 changed files with 475 additions and 177 deletions
+24 -10
View File
@@ -294,29 +294,43 @@
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/types": "^0.15.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
"integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.1",
"@humanfs/core": "^0.19.2",
"@humanfs/types": "^0.15.0",
"@humanwhocodes/retry": "^0.4.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/types": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -3380,9 +3394,9 @@
}
},
"node_modules/postcss-selector-parser": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz",
"integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==",
"dev": true,
"license": "MIT",
"dependencies": {
+4 -4
View File
@@ -33,7 +33,7 @@ dependencies = [
"bleach==6.4.*",
"celery==5.6.*",
"chardet==5.2.*",
"cryptography>=50.0.0",
"cryptography>=50.0.1",
"css-inline==0.21.*",
"defusedcsv>=3.0.0",
"dnspython==2.*",
@@ -84,7 +84,7 @@ dependencies = [
"pycountry",
"pycparser==3.0",
"pycryptodome==3.23.*",
"pypdf==6.11.*",
"pypdf==6.19.*",
"python-bidi==0.6.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*",
"pytz",
@@ -112,10 +112,10 @@ dev = [
"aiohttp==3.14.*",
"coverage",
"coveralls",
"fakeredis==2.37.*",
"fakeredis==2.38.*",
"flake8==7.3.*",
"freezegun",
"isort==8.0.*",
"isort==9.0.*",
"pep8-naming==0.15.*",
"potypo",
"pytest-asyncio>=1.4.0",
+7 -5
View File
@@ -1202,8 +1202,9 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
return field
def clean(self):
from pretix.base.addressvalidation import \
validate_address # local import to prevent impact on startup time
from pretix.base.addressvalidation import ( # local import to prevent impact on startup time
validate_address,
)
d = super().clean()
@@ -1444,8 +1445,9 @@ class BaseInvoiceAddressForm(forms.ModelForm):
self.fields['transmission_type'].widget.attrs['data-trigger-address-info'] = 'on'
def clean(self):
from pretix.base.addressvalidation import \
validate_address # local import to prevent impact on startup time
from pretix.base.addressvalidation import ( # local import to prevent impact on startup time
validate_address,
)
data = self.cleaned_data
@@ -1499,7 +1501,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
"vat_id": _("This field is required.")
})
if self.validate_vat_id and self.instance.vat_id_validated and 'vat_id' not in self.changed_data:
if self.validate_vat_id and self.instance.vat_id_validated and not any(v in self.changed_data for v in ('is_business', 'vat_id', 'country')):
pass # Skip re-validation if it is validated
elif self.validate_vat_id and vat_id_applicable:
try:
@@ -0,0 +1,49 @@
# Generated by Django 5.2.17 on 2026-09-21 11:30
import django.db.models.deletion
from django.db import migrations, models
def fix_unshredded_invoices(apps, _):
Invoice = apps.get_model("pretixbase", "Invoice")
InvoiceLine = apps.get_model("pretixbase", "InvoiceLine")
ignore_fields = (
# bool/int fields are not listed and skipped automatically
'prefix', 'invoice_no', 'full_invoice_no', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
'invoice_from_city', 'invoice_from_state', 'invoice_from_country', 'invoice_from_tax_id',
'invoice_from_vat_id', 'locale', 'payment_provider_stamp', 'footer_text', 'foreign_currency_display',
'foreign_currency_source', 'transmission_type', 'transmission_provider', 'transmission_status',
)
for i in Invoice.objects.filter(shredded=True):
for f in Invoice._meta.fields:
if f.name in ignore_fields:
continue
val = getattr(i, f.name, None)
if val and isinstance(val, str):
setattr(i, f.name, "")
elif val and isinstance(val, list): # jsonfield
setattr(i, f.name, [])
elif val and isinstance(val, dict): # jsonfield
setattr(i, f.name, {"_shredded": True})
i.save()
InvoiceLine.objects.filter(
attendee_name__isnull=False,
invoice__shredded=True
).update(attendee_name="")
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0310_question_valid_string_length_min"),
]
operations = [
migrations.RunPython(
fix_unshredded_invoices,
migrations.RunPython.noop,
),
]
+22 -10
View File
@@ -50,8 +50,8 @@ from pretix.api.serializers.order import (
from pretix.api.serializers.waitinglist import WaitingListSerializer
from pretix.base.i18n import LazyLocaleException
from pretix.base.models import (
CachedCombinedTicket, CachedTicket, Event, InvoiceAddress, OrderPayment,
OrderPosition, OrderRefund, OutgoingMail, QuestionAnswer,
CachedCombinedTicket, CachedTicket, Event, Invoice, InvoiceAddress,
OrderPayment, OrderPosition, OrderRefund, OutgoingMail, QuestionAnswer,
)
from pretix.base.services.invoices import invoice_pdf_task
from pretix.base.signals import register_data_shredders
@@ -598,18 +598,30 @@ class InvoiceShredder(BaseDataShredder):
def shred_data(self, progress_callback=None):
qs_i = self.event.invoices.filter(shredded=False)
total = qs_i.count()
ignore_fields = (
'prefix', 'invoice_no', 'full_invoice_no', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
'invoice_from_city', 'invoice_from_state', 'invoice_from_country', 'invoice_from_tax_id',
'invoice_from_vat_id', 'locale', 'payment_provider_stamp', 'footer_text', 'foreign_currency_display',
'foreign_currency_source', 'transmission_type', 'transmission_provider', 'transmission_status',
)
for i in _progress_helper(qs_i, progress_callback, 0, total):
if i.file:
i.file.delete()
i.shredded = True
i.introductory_text = ""
i.additional_text = ""
i.invoice_to = ""
i.payment_provider_text = ""
i.transmission_info = {"_shredded": True}
i.save()
i.lines.update(description="")
i.shredded = True
for f in Invoice._meta.fields:
if f.name in ignore_fields:
continue
val = getattr(i, f.name, None)
if val and isinstance(val, str):
setattr(i, f.name, "")
elif val and isinstance(val, list): # jsonfield
setattr(i, f.name, [])
elif val and isinstance(val, dict): # jsonfield
setattr(i, f.name, {"_shredded": True})
i.save()
i.lines.update(description="", attendee_name="")
class CachedTicketShredder(BaseDataShredder):
+2 -2
View File
@@ -400,10 +400,10 @@ class EventMetaValueForm(forms.ModelForm):
if self.disabled:
self.fields['value'].widget.attrs['readonly'] = 'readonly'
def clean_slug(self):
def clean_value(self):
if self.disabled:
return self.instance.value if self.instance else None
return self.cleaned_data['slug']
return self.cleaned_data['value']
class Meta:
model = EventMetaValue
+2 -2
View File
@@ -435,10 +435,10 @@ class SubEventMetaValueForm(forms.ModelForm):
if self.disabled:
self.fields['value'].widget.attrs['readonly'] = 'readonly'
def clean_slug(self):
def clean_value(self):
if self.disabled:
return self.instance.value if self.instance else None
return self.cleaned_data['slug']
return self.cleaned_data['value']
class Meta:
model = SubEventMetaValue
+41 -6
View File
@@ -35,6 +35,7 @@
import base64
import json
import logging
import math
import time
from urllib.parse import quote, urljoin, urlparse
@@ -50,11 +51,12 @@ from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.translation import gettext_lazy as _
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _, ngettext
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.views.generic import TemplateView
from django_otp import match_token
from django_otp import devices_for_user
from django_otp.plugins.otp_static.models import StaticDevice
from webauthn.helpers import generate_challenge
@@ -463,6 +465,7 @@ class Login2FAView(TemplateView):
token = request.POST.get('token', '').strip().replace(' ', '')
valid = False
retry_after = None
if 'webauthn_challenge' in self.request.session and token.startswith('{'):
challenge = self.request.session['webauthn_challenge']
@@ -518,12 +521,28 @@ class Login2FAView(TemplateView):
valid = True
break
else:
valid = match_token(self.user, token)
if isinstance(valid, StaticDevice):
with transaction.atomic():
for device in devices_for_user(self.user, for_verify=True):
if isinstance(device, StaticDevice) and len(token) < 12:
# If we enter a wrong TOTP token (which is 6 characters), do not even try if it is a valid
# emergency token, which will only "lock up" the StaticDevice due to the throttling plugin
# and just locks people out without security gain.
continue
if device.verify_token(token):
valid = True
break
elif hasattr(device, 'verify_is_allowed'):
verify_allowed, reason_dict = device.verify_is_allowed()
if not verify_allowed:
if not retry_after or reason_dict['locked_until'] > retry_after:
retry_after = reason_dict['locked_until']
else:
device = None
if isinstance(device, StaticDevice):
self.user.send_security_notice([
_("A recovery code for two-factor authentification was used to log in.")
])
if valid:
logger.info(f"Backend login successful for user {self.user.pk} with 2FA.")
pretix_successful_logins.inc(1)
@@ -536,7 +555,23 @@ class Login2FAView(TemplateView):
return redirect('control:index')
else:
pretix_failed_logins.inc(1, reason="2fa")
messages.error(request, _('Invalid code, please try again.'))
msg = _('Invalid code, please try again.')
if retry_after:
seconds = (retry_after - now()).total_seconds()
minutes = seconds / 60
if minutes >= 1:
msg = ngettext(
'Invalid code. Please try again after waiting {value} minute.',
'Invalid code. Please try again after waiting {value} minutes.',
minutes,
).format(value=math.ceil(minutes))
elif seconds >= 1:
msg = ngettext(
'Invalid code. Please try again after waiting {value} second.',
'Invalid code. Please try again after waiting {value} seconds.',
seconds,
).format(value=math.ceil(seconds))
messages.error(request, msg)
return redirect('control:auth.login.2fa')
def get_context_data(self, **kwargs):
+20 -4
View File
@@ -1177,6 +1177,8 @@ class OrderRefundView(OrderView):
manual_value = formats.sanitize_separators(manual_value)
try:
manual_value = Decimal(manual_value)
if manual_value < Decimal("0.00"):
raise TypeError("Please do not use negative numbers")
except (DecimalException, TypeError):
messages.error(self.request, _('You entered an invalid number.'))
is_valid = False
@@ -1206,6 +1208,8 @@ class OrderRefundView(OrderView):
giftcard_value = formats.sanitize_separators(giftcard_value)
try:
giftcard_value = Decimal(giftcard_value)
if giftcard_value < Decimal("0.00"):
raise TypeError("Please do not use negative numbers")
except (DecimalException, TypeError):
messages.error(self.request, _('You entered an invalid number.'))
is_valid = False
@@ -1255,6 +1259,8 @@ class OrderRefundView(OrderView):
offsetting_value = formats.sanitize_separators(offsetting_value)
try:
offsetting_value = Decimal(offsetting_value)
if offsetting_value < Decimal("0.00"):
raise TypeError("Please do not use negative numbers")
except (DecimalException, TypeError):
messages.error(self.request, _('You entered an invalid number.'))
is_valid = False
@@ -1271,6 +1277,9 @@ class OrderRefundView(OrderView):
if offset_order.event.currency != self.request.event.currency:
messages.error(self.request, _('You entered an order in an event with a different currency.'))
is_valid = False
if not self.request.user.has_event_permission(self.request.organizer, offset_order.event, 'event.orders:write', request=self.request):
messages.error(self.request, _('You entered an order in an event that you do not have access to.'))
is_valid = False
refunds.append(OrderRefund(
order=order,
payment=None,
@@ -1286,10 +1295,13 @@ class OrderRefundView(OrderView):
))
for identifier, prov in self.request.event.get_payment_providers().items():
# prof = process form, not a typo for prov(ider)
prof_value = self.request.POST.get(f'newrefund-{identifier}', '0') or '0'
prof_value = formats.sanitize_separators(prof_value)
try:
prof_value = Decimal(prof_value)
if prof_value < Decimal("0.00"):
raise TypeError("Please do not use negative numbers")
except (DecimalException, TypeError):
messages.error(self.request, _('You entered an invalid number.'))
is_valid = False
@@ -1313,6 +1325,8 @@ class OrderRefundView(OrderView):
value = formats.sanitize_separators(value)
try:
value = Decimal(value)
if value < Decimal("0.00"):
raise TypeError("Please do not use negative numbers")
except (DecimalException, TypeError):
messages.error(self.request, _('You entered an invalid number.'))
is_valid = False
@@ -1342,7 +1356,12 @@ class OrderRefundView(OrderView):
))
any_success = False
if refund_selected == full_refund and is_valid:
if refund_selected != full_refund:
messages.error(self.request, _('The refunds you selected do not match the selected total refund '
'amount.'))
is_valid = False
if is_valid:
for r in refunds:
r.save()
order.log_action('pretix.event.order.refund.created', {
@@ -1414,9 +1433,6 @@ class OrderRefundView(OrderView):
)
}))
return redirect(self.get_order_url())
else:
messages.error(self.request, _('The refunds you selected do not match the selected total refund '
'amount.'))
def post(self, *args, **kwargs):
if self.start_form.is_valid():
+3 -3
View File
@@ -778,9 +778,9 @@ class OrganizerPluginEvents(OrganizerDetailViewMixin, OrganizerPermissionRequire
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["events"] = self.request.user.get_events_with_permission(
"event.settings.general:write", request=self.request
).filter(organizer=self.request.organizer)
# Assumption: Who has access to modify organizer settings may see all events and disable/enable plugins
# for them. Otherwise, inconsistent situations occur.
kwargs["events"] = self.request.organizer.events.all()
kwargs["initial"] = {
"events": self.request.organizer.events.filter(plugins__regex='(^|,)' + self.plugin.module + '(,|$)')
}
+5
View File
@@ -1276,6 +1276,11 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie
self._default_meta = self.request.event.meta_data
for p in self.request.organizer.meta_properties.all():
if p.protected and not self.request.user.has_organizer_permission(
self.request.organizer, 'organizer.settings.general:write', request=self.request
):
continue
inst = SubEventMetaValue(property=p)
if len(matches[p.id]) == 1 and matches[p.id][0]['c'] == total:
inst.value = matches[p.id][0]['value']
+30 -23
View File
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
"PO-Revision-Date: 2026-06-25 14:00+0000\n"
"Last-Translator: Kim Lozano <joaquim.lozano@upc.edu>\n"
"PO-Revision-Date: 2026-09-20 14:00+0000\n"
"Last-Translator: Rita Gimenez <barcelonamusictech@gmail.com>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix/"
"ca/>\n"
"Language: ca\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 2026.6.1\n"
"X-Generator: Weblate 2026.9.1\n"
#: pretix/_base_settings.py
msgid "English"
@@ -37,11 +37,11 @@ msgstr "Àrab"
#: pretix/_base_settings.py
msgid "Basque"
msgstr ""
msgstr "Basc"
#: pretix/_base_settings.py
msgid "Catalan"
msgstr ""
msgstr "Català"
#: pretix/_base_settings.py
msgid "Chinese (simplified)"
@@ -49,11 +49,11 @@ msgstr "Xinès (simplificat)"
#: pretix/_base_settings.py
msgid "Chinese (traditional)"
msgstr ""
msgstr "Xinès (tradicional)"
#: pretix/_base_settings.py
msgid "Czech"
msgstr ""
msgstr "Txec"
#: pretix/_base_settings.py
msgid "Croatian"
@@ -81,7 +81,7 @@ msgstr "Finlandès"
#: pretix/_base_settings.py
msgid "Galician"
msgstr ""
msgstr "Gallec"
#: pretix/_base_settings.py
msgid "Greek"
@@ -89,15 +89,15 @@ msgstr "Grec"
#: pretix/_base_settings.py
msgid "Hebrew"
msgstr ""
msgstr "Hebreu"
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
msgstr "Hongarès"
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr ""
msgstr "Indonesi"
#: pretix/_base_settings.py
msgid "Italian"
@@ -105,7 +105,7 @@ msgstr "Italià"
#: pretix/_base_settings.py
msgid "Japanese"
msgstr ""
msgstr "Japonès"
#: pretix/_base_settings.py
msgid "Latvian"
@@ -113,7 +113,7 @@ msgstr "Letó"
#: pretix/_base_settings.py
msgid "Norwegian Bokmål"
msgstr ""
msgstr "Noruec"
#: pretix/_base_settings.py
msgid "Polish"
@@ -129,7 +129,7 @@ msgstr "Portuguès (Brasil)"
#: pretix/_base_settings.py
msgid "Romanian"
msgstr ""
msgstr "Romanès"
#: pretix/_base_settings.py
msgid "Russian"
@@ -137,11 +137,11 @@ msgstr "Rus"
#: pretix/_base_settings.py
msgid "Slovak"
msgstr ""
msgstr "Eslovac"
#: pretix/_base_settings.py
msgid "Swedish"
msgstr ""
msgstr "Suec"
#: pretix/_base_settings.py
msgid "Spanish"
@@ -149,11 +149,11 @@ msgstr "Espanyol"
#: pretix/_base_settings.py
msgid "Spanish (Latin America)"
msgstr ""
msgstr "Espanyol (Llatinoamèrica)"
#: pretix/_base_settings.py
msgid "Thai"
msgstr ""
msgstr "Tailandès"
#: pretix/_base_settings.py
msgid "Turkish"
@@ -161,13 +161,15 @@ msgstr "Turc"
#: pretix/_base_settings.py
msgid "Ukrainian"
msgstr ""
msgstr "Ucraïnès"
#: pretix/api/auth/devicesecurity.py
msgid ""
"Full device access (reading and changing orders and gift cards, reading of "
"products and settings)"
msgstr ""
"Accés total al dispositiu (lectura i modificació de comandes i targetes "
"regal, lectura de productes i configuració)"
#: pretix/api/auth/devicesecurity.py
msgid "pretixSCAN"
@@ -309,7 +311,7 @@ msgstr "La cistella és buida."
#: pretix/api/serializers/item.py
msgid "The program end must not be empty."
msgstr ""
msgstr "El final del programa no ha de ser buit."
#: pretix/api/serializers/item.py pretix/base/models/items.py
#, fuzzy
@@ -339,7 +341,7 @@ msgstr ""
#: pretix/api/serializers/item.py
msgid "Only admission products can currently be personalized."
msgstr ""
msgstr "Actualment, només es poden personalitzar els productes d'accés."
#: pretix/api/serializers/item.py
msgid ""
@@ -434,7 +436,7 @@ msgstr ""
#: pretix/api/serializers/organizer.py pretix/control/views/organizer.py
#, python-format
msgid "You've been invited to join %(organizer)s"
msgstr ""
msgstr "Has estat convidat/da a unir-te a %(organizer)s"
#: pretix/api/serializers/organizer.py pretix/control/views/organizer.py
msgid "This user already has been invited for this team."
@@ -456,7 +458,7 @@ msgstr ""
#: pretix/api/views/checkin.py
msgid "Medium connected to other event"
msgstr ""
msgstr "Mitjà connectat a un altre esdeveniment"
#: pretix/api/views/checkin.py
#, fuzzy
@@ -642,6 +644,8 @@ msgid ""
"This includes product added or deleted and changes to nested objects like "
"variations or bundles."
msgstr ""
"Això inclou productes afegits o eliminats i canvis en objectes imbricats, "
"com ara variacions o lots."
#: pretix/api/webhooks.py
#, fuzzy
@@ -654,6 +658,9 @@ msgid ""
"This includes related events like creation, deletion, opening or closing of "
"quotas. No webhook is sent for changes to the resulting availability."
msgstr ""
"Això inclou esdeveniments relacionats, com ara la creació, l'eliminació, "
"l'obertura o el tancament de quotes. No s'envia cap webhook per als canvis "
"en la disponibilitat resultant."
#: pretix/api/webhooks.py
#, fuzzy
+129 -100
View File
@@ -8,16 +8,16 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
"PO-Revision-Date: 2026-05-20 10:58+0000\n"
"Last-Translator: Phumraphee Sae-tang <phumraphee@gmail.com>\n"
"Language-Team: Thai <https://translate.pretix.eu/projects/pretix/pretix/th/"
">\n"
"PO-Revision-Date: 2026-09-19 13:00+0000\n"
"Last-Translator: Nate Horst <nate@agcthailand.org>\n"
"Language-Team: Thai <https://translate.pretix.eu/projects/pretix/pretix/th/>"
"\n"
"Language: th\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 2026.5\n"
"X-Generator: Weblate 2026.9.1\n"
#: pretix/_base_settings.py
msgid "English"
@@ -17264,7 +17264,7 @@ msgstr ""
#: pretix/control/navigation.py
#: pretix/control/templates/pretixcontrol/dashboard.html
msgid "Dashboard"
msgstr ""
msgstr "แผงควบคุม"
#: pretix/control/navigation.py
#: pretix/control/templates/pretixcontrol/checkin/list_edit.html
@@ -17278,7 +17278,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/mail.html
#: pretix/control/templates/pretixcontrol/organizers/property_edit.html
msgid "General"
msgstr ""
msgstr "ทั่วไป"
#: pretix/control/navigation.py
#: pretix/control/templates/pretixcontrol/event/quick_setup.html
@@ -17290,7 +17290,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/checkout_payment.html
#: pretix/presale/templates/pretixpresale/event/order.html
msgid "Payment"
msgstr ""
msgstr "การชำระเงิน"
#: pretix/control/navigation.py pretix/control/views/event.py
#: pretix/control/views/subevents.py
@@ -17300,18 +17300,18 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html
#: pretix/presale/templates/pretixpresale/organizers/index.html
msgid "Tickets"
msgstr ""
msgstr "ตั๋ว"
#: pretix/control/navigation.py
#: pretix/control/templates/pretixcontrol/event/tax.html
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
msgid "Taxes"
msgstr ""
msgstr "ภาษี"
#: pretix/control/navigation.py
msgid "Invoicing"
msgstr ""
msgstr "การออกใบแจ้งหนี้"
#: pretix/control/navigation.py
msgctxt "action"
@@ -21506,19 +21506,19 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/oauth/authorized.html
msgid "Permissions"
msgstr ""
msgstr "สิทธิ์การใช้งาน"
#: pretix/control/templates/pretixcontrol/oauth/authorized.html
msgid "No applications have access to your pretix account."
msgstr ""
msgstr "ไม่มีแอปพลิเคชันใดเข้าถึงบัญชี pretix ของคุณ"
#: pretix/control/templates/pretixcontrol/order/approve.html
msgid "Approve order"
msgstr ""
msgstr "อนุมัติคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/approve.html
msgid "Do you really want to approve this order?"
msgstr ""
msgstr "คุณแน่ใจหรือไม่ว่าต้องการอนุมัติคำสั่งซื้อนี้?"
#: pretix/control/templates/pretixcontrol/order/approve.html
#: pretix/control/templates/pretixcontrol/order/cancel.html
@@ -21529,29 +21529,32 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/order/refund_cancel.html
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "No, take me back"
msgstr ""
msgstr "ไม่, ย้อนกลับ"
#: pretix/control/templates/pretixcontrol/order/approve.html
msgid "Yes, approve order"
msgstr ""
msgstr "ใช่, อนุมัติคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/cancel.html
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/presale/templates/pretixpresale/event/order.html
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "Cancel order"
msgstr ""
msgstr "ยกเลิกคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/cancel.html
#: pretix/control/templates/pretixcontrol/order/deny.html
msgid "Do you really want to cancel this order? You cannot revert this action."
msgstr ""
"คุณแน่ใจหรือไม่ว่าต้องการยกเลิกคำสั่งซื้อนี้? การดำเนินการนี้ไม่สามารถย้อนกลับได้"
#: pretix/control/templates/pretixcontrol/order/cancel.html
msgid ""
"This will <strong>not</strong> automatically transfer the money back, but "
"you will be offered options to refund the payment afterwards."
msgstr ""
"การดำเนินการนี้จะ <strong>ไม่</strong>คืนเงินโดยอัตโนมัติ แต่คุณจะมีตัวเลือกในการคืนเงินหลังจา"
"กนี้"
#: pretix/control/templates/pretixcontrol/order/cancel.html
#, python-format
@@ -21560,15 +21563,17 @@ msgid ""
"%(fee)s for this order, but for a cancellation performed by you, you need to "
"set the cancellation fee here:"
msgstr ""
"ค่าธรรมเนียมการยกเลิกที่ตั้งค่าไว้สำหรับการยกเลิกด้วยตนเองจะอยู่ที่ %(fee)s สำหรับคำสั่งซื้อนี้ แต่สำ"
"หรับการยกเลิกที่ดำเนินการโดยคุณ คุณจะต้องกำหนดค่าธรรมเนียมการยกเลิกที่นี่:"
#: pretix/control/templates/pretixcontrol/order/cancel.html
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "Yes, cancel order"
msgstr ""
msgstr "ใช่, ยกเลิกคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/cancellation_request_delete.html
msgid "Ignore cancellation request"
msgstr ""
msgstr "เพิกเฉยต่อคำขอยกเลิก"
#: pretix/control/templates/pretixcontrol/order/cancellation_request_delete.html
msgid ""
@@ -21576,6 +21581,8 @@ msgid ""
"informed automatically, but you will have the option to email them "
"individually in the next step."
msgstr ""
"คุณแน่ใจหรือไม่ว่าต้องการลบคำขอยกเลิกนี้? ผู้ใช้จะไม่ได้รับการแจ้งเตือนโดยอัตโนมัติ แต่คุณจะมีตัวเลือก"
"ในการส่งอีเมลถึงพวกเขาโดยตรงในขั้นตอนถัดไป"
#: pretix/control/templates/pretixcontrol/order/cancellation_request_delete.html
msgid "Yes, delete request"
@@ -21753,24 +21760,24 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/order_change_confirm.html
#: pretix/presale/templates/pretixpresale/event/position_change_confirm.html
msgid "Perform changes"
msgstr ""
msgstr "ดำเนินการเปลี่ยนแปลง"
#: pretix/control/templates/pretixcontrol/order/change_contact.html
#: pretix/control/templates/pretixcontrol/order/change_questions.html
msgid "Change contact information"
msgstr ""
msgstr "เปลี่ยนแปลงข้อมูลการติดต่อ"
#: pretix/control/templates/pretixcontrol/order/change_locale.html
msgid "Change locale information"
msgstr ""
msgstr "เปลี่ยนแปลงข้อมูลภาษาและภูมิภาค"
#: pretix/control/templates/pretixcontrol/order/change_locale.html
msgid "This language will be used whenever emails are sent to the users."
msgstr ""
msgstr "ภาษานี้จะถูกใช้เมื่อใดก็ตามที่มีการส่งอีเมลไปยังผู้ใช้"
#: pretix/control/templates/pretixcontrol/order/change_questions.html
msgid "Change order information"
msgstr ""
msgstr "เปลี่ยนแปลงข้อมูลคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/change_questions.html
#: pretix/control/templates/pretixcontrol/order/index.html
@@ -21778,13 +21785,13 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/checkout_questions.html
#: pretix/presale/templates/pretixpresale/event/order_modify.html
msgid "Invoice information"
msgstr ""
msgstr "ข้อมูลใบแจ้งหนี้"
#: pretix/control/templates/pretixcontrol/order/change_questions.html
#: pretix/presale/templates/pretixpresale/event/checkout_questions.html
#: pretix/presale/templates/pretixpresale/event/order_modify.html
msgid "(optional)"
msgstr ""
msgstr "(ระบุหรือไม่ก็ได้)"
#: pretix/control/templates/pretixcontrol/order/change_questions.html
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
@@ -21797,17 +21804,19 @@ msgstr "ต้องการข้อมูลเพิ่มเติม"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr ""
msgstr "ลบคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid ""
"Do you really want to delete this order? <strong>You really cannot revert "
"this action and we can't either.</strong>"
msgstr ""
"คุณแน่ใจหรือไม่ว่าต้องการลบคำสั่งซื้อนี้? <strong>คุณจะไม่สามารถย้อนกลับการดำเนินการนี้ได้จริงๆ แล"
"ะเราก็ไม่สามารถย้อนกลับได้เช่นกัน</strong>"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Yes, delete order"
msgstr ""
msgstr "ใช่, ลบคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/deny.html
msgid "Deny order"
@@ -22256,12 +22265,13 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/order/refund_cancel.html
msgid "Cancel refund"
msgstr ""
msgstr "ยกเลิกการคืนเงิน"
#: pretix/control/templates/pretixcontrol/order/refund_cancel.html
msgid ""
"Do you really want to cancel this refund? You cannot revert this action."
msgstr ""
"คุณแน่ใจหรือไม่ว่าต้องการยกเลิกการคืนเงินนี้? การดำเนินการนี้ไม่สามารถย้อนกลับได้"
#: pretix/control/templates/pretixcontrol/order/refund_cancel.html
msgid ""
@@ -22269,19 +22279,22 @@ msgid ""
"will just mark this transfer as aborted in pretix. This will also not "
"reactivate the order, it will just allow you to choose a new refund method."
msgstr ""
"หากอยู่ในระหว่างการโอนเงินคืนแล้ว การดำเนินการนี้จะไม่สามารถระงับการโอนเงินได้ แต่จะเป็นเพียง"
"การทำเครื่องหมายว่ารายการโอนนี้ถูกยกเลิกใน pretix เท่านั้น ทั้งนี้ จะไม่เป็นการเปิดใช้งานคำสั่งซื้อ"
"อีกครั้ง แต่จะเปิดโอกาสให้คุณเลือกวิธีการคืนเงินรูปแบบใหม่ได้"
#: pretix/control/templates/pretixcontrol/order/refund_cancel.html
msgid "Yes, cancel refund"
msgstr ""
msgstr "ใช่, ยกเลิกการคืนเงิน"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
#: pretix/control/templates/pretixcontrol/order/refund_start.html
msgid "Refund order"
msgstr ""
msgstr "คืนเงินคำสั่งซื้อ"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "How should the refund be sent?"
msgstr ""
msgstr "ต้องการส่งเงินคืนด้วยวิธีใด?"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid ""
@@ -22290,38 +22303,41 @@ msgid ""
"created as pending refunds, which you can later mark as done once you have "
"actually transferred the money back to the customer."
msgstr ""
"รายการชำระเงินใดที่คุณเลือกเป็นการคืนเงินอัตโนมัติ คำขอคืนเงินจะถูกส่งไปยังผู้ให้บริการชำระเงินที่เกี่"
"ยวข้องทันที ส่วนการคืนเงินด้วยตนเองจะถูกสร้างเป็นรายการคืนเงินที่รอดำเนินการ ซึ่งคุณสามารถทำเครื่"
"องหมายว่าเสร็จสิ้นได้ในภายหลัง หลังจากที่ได้โอนเงินคืนให้ลูกค้าเรียบร้อยแล้ว"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "Refund to original payment method"
msgstr ""
msgstr "คืนเงินไปยังช่องทางชำระเงินเดิม"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "Amount not refunded"
msgstr ""
msgstr "จำนวนเงินที่ไม่ได้รับการคืน"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "Refund amount"
msgstr ""
msgstr "จำนวนเงินที่คืน"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "Full amount"
msgstr ""
msgstr "จำนวนเงินเต็ม"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "This payment method does not support automatic refunds."
msgstr ""
msgstr "วิธีการชำระเงินนี้ไม่รองรับการคืนเงินอัตโนมัติ"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "Refund to a different payment method"
msgstr ""
msgstr "คืนเงินไปยังช่องทางชำระเงินอื่น"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "Recipient / options"
msgstr ""
msgstr "ผู้รับ / ตัวเลือก"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
msgid "Transfer to other order"
msgstr ""
msgstr "โอนไปยังคำสั่งซื้ออื่น"
#: pretix/control/templates/pretixcontrol/order/refund_choose.html
#: pretix/control/templates/pretixcontrol/organizers/giftcard_create.html
@@ -31172,48 +31188,48 @@ msgstr ""
#: pretix/presale/forms/renderers.py
msgctxt "form"
msgid "is valid"
msgstr ""
msgstr "ใช้ได้"
#: pretix/presale/forms/renderers.py
msgctxt "form"
msgid "has errors"
msgstr ""
msgstr "มีข้อผิดพลาด"
#: pretix/presale/forms/renderers.py
#: pretix/presale/templates/pretixpresale/event/fragment_voucher_form.html
msgctxt "form"
msgid "required"
msgstr ""
msgstr "จำเป็นต้องระบุ"
#: pretix/presale/ical.py
#, python-brace-format
msgid "Tickets: {url}"
msgstr ""
msgstr "ตั๋ว: {url}"
#: pretix/presale/ical.py
#, python-brace-format
msgid "Admission: {datetime}"
msgstr ""
msgstr "เวลาประตูเปิด: {datetime}"
#: pretix/presale/ical.py
#, python-brace-format
msgid "Organizer: {organizer}"
msgstr ""
msgstr "ผู้จัดงาน: {organizer}"
#: pretix/presale/ical.py
#, python-brace-format
msgid "{event} - {item}"
msgstr ""
msgstr "{event} - {item}"
#: pretix/presale/ical.py
#, python-brace-format
msgid "Start: {datetime}"
msgstr ""
msgstr "เริ่มต้น: {datetime}"
#: pretix/presale/ical.py
#, python-brace-format
msgid "End: {datetime}"
msgstr ""
msgstr "สิ้นสุด: {datetime}"
#: pretix/presale/templates/pretixpresale/base.html
msgctxt "skip-to-main-nav"
@@ -31355,23 +31371,23 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/organizers/customer_address_delete.html
#: pretix/presale/templates/pretixpresale/organizers/customer_profile_delete.html
msgid "Go back"
msgstr ""
msgstr "ย้อนกลับ"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html
#, python-format
msgid "Step %(current)s of %(total)s: %(label)s"
msgstr ""
msgstr "ขั้นตอนที่ %(current)s จาก %(total)s: %(label)s"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html
msgid "Checkout"
msgstr ""
msgstr "ชำระเงิน"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
#: pretix/presale/templates/pretixpresale/event/fragment_cart_box.html
msgid "Your cart"
msgstr ""
msgstr "ตระกร้าของคุณ"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
@@ -31381,36 +31397,36 @@ msgstr "ตะกร้าสินค้าหมดเวลาแล้ว"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html
msgid "Show full cart"
msgstr ""
msgstr "แสดงตะกร้าสินค้าทั้งหมด"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html
#: pretix/presale/templates/pretixpresale/event/index.html
msgid "Add tickets for a different date"
msgstr ""
msgstr "เพิ่มตั๋วสำหรับวันที่อื่น"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
msgid "Review order"
msgstr ""
msgstr "ตรวจสอบคำสั่งซื้อ"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
msgid "Please review the details below and confirm your order."
msgstr ""
msgstr "โปรดตรวจสอบรายละเอียดด้านล่างและยืนยันคำสั่งซื้อของคุณ"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
msgid "Add or remove tickets"
msgstr ""
msgstr "เพิ่มหรือลบตั๋ว"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
msgid "Please hang tight, we're finalizing your order!"
msgstr ""
msgstr "โปรดรอสักครู่ ระบบกำลังดำเนินการสรุปคำสั่งซื้อของคุณ!"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
msgid "Modify payment"
msgstr ""
msgstr "แก้ไขการชำระเงิน"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
msgid "Modify"
msgstr ""
msgstr "แก้ไข"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
msgid "Modify invoice information"
@@ -31901,8 +31917,7 @@ msgstr ""
#, python-format
msgid "One product"
msgid_plural "%(num)s products"
msgstr[0] ""
msgstr[1] ""
msgstr[0] "%(num)s รายการสินค้า"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
#, python-format
@@ -32442,31 +32457,35 @@ msgid ""
"your order later. We also sent you an email to the address you specified "
"containing the link to this page."
msgstr ""
"โปรดบุ๊กมาร์กหรือบันทึกลิงก์ของหน้านี้ไว้ หากคุณต้องการเข้าถึงคำสั่งซื้อของคุณในภายหลัง นอกจากนี้ เรา"
"ได้ส่งอีเมลพร้อมลิงก์สำหรับเข้าถึงหน้านี้ไปยังที่อยู่อีเมลที่คุณระบุไว้แล้ว"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid ""
"Please save the following link if you want to access your order later. We "
"also sent you an email to the address you specified containing the link."
msgstr ""
"โปรดบันทึกลิงก์ต่อไปนี้ไว้ หากคุณต้องการเข้าถึงคำสั่งซื้อของคุณในภายหลัง นอกจากนี้ เราได้ส่งอีเมลพร้อ"
"มลิงก์ไปยังที่อยู่อีเมลที่คุณระบุไว้แล้ว"
#: pretix/presale/templates/pretixpresale/event/order.html
#: pretix/presale/templates/pretixpresale/event/position.html
msgid "View in backend"
msgstr ""
msgstr "ดูในระบบหลังบ้าน"
#: pretix/presale/templates/pretixpresale/event/order.html
#, python-format
msgid "A payment of %(total)s is still pending for this order."
msgstr ""
msgstr "คำสั่งซื้อนี้ยังมียอดคงค้างชำระอยู่อีก %(total)s"
#: pretix/presale/templates/pretixpresale/event/order.html
#, python-format
msgid "Please complete your payment before %(date)s"
msgstr ""
msgstr "โปรดชำระเงินให้เสร็จสิ้นก่อนวันที่ %(date)s"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid "Re-try payment or choose another payment method"
msgstr ""
msgstr "ลองชำระเงินอีกครั้ง หรือเลือกวิธีชำระเงินอื่น"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid ""
@@ -32510,22 +32529,22 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/order.html
#: pretix/presale/templates/pretixpresale/event/position.html
msgid "Change ordered items"
msgstr ""
msgstr "เปลี่ยนแปลงรายการสินค้าที่สั่งซื้อ"
#: pretix/presale/templates/pretixpresale/event/order.html
#: pretix/presale/templates/pretixpresale/event/position.html
msgid "Change details"
msgstr ""
msgstr "เปลี่ยนแปลงรายละเอียด"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid ""
"You need to select a payment method above before you can request an invoice."
msgstr ""
msgstr "คุณต้องเลือกวิธีการชำระเงินด้านบนก่อน จึงจะสามารถขอใบแจ้งหนี้ได้"
#: pretix/presale/templates/pretixpresale/event/order.html
#: pretix/presale/templates/pretixpresale/event/order_modify.html
msgid "Request invoice"
msgstr ""
msgstr "ขอใบแจ้งหนี้"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid "Your information"
@@ -32533,41 +32552,42 @@ msgstr "ข้อมูลของคุณ"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid "Change your information"
msgstr ""
msgstr "เปลี่ยนแปลงข้อมูลของคุณ"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid "Internal Reference"
msgstr ""
msgstr "รหัสอ้างอิงภายใน"
#: pretix/presale/templates/pretixpresale/event/order.html
msgctxt "action"
msgid "Change or cancel your order"
msgstr ""
msgstr "เปลี่ยนแปลงหรือยกเลิกคำสั่งซื้อของคุณ"
#: pretix/presale/templates/pretixpresale/event/order.html
msgctxt "action"
msgid "Change your order"
msgstr ""
msgstr "เปลี่ยนแปลงคำสั่งซื้อของคุณ"
#: pretix/presale/templates/pretixpresale/event/order.html
msgctxt "action"
msgid "Cancel your order"
msgstr ""
msgstr "ยกเลิกคำสั่งซื้อของคุณ"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid ""
"If you want to make changes to the products you bought, you can click on the "
"button to change your order."
msgstr ""
"หากคุณต้องการเปลี่ยนแปลงสินค้าที่ซื้อไว้ คุณสามารถคลิกที่ปุ่มเพื่อเปลี่ยนแปลงคำสั่งซื้อของคุณได้"
#: pretix/presale/templates/pretixpresale/event/order.html
msgid "Change order"
msgstr ""
msgstr "เปลี่ยนแปลงคำสั่งซื้อ"
#: pretix/presale/templates/pretixpresale/event/order.html
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "You can request to cancel this order."
msgstr ""
msgstr "คุณสามารถยื่นขอยกเลิกคำสั่งซื้อนี้ได้"
#: pretix/presale/templates/pretixpresale/event/order.html
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
@@ -32635,17 +32655,17 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/order.html
msgid "You can cancel this order using the following button."
msgstr ""
msgstr "คุณสามารถยกเลิกคำสั่งซื้อนี้ได้โดยใช้ปุ่มต่อไปนี้"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
#, python-format
msgid "Request cancellation: %(code)s"
msgstr ""
msgstr "ยื่นขอยกเลิก: %(code)s"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
#, python-format
msgid "Cancel order: %(code)s"
msgstr ""
msgstr "ยกเลิกคำสั่งซื้อ: %(code)s"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid ""
@@ -32653,12 +32673,16 @@ msgid ""
"organizer will then decide on your request. If they approve, your order will "
"be canceled and all tickets will be invalidated."
msgstr ""
"คุณสามารถยื่นขอยกเลิกคำสั่งซื้อของคุณได้ในหน้านี้ โดยผู้จัดงานจะพิจารณาคำขอของคุณ หากคำขอได้รับกา"
"รอนุมัติ คำสั่งซื้อของคุณจะถูกยกเลิก และตั๋วทั้งหมดจะถูกยกเลิกใช้งาน"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid ""
"If you cancel this order, all tickets will be invalidated and you can no "
"longer use them. You cannot revert this action."
msgstr ""
"หากคุณยกเลิกคำสั่งซื้อนี้ ตั๋วทั้งหมดจะถูกยกเลิกใช้งาน และคุณจะไม่สามารถใช้งานตั๋วได้อีกต่อไป การดำเ"
"นินการนี้ไม่สามารถย้อนกลับได้"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
#, python-format
@@ -32666,97 +32690,102 @@ msgid ""
"If you want, you can request a refund for the full amount minus a "
"cancellation fee of %(fee)s."
msgstr ""
"หากคุณต้องการ คุณสามารถยื่นขอคืนเงินเต็มจำนวนโดยหักค่าธรรมเนียมการยกเลิก %(fee)s"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "If you want, you can request a full refund."
msgstr ""
msgstr "หากคุณต้องการ คุณสามารถยื่นขอคืนเงินเต็มจำนวนได้"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "Enter custom amount"
msgstr ""
msgstr "ระบุจำนวนเงินเอง"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "Refund amount:"
msgstr ""
msgstr "จำนวนเงินที่คืน:"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
#, python-format
msgid "Your gift card will be valid until %(expiry_date)s."
msgstr ""
msgstr "บัตรของขวัญของคุณจะสามารถใช้งานได้จนถึงวันที่ %(expiry_date)s"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "I want the refund as a gift card for later purchases"
msgstr ""
msgstr "ฉันต้องการรับเงินคืนเป็นบัตรของขวัญสำหรับการซื้อในครั้งถัดไป"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "I want the refund to be sent to my original payment method"
msgstr ""
msgstr "ฉันต้องการรับเงินคืนผ่านช่องทางชำระเงินเดิม"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "The following payment methods will be used to refund the money to you:"
msgstr ""
msgstr "ช่องทางการชำระเงินต่อไปนี้จะถูกใช้เพื่อคืนเงินให้กับคุณ:"
#: pretix/presale/templates/pretixpresale/event/order_cancel.html
msgid "Yes, request cancellation"
msgstr ""
msgstr "ใช่, ยื่นขอยกเลิก"
#: pretix/presale/templates/pretixpresale/event/order_change_confirm.html
msgid "Please confirm the following changes to your order."
msgstr ""
msgstr "โปรดยืนยันการเปลี่ยนแปลงคำสั่งซื้อของคุณดังต่อไปนี้"
#: pretix/presale/templates/pretixpresale/event/order_giftcard.html
#: pretix/presale/templates/pretixpresale/event/position_giftcard.html
#, python-format
msgid "Gift card: %(code)s"
msgstr ""
msgstr "บัตรของขวัญ: %(code)s"
#: pretix/presale/templates/pretixpresale/event/order_modify.html
msgid "Modify order"
msgstr ""
msgstr "แก้ไขคำสั่งซื้อ"
#: pretix/presale/templates/pretixpresale/event/order_modify.html
#, python-format
msgid "Modify order: %(code)s"
msgstr ""
msgstr "แก้ไขคำสั่งซื้อ: %(code)s"
#: pretix/presale/templates/pretixpresale/event/order_modify.html
msgid ""
"Modifying your invoice address will not automatically generate a new "
"invoice. Please contact us if you need a new invoice."
msgstr ""
"การแก้ไขที่อยู่สำหรับออกใบแจ้งหนี้จะไม่สร้างใบแจ้งหนี้ใหม่โดยอัตโนมัติ โปรดติดต่อเราหากคุณต้องการใบ"
"แจ้งหนี้ใหม่"
#: pretix/presale/templates/pretixpresale/event/order_modify.html
#: pretix/presale/templates/pretixpresale/event/position_modify.html
msgid "Save changes"
msgstr ""
msgstr "บันทึกการเปลี่ยนแปลง"
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html
msgid "Change payment method"
msgstr ""
msgstr "เปลี่ยนแปลงวิธีการชำระเงิน"
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html
#, python-format
msgid "Choose payment method: %(code)s"
msgstr ""
msgstr "เลือกวิธีการชำระเงิน: %(code)s"
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html
msgid ""
"Please note: If you change your payment method, your order total will change "
"by the amount displayed to the right of each method."
msgstr ""
"ข้อควรจำ: หากคุณเปลี่ยนแปลงวิธีการชำระเงิน ยอดรวมคำสั่งซื้อของคุณจะเปลี่ยนแปลงตามจำนวนที่แสดง"
"ด้านขวาของแต่ละวิธี"
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html
msgid "There are no alternative payment providers available for this order."
msgstr ""
msgstr "ไม่มีผู้ให้บริการชำระเงินอื่นที่สามารถใช้งานได้สำหรับคำสั่งซื้อนี้"
#: pretix/presale/templates/pretixpresale/event/order_pay_confirm.html
msgid "Please confirm the following payment details."
msgstr ""
msgstr "โปรดยืนยันรายละเอียดการชำระเงินดังต่อไปนี้"
#: pretix/presale/templates/pretixpresale/event/order_pay_confirm.html
#, python-format
msgid "Total: %(total)s"
msgstr ""
msgstr "ยอดรวม: %(total)s"
#: pretix/presale/templates/pretixpresale/event/payment_qr_codes.html
msgid ""
@@ -0,0 +1,29 @@
from django.db import migrations
from django.db.models import Exists, OuterRef
def activate_plugin(apps, schema_editor):
Event = apps.get_model("pretixbase", "Event")
ScheduledMail = apps.get_model("sendmail", "ScheduledMail")
events = (
Event.objects
.exclude(plugins__icontains="pretix.plugins.sendmail")
.filter(
Exists(
ScheduledMail.objects.filter(event=OuterRef('pk')).exclude(state=ScheduledMail.STATE_COMPLETED)
)
)
)
for event in events:
event.enable_plugin('pretix.plugins.sendmail')
event.save(update_fields=['plugins'])
class Migration(migrations.Migration):
dependencies = [
("sendmail", "0012_remove_cross_event_scheduled_mails"),
]
operations = [
migrations.RunPython(activate_plugin),
]
+2
View File
@@ -150,6 +150,8 @@ class SendmailPluginRuleLogEntryType(EventLogEntryType):
@receiver(periodic_task)
def sendmail_run_rules(sender, **kwargs):
if "pretix.plugins.sendmail" not in sender.get_plugins():
return # do not send scheduled mails if this plugin is disabled
with scopes_disabled():
mails = ScheduledMail.objects.all()
@@ -22,12 +22,14 @@
{% trans "Change account information" %}
</a>
</li>
<li>
<a href="{% eventurl request.organizer "presale:organizer.customer.password" %}">
{% icon "key" %}
{% trans "Change password" %}
</a>
</li>
{% if not customer.provider %}
<li>
<a href="{% eventurl request.organizer "presale:organizer.customer.password" %}">
{% icon "key" %}
{% trans "Change password" %}
</a>
</li>
{% endif %}
</ul>
</dd>
<dd>
+5 -2
View File
@@ -342,8 +342,11 @@ def test_invoice_shredder(event, order):
s.shred_data()
inv.refresh_from_db()
assert "Acme" not in inv.invoice_to
assert "icket" not in inv.lines.first().description
assert inv.invoice_to == ""
assert inv.invoice_to_company == ""
l = inv.lines.first()
assert l.description == ""
assert l.attendee_name == ""
assert not inv.file
assert not os.path.exists(fname)
+41
View File
@@ -2438,6 +2438,47 @@ def test_refund_paid_order_offsetting_to_wrong_currency(client, env):
assert b'different currency' in r.content
@pytest.mark.django_db
def test_refund_paid_order_offsetting_to_wrong_permissions(client, env):
with scopes_disabled():
p = env[2].payments.last()
p.confirm()
t = env[0].organizer.teams.get()
t.all_events = False
t.save()
t.limit_events.set([env[0]])
client.login(email='dummy@dummy.dummy', password='dummy')
event2 = Event.objects.create(
organizer=env[0].organizer, name='Dummy', slug='dummy2',
date_from=now(), plugins='pretix.plugins.banktransfer,pretix.plugins.stripe,tests.testdummy',
currency='EUR',
)
ticket2 = Item.objects.create(event=event2, name='Early-bird ticket',
category=None, default_price=23,
admission=True, personalized=True)
o = Order.objects.create(
code='BAZ', event=event2, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
sales_channel=event2.organizer.sales_channels.get(identifier="web"),
total=5, locale='en'
)
o.positions.create(price=5, item=ticket2)
r = client.post('/control/event/dummy/dummy/orders/ABC32/refund', {
'start-partial_amount': '5.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-offsetting': '5.00',
'order-offsetting': 'BAZ',
'manual_state': 'pending',
'last_known_refund_id': 0,
'perform': 'on'
}, follow=True)
assert b'alert-danger' in r.content
assert b'do not have access' in r.content
@pytest.mark.django_db
def test_refund_paid_order_offsetting(client, env):
with scopes_disabled():
+52
View File
@@ -478,6 +478,58 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
assert ia.vat_id == "AT123456"
assert not ia.vat_id_validated
def test_vat_id_revalidated_after_country_change(self):
self.tr19.eu_reverse_charge = True
self.tr19.keep_gross_if_rate_changes = True
self.tr19.home_country = Country('DE')
self.tr19.save()
self.event.settings.invoice_address_vatid = True
with scopes_disabled():
cr1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=23, expires=now() + timedelta(minutes=10)
)
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
mock_validate.return_value = 'AT123456'
self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
'is_business': 'business',
'company': 'Foo',
'name': 'Bar',
'street': 'Baz',
'zipcode': '1234',
'city': 'Here',
'country': 'AT',
'vat_id': 'AT123456',
'email': 'admin@localhost',
'transmission_type': 'email',
}, follow=True)
cr1.refresh_from_db()
assert cr1.price == Decimal('23.00')
assert cr1.tax_rate == Decimal('0.00')
assert cr1.tax_value == Decimal('0.00')
with mock.patch('pretix.base.services.tax._validate_vat_id_EU') as mock_validate:
def raiser(*args, **kwargs):
raise VATIDFinalError('country mismatch')
mock_validate.side_effect = raiser
r = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
'is_business': 'business',
'company': 'Foo',
'name': 'Bar',
'street': 'Baz',
'zipcode': '1012 WX',
'city': 'Here',
'country': 'NL',
'vat_id': 'AT123456',
'email': 'admin@localhost',
'transmission_type': 'email',
}, follow=True)
assert b'alert-danger' in r.content
def test_reverse_charge_keep_gross(self):
self.tr19.eu_reverse_charge = True
self.tr19.keep_gross_if_rate_changes = True